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

Commit

Permalink
[core] Move Sprite parsing to thread pool
Browse files Browse the repository at this point in the history
  • Loading branch information
kkaefer committed Apr 26, 2017
1 parent 03f26cb commit ed2ccda
Show file tree
Hide file tree
Showing 20 changed files with 144 additions and 74 deletions.
2 changes: 2 additions & 0 deletions cmake/core-files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ set(MBGL_CORE_FILES
src/mbgl/sprite/sprite_atlas.cpp
src/mbgl/sprite/sprite_atlas.hpp
src/mbgl/sprite/sprite_atlas_observer.hpp
src/mbgl/sprite/sprite_atlas_worker.cpp
src/mbgl/sprite/sprite_atlas_worker.hpp
src/mbgl/sprite/sprite_image.cpp
src/mbgl/sprite/sprite_parser.cpp
src/mbgl/sprite/sprite_parser.hpp
Expand Down
16 changes: 3 additions & 13 deletions src/mbgl/annotation/annotation_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,9 @@ const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.poin

AnnotationManager::AnnotationManager(float pixelRatio)
: spriteAtlas({ 1024, 1024 }, pixelRatio) {

struct NullFileSource : public FileSource {
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override {
assert(false);
return nullptr;
}
};

NullFileSource nullFileSource;

// This is a special atlas, holding only images added via addIcon. But we need its isLoaded()
// method to return true.
spriteAtlas.load("", nullFileSource);
// This is a special atlas, holding only images added via addIcon, so we always treat it as
// loaded.
spriteAtlas.markAsLoaded();
}

AnnotationManager::~AnnotationManager() = default;
Expand Down
4 changes: 2 additions & 2 deletions src/mbgl/map/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ void Map::setStyleURL(const std::string& url) {
impl->styleJSON.clear();
impl->styleMutated = false;

impl->style = std::make_unique<Style>(impl->fileSource, impl->pixelRatio);
impl->style = std::make_unique<Style>(impl->scheduler, impl->fileSource, impl->pixelRatio);

impl->styleRequest = impl->fileSource.request(Resource::style(impl->styleURL), [this](Response res) {
// Once we get a fresh style, or the style is mutated, stop revalidating.
Expand Down Expand Up @@ -397,7 +397,7 @@ void Map::setStyleJSON(const std::string& json) {
impl->styleJSON.clear();
impl->styleMutated = false;

impl->style = std::make_unique<Style>(impl->fileSource, impl->pixelRatio);
impl->style = std::make_unique<Style>(impl->scheduler, impl->fileSource, impl->pixelRatio);

impl->loadStyleJSON(json);
}
Expand Down
36 changes: 25 additions & 11 deletions src/mbgl/sprite/sprite_atlas.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/sprite/sprite_atlas_worker.hpp>
#include <mbgl/sprite/sprite_atlas_observer.hpp>
#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/gl/context.hpp>
Expand All @@ -11,6 +12,8 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/actor/actor.hpp>

#include <cassert>
#include <cmath>
Expand All @@ -21,10 +24,17 @@ namespace mbgl {
static SpriteAtlasObserver nullObserver;

struct SpriteAtlas::Loader {
Loader(Scheduler& scheduler, SpriteAtlas& spriteAtlas)
: mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
worker(scheduler, ActorRef<SpriteAtlas>(spriteAtlas, mailbox)) {
}

std::shared_ptr<const std::string> image;
std::shared_ptr<const std::string> json;
std::unique_ptr<AsyncRequest> jsonRequest;
std::unique_ptr<AsyncRequest> spriteRequest;
std::shared_ptr<Mailbox> mailbox;
Actor<SpriteAtlasWorker> worker;
};

SpriteAtlasElement::SpriteAtlasElement(Rect<uint16_t> rect_,
Expand Down Expand Up @@ -54,14 +64,14 @@ SpriteAtlas::SpriteAtlas(Size size_, float pixelRatio_)

SpriteAtlas::~SpriteAtlas() = default;

void SpriteAtlas::load(const std::string& url, FileSource& fileSource) {
void SpriteAtlas::load(const std::string& url, Scheduler& scheduler, FileSource& fileSource) {
if (url.empty()) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
loaded = true;
markAsLoaded();
return;
}

loader = std::make_unique<Loader>();
loader = std::make_unique<Loader>(scheduler, *this);

loader->jsonRequest = fileSource.request(Resource::spriteJSON(url, pixelRatio), [this](Response res) {
if (res.error) {
Expand Down Expand Up @@ -100,14 +110,18 @@ void SpriteAtlas::emitSpriteLoadedIfComplete() {
return;
}

auto result = parseSprite(*loader->image, *loader->json);
if (result.is<Sprites>()) {
loaded = true;
setSprites(result.get<Sprites>());
observer->onSpriteLoaded();
} else {
observer->onSpriteError(result.get<std::exception_ptr>());
}
loader->worker.invoke(&SpriteAtlasWorker::parse, loader->image, loader->json);
// TODO: delete the loader?
}

void SpriteAtlas::onParsed(Sprites&& result) {
markAsLoaded();
setSprites(result);
observer->onSpriteLoaded();
}

void SpriteAtlas::onError(std::exception_ptr err) {
observer->onSpriteError(err);
}

void SpriteAtlas::setObserver(SpriteAtlasObserver* observer_) {
Expand Down
11 changes: 10 additions & 1 deletion src/mbgl/sprite/sprite_atlas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

namespace mbgl {

class Scheduler;
class FileSource;
class SpriteAtlasObserver;

Expand Down Expand Up @@ -43,7 +44,11 @@ class SpriteAtlas : public util::noncopyable {
SpriteAtlas(Size, float pixelRatio);
~SpriteAtlas();

void load(const std::string& url, FileSource&);
void load(const std::string& url, Scheduler&, FileSource&);

void markAsLoaded() {
loaded = true;
}

bool isLoaded() const {
return loaded;
Expand Down Expand Up @@ -81,6 +86,10 @@ class SpriteAtlas : public util::noncopyable {
void _setSprite(const std::string&, const std::shared_ptr<const SpriteImage>& = nullptr);
void emitSpriteLoadedIfComplete();

// Invoked by SpriteAtlasWorker
friend class SpriteAtlasWorker;
void onParsed(Sprites&& result);
void onError(std::exception_ptr);

const Size size;
const float pixelRatio;
Expand Down
29 changes: 29 additions & 0 deletions src/mbgl/sprite/sprite_atlas_worker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <mbgl/sprite/sprite_atlas_worker.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/sprite/sprite_parser.hpp>

namespace mbgl {

SpriteAtlasWorker::SpriteAtlasWorker(ActorRef<SpriteAtlasWorker>, ActorRef<SpriteAtlas> parent_)
: parent(std::move(parent_)) {
}

void SpriteAtlasWorker::parse(std::shared_ptr<const std::string> image,
std::shared_ptr<const std::string> json) {
try {
if (!image) {
// This shouldn't happen, since we always invoke it with a non-empty pointer.
throw std::runtime_error("missing sprite image");
}
if (!json) {
// This shouldn't happen, since we always invoke it with a non-empty pointer.
throw std::runtime_error("missing sprite metadata");
}

parent.invoke(&SpriteAtlas::onParsed, parseSprite(*image, *json));
} catch (...) {
parent.invoke(&SpriteAtlas::onError, std::current_exception());
}
}

} // namespace mbgl
23 changes: 23 additions & 0 deletions src/mbgl/sprite/sprite_atlas_worker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/sprite/sprite_parser.hpp>

#include <memory>
#include <string>

namespace mbgl {

class SpriteAtlas;

class SpriteAtlasWorker {
public:
SpriteAtlasWorker(ActorRef<SpriteAtlasWorker>, ActorRef<SpriteAtlas>);

void parse(std::shared_ptr<const std::string> image, std::shared_ptr<const std::string> json);

private:
ActorRef<SpriteAtlas> parent;
};

} // namespace mbgl
20 changes: 6 additions & 14 deletions src/mbgl/sprite/sprite_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,26 +84,19 @@ bool getBoolean(const JSValue& value, const char* name, const bool def = false)

} // namespace

SpriteParseResult parseSprite(const std::string& image, const std::string& json) {
Sprites sprites;
PremultipliedImage raster;

try {
raster = decodeImage(image);
} catch (...) {
return std::current_exception();
}
Sprites parseSprite(const std::string& image, const std::string& json) {
const PremultipliedImage raster = decodeImage(image);

JSDocument doc;
doc.Parse<0>(json.c_str());

if (doc.HasParseError()) {
std::stringstream message;
message << "Failed to parse JSON: " << rapidjson::GetParseError_En(doc.GetParseError()) << " at offset " << doc.GetErrorOffset();
return std::make_exception_ptr(std::runtime_error(message.str()));
throw std::runtime_error(message.str());
} else if (!doc.IsObject()) {
return std::make_exception_ptr(std::runtime_error("Sprite JSON root must be an object"));
throw std::runtime_error("Sprite JSON root must be an object");
} else {
Sprites sprites;
for (const auto& property : doc.GetObject()) {
const std::string name = { property.name.GetString(), property.name.GetStringLength() };
const JSValue& value = property.value;
Expand All @@ -122,9 +115,8 @@ SpriteParseResult parseSprite(const std::string& image, const std::string& json)
}
}
}
return sprites;
}

return sprites;
}

} // namespace mbgl
7 changes: 1 addition & 6 deletions src/mbgl/sprite/sprite_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,7 @@ SpriteImagePtr createSpriteImage(const PremultipliedImage&,

using Sprites = std::map<std::string, SpriteImagePtr>;


using SpriteParseResult = variant<
Sprites, // success
std::exception_ptr>; // error

// Parses an image and an associated JSON file and returns the sprite objects.
SpriteParseResult parseSprite(const std::string& image, const std::string& json);
Sprites parseSprite(const std::string& image, const std::string& json);

} // namespace mbgl
7 changes: 4 additions & 3 deletions src/mbgl/style/style.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ namespace style {

static Observer nullObserver;

Style::Style(FileSource& fileSource_, float pixelRatio)
: fileSource(fileSource_),
Style::Style(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio)
: scheduler(scheduler_),
fileSource(fileSource_),
glyphAtlas(std::make_unique<GlyphAtlas>(Size{ 2048, 2048 }, fileSource)),
spriteAtlas(std::make_unique<SpriteAtlas>(Size{ 1024, 1024 }, pixelRatio)),
lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })),
Expand Down Expand Up @@ -131,7 +132,7 @@ void Style::setJSON(const std::string& json) {
defaultPitch = parser.pitch;

glyphAtlas->setURL(parser.glyphURL);
spriteAtlas->load(parser.spriteURL, fileSource);
spriteAtlas->load(parser.spriteURL, scheduler, fileSource);

loaded = true;

Expand Down
4 changes: 3 additions & 1 deletion src/mbgl/style/style.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class LineAtlas;
class RenderData;
class TransformState;
class RenderedQueryOptions;
class Scheduler;

namespace style {

Expand All @@ -43,7 +44,7 @@ class Style : public GlyphAtlasObserver,
public LayerObserver,
public util::noncopyable {
public:
Style(FileSource&, float pixelRatio);
Style(Scheduler&, FileSource&, float pixelRatio);
~Style() override;

void setJSON(const std::string&);
Expand Down Expand Up @@ -108,6 +109,7 @@ class Style : public GlyphAtlasObserver,

void dumpDebugLogs() const;

Scheduler& scheduler;
FileSource& fileSource;
std::unique_ptr<GlyphAtlas> glyphAtlas;
std::unique_ptr<SpriteAtlas> spriteAtlas;
Expand Down
8 changes: 5 additions & 3 deletions test/sprite/sprite_atlas.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <mbgl/util/io.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/string.hpp>

#include <utility>
Expand All @@ -22,7 +23,7 @@ TEST(SpriteAtlas, Basic) {
util::read_file("test/fixtures/annotations/emerald.json"));

SpriteAtlas atlas({ 63, 112 }, 1);
atlas.setSprites(spriteParseResult.get<Sprites>());
atlas.setSprites(spriteParseResult);

EXPECT_EQ(1.0f, atlas.getPixelRatio());
EXPECT_EQ(63u, atlas.getSize().width);
Expand Down Expand Up @@ -75,7 +76,7 @@ TEST(SpriteAtlas, Size) {
util::read_file("test/fixtures/annotations/emerald.json"));

SpriteAtlas atlas({ 63, 112 }, 1.4);
atlas.setSprites(spriteParseResult.get<Sprites>());
atlas.setSprites(spriteParseResult);

EXPECT_DOUBLE_EQ(1.4f, atlas.getPixelRatio());
EXPECT_EQ(63u, atlas.getSize().width);
Expand Down Expand Up @@ -266,14 +267,15 @@ class SpriteAtlasTest {
util::RunLoop loop;
StubFileSource fileSource;
StubStyleObserver observer;
ThreadPool threadPool { 1 };
SpriteAtlas spriteAtlas{ { 32, 32 }, 1 };

void run() {
// Squelch logging.
Log::setObserver(std::make_unique<Log::NullObserver>());

spriteAtlas.setObserver(&observer);
spriteAtlas.load("test/fixtures/resources/sprite", fileSource);
spriteAtlas.load("test/fixtures/resources/sprite", threadPool, fileSource);

loop.run();
}
Expand Down
Loading

0 comments on commit ed2ccda

Please sign in to comment.