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

add onStyleImageMissing to allow dynamically loaded or generated images #14253

Merged
merged 9 commits into from
Apr 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/mbgl/map/map_observer.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <mbgl/style/source.hpp>
#include <mbgl/style/image.hpp>
ansis marked this conversation as resolved.
Show resolved Hide resolved

#include <cstdint>
#include <string>
Expand Down Expand Up @@ -46,6 +47,7 @@ class MapObserver {
virtual void onDidFinishLoadingStyle() {}
virtual void onSourceChanged(style::Source&) {}
virtual void onDidBecomeIdle() {}
virtual void onStyleImageMissing(const std::string&) {}
};

} // namespace mbgl
4 changes: 4 additions & 0 deletions include/mbgl/renderer/renderer_observer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class RendererObserver {

// Final frame
virtual void onDidFinishRenderingMap() {}

// Style is missing an image
using StyleImageMissingCallback = std::function<void()>;
virtual void onStyleImageMissing(const std::string&, StyleImageMissingCallback done) { done(); }
};

} // namespace mbgl
7 changes: 6 additions & 1 deletion platform/darwin/src/MGLRendererFrontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ class MGLRenderFrontend : public mbgl::RendererFrontend

mbgl::BackendScope guard { mbglBackend, mbgl::BackendScope::ScopeType::Implicit };

renderer->render(*updateParameters);
// onStyleImageMissing might be called during a render. The user implemented method
// could trigger a call to MGLRenderFrontend#update which overwrites `updateParameters`.
// Copy the shared pointer here so that the parameters aren't destroyed while `render(...)` is
// still using them.
auto updateParameters_ = updateParameters;
renderer->render(*updateParameters_);
}

mbgl::Renderer* getRenderer() {
Expand Down
8 changes: 7 additions & 1 deletion platform/default/src/mbgl/gl/headless_frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, Scheduler& sch
asyncInvalidate([this] {
if (renderer && updateParameters) {
mbgl::BackendScope guard { backend };
renderer->render(*updateParameters);

// onStyleImageMissing might be called during a render. The user implemented method
// could trigger a call to MGLRenderFrontend#update which overwrites `updateParameters`.
// Copy the shared pointer here so that the parameters aren't destroyed while `render(...)` is
// still using them.
auto updateParameters_ = updateParameters;
renderer->render(*updateParameters_);
}
}),
renderer(std::make_unique<Renderer>(backend, pixelRatio, scheduler, mode, programCacheDir, localFontFamily)) {
Expand Down
7 changes: 6 additions & 1 deletion platform/glfw/glfw_renderer_frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ void GLFWRendererFrontend::render() {

mbgl::BackendScope guard { glfwView, mbgl::BackendScope::ScopeType::Implicit };

renderer->render(*updateParameters);
// onStyleImageMissing might be called during a render. The user implemented method
// could trigger a call to MGLRenderFrontend#update which overwrites `updateParameters`.
// Copy the shared pointer here so that the parameters aren't destroyed while `render(...)` is
// still using them.
auto updateParameters_ = updateParameters;
renderer->render(*updateParameters_);
}

mbgl::Renderer* GLFWRendererFrontend::getRenderer() {
Expand Down
1 change: 1 addition & 0 deletions src/core-files.json
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@
"mbgl/renderer/group_by_layout.hpp": "src/mbgl/renderer/group_by_layout.hpp",
"mbgl/renderer/image_atlas.hpp": "src/mbgl/renderer/image_atlas.hpp",
"mbgl/renderer/image_manager.hpp": "src/mbgl/renderer/image_manager.hpp",
"mbgl/renderer/image_manager_observer.hpp": "src/mbgl/renderer/image_manager_observer.hpp",
"mbgl/renderer/layers/render_background_layer.hpp": "src/mbgl/renderer/layers/render_background_layer.hpp",
"mbgl/renderer/layers/render_circle_layer.hpp": "src/mbgl/renderer/layers/render_circle_layer.hpp",
"mbgl/renderer/layers/render_custom_layer.hpp": "src/mbgl/renderer/layers/render_custom_layer.hpp",
Expand Down
14 changes: 14 additions & 0 deletions src/mbgl/gfx/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,25 @@ class Context {
texture.size = image.size;
}

template <typename Image>
void updateTextureSub(Texture& texture,
const Image& image,
const uint16_t offsetX,
const uint16_t offsetY,
TextureChannelDataType type = TextureChannelDataType::UnsignedByte) {
assert(image.size.width + offsetX <= texture.size.width);
assert(image.size.height + offsetY <= texture.size.height);
auto format = image.channels == 4 ? TexturePixelType::RGBA : TexturePixelType::Alpha;
updateTextureResourceSub(*texture.resource, offsetX, offsetY, image.size, image.data.get(), format, type);
}

protected:
virtual std::unique_ptr<TextureResource> createTextureResource(
Size, const void* data, TexturePixelType, TextureChannelDataType) = 0;
virtual void updateTextureResource(const TextureResource&, Size, const void* data,
TexturePixelType, TextureChannelDataType) = 0;
virtual void updateTextureResourceSub(const TextureResource&, uint16_t xOffset, uint16_t yOffset, Size, const void* data,
TexturePixelType, TextureChannelDataType) = 0;

public:
DrawScope createDrawScope() {
Expand Down
18 changes: 18 additions & 0 deletions src/mbgl/gl/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,24 @@ void Context::updateTextureResource(const gfx::TextureResource& resource,
Enum<gfx::TextureChannelDataType>::to(type), data));
}

void Context::updateTextureResourceSub(const gfx::TextureResource& resource,
const uint16_t xOffset,
const uint16_t yOffset,
const Size size,
const void* data,
gfx::TexturePixelType format,
gfx::TextureChannelDataType type) {
// Always use texture unit 0 for manipulating it.
activeTextureUnit = 0;
texture[0] = static_cast<const gl::TextureResource&>(resource).texture;
MBGL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0,
xOffset, yOffset,
size.width, size.height,
Enum<gfx::TexturePixelType>::to(format),
Enum<gfx::TextureChannelDataType>::to(type), data));
}


