From a9abc43510b4206ffa9c54c3d4c9aa2ed0d1cfce Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Wed, 29 May 2019 16:06:56 +0300 Subject: [core] Introduce PatternAtlas --- src/mbgl/renderer/image_manager.cpp | 110 ++------------------- src/mbgl/renderer/image_manager.hpp | 53 ++-------- .../renderer/layers/render_background_layer.cpp | 23 +++-- src/mbgl/renderer/paint_parameters.cpp | 6 +- src/mbgl/renderer/paint_parameters.hpp | 7 +- src/mbgl/renderer/pattern_atlas.cpp | 109 ++++++++++++++++++++ src/mbgl/renderer/pattern_atlas.hpp | 53 ++++++++++ src/mbgl/renderer/render_layer.hpp | 2 + src/mbgl/renderer/renderer_impl.cpp | 14 ++- src/mbgl/renderer/renderer_impl.hpp | 2 + src/mbgl/tile/geometry_tile.hpp | 10 +- 11 files changed, 221 insertions(+), 168 deletions(-) create mode 100644 src/mbgl/renderer/pattern_atlas.cpp create mode 100644 src/mbgl/renderer/pattern_atlas.hpp (limited to 'src/mbgl') diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp index d5fe864fa7..ff4c20eefb 100644 --- a/src/mbgl/renderer/image_manager.cpp +++ b/src/mbgl/renderer/image_manager.cpp @@ -1,16 +1,19 @@ #include -#include + #include #include -#include -#include -#include #include +#include +#include namespace mbgl { static ImageManagerObserver nullObserver; +ImageManager::ImageManager() = default; + +ImageManager::~ImageManager() = default; + void ImageManager::setObserver(ImageManagerObserver* observer_) { observer = observer_ ? observer_ : &nullObserver; } @@ -62,7 +65,6 @@ bool ImageManager::updateImage(Immutable image_) { updatedImageVersions[image_->id]++; } - removePattern(image_->id); oldImage->second = std::move(image_); return sizeChanged; @@ -79,22 +81,6 @@ void ImageManager::removeImage(const std::string& id) { requestedImages.erase(requestedIt); } images.erase(it); - removePattern(id); -} - -void ImageManager::removePattern(const std::string& id) { - auto it = patterns.find(id); - if (it != patterns.end()) { - // Clear pattern from the atlas image. - const uint32_t x = it->second.bin->x; - const uint32_t y = it->second.bin->y; - const uint32_t w = it->second.bin->w; - const uint32_t h = it->second.bin->h; - PremultipliedImage::clear(atlasImage, { x, y }, { w, h }); - - shelfPack.unref(*it->second.bin); - patterns.erase(it); - } } const style::Image::Impl* ImageManager::getImage(const std::string& id) const { @@ -243,88 +229,6 @@ void ImageManager::dumpDebugLogs() const { Log::Info(Event::General, "ImageManager::loaded: %d", loaded); } -// When copied into the atlas texture, image data is padded by one pixel on each side. Icon -// images are padded with fully transparent pixels, while pattern images are padded with a -// copy of the image data wrapped from the opposite side. In both cases, this ensures the -// correct behavior of GL_LINEAR texture sampling mode. -static constexpr uint16_t padding = 1; - -static mapbox::ShelfPack::ShelfPackOptions shelfPackOptions() { - mapbox::ShelfPack::ShelfPackOptions options; - options.autoResize = true; - return options; -} - -ImageManager::ImageManager() - : shelfPack(64, 64, shelfPackOptions()) { -} - -ImageManager::~ImageManager() = default; - -optional ImageManager::getPattern(const std::string& id) { - auto it = patterns.find(id); - if (it != patterns.end()) { - return it->second.position; - } - - const style::Image::Impl* image = getImage(id); - if (!image) { - return {}; - } - - const uint16_t width = image->image.size.width + padding * 2; - const uint16_t height = image->image.size.height + padding * 2; - - mapbox::Bin* bin = shelfPack.packOne(-1, width, height); - if (!bin) { - return {}; - } - - atlasImage.resize(getPixelSize()); - - const PremultipliedImage& src = image->image; - - const uint32_t x = bin->x + padding; - const uint32_t y = bin->y + padding; - const uint32_t w = src.size.width; - const uint32_t h = src.size.height; - - PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y }, { w, h }); - - // Add 1 pixel wrapped padding on each side of the image. - PremultipliedImage::copy(src, atlasImage, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T - PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y + h }, { w, 1 }); // B - PremultipliedImage::copy(src, atlasImage, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L - PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x + w, y }, { 1, h }); // R - - dirty = true; - - return patterns.emplace(id, Pattern { bin, { *bin, *image } }).first->second.position; -} - -Size ImageManager::getPixelSize() const { - return Size { - static_cast(shelfPack.width()), - static_cast(shelfPack.height()) - }; -} - -void ImageManager::upload(gfx::UploadPass& uploadPass) { - if (!atlasTexture) { - atlasTexture = uploadPass.createTexture(atlasImage); - } else if (dirty) { - uploadPass.updateTexture(*atlasTexture, atlasImage); - } - - dirty = false; -} - -gfx::TextureBinding ImageManager::textureBinding() { - assert(atlasTexture); - assert(!dirty); - return { atlasTexture->getResource(), gfx::TextureFilterType::Linear }; -} - ImageRequestor::ImageRequestor(ImageManager& imageManager_) : imageManager(imageManager_) { } diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp index f20d55415b..5305358513 100644 --- a/src/mbgl/renderer/image_manager.hpp +++ b/src/mbgl/renderer/image_manager.hpp @@ -1,16 +1,9 @@ #pragma once #include -#include -#include #include -#include -#include -#include -#include - -#include +#include #include namespace mbgl { @@ -22,21 +15,17 @@ namespace gfx { class UploadPass; } // namespace gfx +class ImageManagerObserver; class ImageRequestor; -/* - ImageManager does two things: - - 1. Tracks requests for icon images from tile workers and sends responses when the requests are fulfilled. - 2. Builds a texture atlas for pattern images. - - 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. -*/ -class ImageManager : public util::noncopyable { +/** + * @brief tracks requests for icon images from tile workers and sends responses when the requests are fulfilled. + */ +class ImageManager { public: ImageManager(); + ImageManager(const ImageManager&) = delete; + ImageManager& operator=(const ImageManager&) = delete; ~ImageManager(); void setObserver(ImageManagerObserver*); @@ -80,32 +69,6 @@ private: ImageMap images; ImageManagerObserver* observer = nullptr; - -// Pattern stuff -public: - optional getPattern(const std::string& name); - - gfx::TextureBinding textureBinding(); - void upload(gfx::UploadPass&); - - Size getPixelSize() const; - - // Only for use in tests. - const PremultipliedImage& getAtlasImage() const { - return atlasImage; - } - -private: - struct Pattern { - mapbox::Bin* bin; - ImagePosition position; - }; - - mapbox::ShelfPack shelfPack; - std::unordered_map patterns; - PremultipliedImage atlasImage; - mbgl::optional atlasTexture; - bool dirty = true; }; class ImageRequestor { diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp index 9d78315afa..be18e2ccb2 100644 --- a/src/mbgl/renderer/layers/render_background_layer.cpp +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -90,8 +91,8 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) { const auto& evaluated = static_cast(*evaluatedProperties).evaluated; const auto& crossfade = static_cast(*evaluatedProperties).crossfade; if (!evaluated.get().to.empty()) { - optional imagePosA = parameters.imageManager.getPattern(evaluated.get().from); - optional imagePosB = parameters.imageManager.getPattern(evaluated.get().to); + optional imagePosA = parameters.patternAtlas.getPattern(evaluated.get().from); + optional imagePosB = parameters.patternAtlas.getPattern(evaluated.get().to); if (!imagePosA || !imagePosB) return; @@ -102,7 +103,7 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) { BackgroundPatternProgram::layoutUniformValues( parameters.matrixForTile(tileID), evaluated.get(), - parameters.imageManager.getPixelSize(), + parameters.patternAtlas.getPixelSize(), *imagePosA, *imagePosB, crossfade, @@ -110,7 +111,7 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) { parameters.state ), BackgroundPatternProgram::TextureBindings{ - textures::image::Value{ parameters.imageManager.textureBinding() }, + textures::image::Value{ parameters.patternAtlas.textureBinding() }, }, tileID ); @@ -140,12 +141,22 @@ optional RenderBackgroundLayer::getSolidBackground() const { return { evaluated.get() * evaluated.get() }; } +namespace { +void addPatternIfNeeded(const std::string& id, const LayerPrepareParameters& params) { + if (!params.patternAtlas.getPattern(id)) { + if (auto* image = params.imageManager.getImage(id)) { + params.patternAtlas.addPattern(*image); + } + } +} +} // namespace + void RenderBackgroundLayer::prepare(const LayerPrepareParameters& params) { const auto& evaluated = static_cast(*evaluatedProperties).evaluated; if (!evaluated.get().to.empty()) { // Ensures that the texture gets added and uploaded to the atlas. - params.imageManager.getPattern(evaluated.get().from); - params.imageManager.getPattern(evaluated.get().to); + addPatternIfNeeded(evaluated.get().from, params); + addPatternIfNeeded(evaluated.get().to, params); } } diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp index 2e7cae4d3a..f4498f66b1 100644 --- a/src/mbgl/renderer/paint_parameters.cpp +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -32,8 +32,8 @@ PaintParameters::PaintParameters(gfx::Context& context_, const EvaluatedLight& evaluatedLight_, const TransformParameters& transformParams_, RenderStaticData& staticData_, - ImageManager& imageManager_, - LineAtlas& lineAtlas_) + LineAtlas& lineAtlas_, + PatternAtlas& patternAtlas_) : context(context_), backend(backend_), encoder(context.createCommandEncoder()), @@ -41,8 +41,8 @@ PaintParameters::PaintParameters(gfx::Context& context_, evaluatedLight(evaluatedLight_), transformParams(transformParams_), staticData(staticData_), - imageManager(imageManager_), lineAtlas(lineAtlas_), + patternAtlas(patternAtlas_), mapMode(updateParameters.mode), debugOptions(updateParameters.debugOptions), timePoint(updateParameters.timePoint), diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index e57e49a615..89084e9e52 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -20,6 +20,7 @@ class Programs; class TransformState; class ImageManager; class LineAtlas; +class PatternAtlas; class UnwrappedTileID; class RenderSource; class RenderTile; @@ -50,8 +51,8 @@ public: const EvaluatedLight&, const TransformParameters&, RenderStaticData&, - ImageManager&, - LineAtlas&); + LineAtlas&, + PatternAtlas&); ~PaintParameters(); gfx::Context& context; @@ -64,8 +65,8 @@ public: const TransformParameters& transformParams; RenderStaticData& staticData; - ImageManager& imageManager; LineAtlas& lineAtlas; + PatternAtlas& patternAtlas; RenderPass pass = RenderPass::Opaque; MapMode mapMode; diff --git a/src/mbgl/renderer/pattern_atlas.cpp b/src/mbgl/renderer/pattern_atlas.cpp new file mode 100644 index 0000000000..320412685b --- /dev/null +++ b/src/mbgl/renderer/pattern_atlas.cpp @@ -0,0 +1,109 @@ +#include +#include +#include + +namespace mbgl { + +namespace { + +// When copied into the atlas texture, image data is padded by one pixel on each side. Icon +// images are padded with fully transparent pixels, while pattern images are padded with a +// copy of the image data wrapped from the opposite side. In both cases, this ensures the +// correct behavior of GL_LINEAR texture sampling mode. +const uint16_t padding = 1; + +mapbox::ShelfPack::ShelfPackOptions shelfPackOptions() { + mapbox::ShelfPack::ShelfPackOptions options; + options.autoResize = true; + return options; +} + +} // namespace + +PatternAtlas::PatternAtlas() + : shelfPack(64, 64, shelfPackOptions()) { +} + +PatternAtlas::~PatternAtlas() = default; + +optional PatternAtlas::getPattern(const std::string& id) const { + auto it = patterns.find(id); + if (it != patterns.end()) { + return it->second.position; + } + return nullopt; +} + +optional PatternAtlas::addPattern(const style::Image::Impl& image) { + if (patterns.find(image.id) != patterns.end()) { + return nullopt; + } + const uint16_t width = image.image.size.width + padding * 2; + const uint16_t height = image.image.size.height + padding * 2; + + mapbox::Bin* bin = shelfPack.packOne(-1, width, height); + if (!bin) { + return nullopt; + } + + atlasImage.resize(getPixelSize()); + + const PremultipliedImage& src = image.image; + + const uint32_t x = bin->x + padding; + const uint32_t y = bin->y + padding; + const uint32_t w = src.size.width; + const uint32_t h = src.size.height; + + PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y }, { w, h }); + + // Add 1 pixel wrapped padding on each side of the image. + PremultipliedImage::copy(src, atlasImage, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T + PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y + h }, { w, 1 }); // B + PremultipliedImage::copy(src, atlasImage, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L + PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x + w, y }, { 1, h }); // R + + dirty = true; + + return patterns.emplace(image.id, Pattern { bin, { *bin, image } }).first->second.position; +} + +void PatternAtlas::removePattern(const std::string& id) { + auto it = patterns.find(id); + if (it != patterns.end()) { + // Clear pattern from the atlas image. + const uint32_t x = it->second.bin->x; + const uint32_t y = it->second.bin->y; + const uint32_t w = it->second.bin->w; + const uint32_t h = it->second.bin->h; + PremultipliedImage::clear(atlasImage, { x, y }, { w, h }); + + shelfPack.unref(*it->second.bin); + patterns.erase(it); + } +} + +Size PatternAtlas::getPixelSize() const { + return { + static_cast(shelfPack.width()), + static_cast(shelfPack.height()) + }; +} + +void PatternAtlas::upload(gfx::UploadPass& uploadPass) { + if (!atlasTexture) { + atlasTexture = uploadPass.createTexture(atlasImage); + } else if (dirty) { + uploadPass.updateTexture(*atlasTexture, atlasImage); + } + + dirty = false; +} + +gfx::TextureBinding PatternAtlas::textureBinding() const { + assert(atlasTexture); + assert(!dirty); + return { atlasTexture->getResource(), gfx::TextureFilterType::Linear }; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/pattern_atlas.hpp b/src/mbgl/renderer/pattern_atlas.hpp new file mode 100644 index 0000000000..f39ba25233 --- /dev/null +++ b/src/mbgl/renderer/pattern_atlas.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace mbgl { + +template +class Actor; + +namespace gfx { +class UploadPass; +} // namespace gfx + +class PatternAtlas { +public: + PatternAtlas(); + PatternAtlas(const PatternAtlas&) = delete; + PatternAtlas& operator=(const PatternAtlas&) = delete; + ~PatternAtlas(); + + optional getPattern(const std::string&) const; + optional addPattern(const style::Image::Impl&); + void removePattern(const std::string&); + + gfx::TextureBinding textureBinding() const; + + void upload(gfx::UploadPass&); + Size getPixelSize() const; + + const PremultipliedImage& getAtlasImageForTests() const { + return atlasImage; + } + +private: + struct Pattern { + mapbox::Bin* bin; + ImagePosition position; + }; + mapbox::ShelfPack shelfPack; + std::unordered_map patterns; + PremultipliedImage atlasImage; + mbgl::optional atlasTexture; + bool dirty = true; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index 887acdfb59..94d7384ba4 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -18,6 +18,7 @@ class PaintParameters; class RenderSource; class RenderTile; class TransformState; +class PatternAtlas; class LayerRenderData { public: @@ -35,6 +36,7 @@ class LayerPrepareParameters { public: RenderSource* source; ImageManager& imageManager; + PatternAtlas& patternAtlas; const TransformState& state; }; diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 993a912535..96ba8d7554 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ Renderer::Impl::Impl(gfx::RendererBackend& backend_, , glyphManager(std::make_unique(std::make_unique(localFontFamily))) , imageManager(std::make_unique()) , lineAtlas(std::make_unique(Size{ 256, 512 })) + , patternAtlas(std::make_unique()) , imageImpls(makeMutable>>()) , sourceImpls(makeMutable>>()) , layerImpls(makeMutable>>()) @@ -143,6 +145,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { // Remove removed images from sprite atlas. for (const auto& entry : imageDiff.removed) { imageManager->removeImage(entry.first); + patternAtlas->removePattern(entry.first); } // Add added images to sprite atlas. @@ -152,7 +155,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { // Update changed images. for (const auto& entry : imageDiff.changed) { - hasImageDiff = imageManager->updateImage(entry.second.after) || hasImageDiff; + if (imageManager->updateImage(entry.second.after)) { + patternAtlas->removePattern(entry.first); + hasImageDiff = true; + } } imageManager->notifyIfMissingImageAdded(); @@ -302,7 +308,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { for (auto& renderItem : renderItems) { RenderLayer& renderLayer = renderItem.layer; - renderLayer.prepare({renderItem.source, *imageManager, updateParameters.transformState}); + renderLayer.prepare({renderItem.source, *imageManager, *patternAtlas, updateParameters.transformState}); if (renderLayer.needsPlacement()) { layersNeedPlacement.emplace_back(renderLayer); } @@ -362,8 +368,8 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { renderLight.getEvaluated(), transformParams, *staticData, - *imageManager, *lineAtlas, + *patternAtlas }; parameters.symbolFadeChange = placement->symbolFadeChange(updateParameters.timePoint); @@ -393,8 +399,8 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { } staticData->upload(*uploadPass); - imageManager->upload(*uploadPass); lineAtlas->upload(*uploadPass); + patternAtlas->upload(*uploadPass); } // - 3D PASS ------------------------------------------------------------------------------------- diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 7bcbdbbf7c..fae79b6d52 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -29,6 +29,7 @@ class SourceQueryOptions; class GlyphManager; class ImageManager; class LineAtlas; +class PatternAtlas; class CrossTileSymbolIndex; namespace gfx { @@ -116,6 +117,7 @@ private: std::unique_ptr glyphManager; std::unique_ptr imageManager; std::unique_ptr lineAtlas; + std::unique_ptr patternAtlas; std::unique_ptr staticData; Immutable>> imageImpls; diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index 768948a82d..cbc94deed0 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -1,12 +1,14 @@ #pragma once -#include -#include +#include +#include +#include #include #include +#include +#include #include -#include -#include +#include #include #include -- cgit v1.2.1