std::unique_ptr<gfx::DrawScopeResource> Context::createDrawScopeResource() {
return std::make_unique<gl::DrawScopeResource>(createVertexArray());
}
Expand Down
1 change: 1 addition & 0 deletions src/mbgl/gl/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ class Context final : public gfx::Context {

std::unique_ptr<gfx::TextureResource> createTextureResource(Size, const void* data, gfx::TexturePixelType, gfx::TextureChannelDataType) override;
void updateTextureResource(const gfx::TextureResource&, Size, const void* data, gfx::TexturePixelType, gfx::TextureChannelDataType) override;
void updateTextureResourceSub(const gfx::TextureResource&, const uint16_t xOffset, const uint16_t yOffset, Size, const void* data, gfx::TexturePixelType, gfx::TextureChannelDataType) override;

std::unique_ptr<gfx::DrawScopeResource> createDrawScopeResource() override;

Expand Down
10 changes: 10 additions & 0 deletions src/mbgl/map/map_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,14 @@ void Map::Impl::jumpTo(const CameraOptions& camera) {
onUpdate();
}

void Map::Impl::onStyleImageMissing(const std::string& id, std::function<void()> done) {

if (style->getImage(id) == nullptr) {
observer.onStyleImageMissing(id);
}

done();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is there a done function? What does it do that we can't do just through the mere fact of returning from this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I might be wrong here, but: on Android the map and renderer are on different threads. The missing image event originates on the render thread but needs to be called on the ui thread. The observer for RendererFrontend needs to be implemented in a way to calls onto the main thread and then calls the callback back on the render thread. I haven't implemented the changes to the Android platform yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wait, I think you're right. This isn't necessary here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, I think the RendererFrontend's observer needs to have this callback and since Map::Impl implements the same interface it also needs it

onUpdate();
}

} // namespace mbgl
1 change: 1 addition & 0 deletions src/mbgl/map/map_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class Map::Impl : public style::Observer, public RendererObserver {
void onDidFinishRenderingFrame(RenderMode, bool) final;
void onWillStartRenderingMap() final;
void onDidFinishRenderingMap() final;
void onStyleImageMissing(const std::string&, std::function<void()>) final;

// Map
void jumpTo(const CameraOptions&);
Expand Down
40 changes: 35 additions & 5 deletions src/mbgl/renderer/image_atlas.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/gfx/context.hpp>
#include <mbgl/renderer/image_manager.hpp>

#include <mapbox/shelf-pack.hpp>

namespace mbgl {

static constexpr uint32_t padding = 1;

ImagePosition::ImagePosition(const mapbox::Bin& bin, const style::Image::Impl& image)
ImagePosition::ImagePosition(const mapbox::Bin& bin, const style::Image::Impl& image, uint32_t version_)
: pixelRatio(image.pixelRatio),
textureRect(
bin.x + padding,
bin.y + padding,
bin.w - padding * 2,
bin.h - padding * 2
) {
),
version(version_) {
}

const mapbox::Bin& _packImage(mapbox::ShelfPack& pack, const style::Image::Impl& image, ImageAtlas& resultImage, ImageType imageType) {
Expand Down Expand Up @@ -49,7 +52,30 @@ const mapbox::Bin& _packImage(mapbox::ShelfPack& pack, const style::Image::Impl&
return bin;
}

ImageAtlas makeImageAtlas(const ImageMap& icons, const ImageMap& patterns) {
void ImageAtlas::patchUpdatedImages(gfx::Context& context, gfx::Texture& atlasTexture, const ImageManager& imageManager) {
for (auto& updatedImageVersion : imageManager.updatedImageVersions) {
auto iconPosition = iconPositions.find(updatedImageVersion.first);
if (iconPosition != iconPositions.end()) {
patchUpdatedImage(context, atlasTexture, iconPosition->second, imageManager, updatedImageVersion.first, updatedImageVersion.second);
}
auto patternPosition = patternPositions.find(updatedImageVersion.first);
if (patternPosition != patternPositions.end()) {
patchUpdatedImage(context, atlasTexture, patternPosition->second, imageManager, updatedImageVersion.first, updatedImageVersion.second);
}
}
}

void ImageAtlas::patchUpdatedImage(gfx::Context& context, gfx::Texture& atlasTexture, ImagePosition& position, const ImageManager& imageManager, const std::string& name, uint16_t version) {
if (position.version == version) return;

auto updatedImage = imageManager.getImage(name);
if (updatedImage == nullptr) return;

context.updateTextureSub(atlasTexture, updatedImage->image, position.textureRect.x, position.textureRect.y);
position.version = version;
}

ImageAtlas makeImageAtlas(const ImageMap& icons, const ImageMap& patterns, const std::unordered_map<std::string, uint32_t>& versionMap) {
ImageAtlas result;

mapbox::ShelfPack::ShelfPackOptions options;
Expand All @@ -59,13 +85,17 @@ ImageAtlas makeImageAtlas(const ImageMap& icons, const ImageMap& patterns) {
for (const auto& entry : icons) {
const style::Image::Impl& image = *entry.second;
const mapbox::Bin& bin = _packImage(pack, image, result, ImageType::Icon);
result.iconPositions.emplace(image.id, ImagePosition { bin, image });
auto it = versionMap.find(entry.first);
auto version = it != versionMap.end() ? it->second : 0;
result.iconPositions.emplace(image.id, ImagePosition { bin, image, version });
}

for (const auto& entry : patterns) {
const style::Image::Impl& image = *entry.second;
const mapbox::Bin& bin = _packImage(pack, image, result, ImageType::Pattern);
result.patternPositions.emplace(image.id, ImagePosition { bin, image });
auto it = versionMap.find(entry.first);
auto version = it != versionMap.end() ? it->second : 0;
result.patternPositions.emplace(image.id, ImagePosition { bin, image, version });
}

pack.shrink();
Expand Down
16 changes: 14 additions & 2 deletions src/mbgl/renderer/image_atlas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@

namespace mbgl {

namespace gfx {
class Texture;
class Context;
} // namespace gfx

class ImageManager;

class ImagePosition {
public:
ImagePosition(const mapbox::Bin&, const style::Image::Impl&);
ImagePosition(const mapbox::Bin&, const style::Image::Impl&, uint32_t version = 0);

float pixelRatio;
Rect<uint16_t> textureRect;
uint32_t version;

std::array<uint16_t, 2> tl() const {
return {{
Expand Down Expand Up @@ -51,8 +59,12 @@ class ImageAtlas {
PremultipliedImage image;
ImagePositions iconPositions;
ImagePositions patternPositions;

void patchUpdatedImages(gfx::Context&, gfx::Texture&, const ImageManager&);
private:
void patchUpdatedImage(gfx::Context&, gfx::Texture&, ImagePosition& position, const ImageManager& imageManager, const std::string& name, uint16_t version);
};

ImageAtlas makeImageAtlas(const ImageMap&, const ImageMap&);
ImageAtlas makeImageAtlas(const ImageMap&, const ImageMap&, const std::unordered_map<std::string, uint32_t>& versionMap);
Copy link
Contributor

Choose a reason for hiding this comment

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

As we're accumulating more objects that work together to form an ImageAtlas, maybe it's time to bundle them up together in a follow up PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds like a good idea


} // namespace mbgl
Loading