Files
MapLibreParticleLab/src/mbgl/sprite/sprite_loader.cpp
Mikhail Pozdnyakov 80cb05420a [core] Fix excessive onSpriteLoaded() notifications
What happened:

The excessive `onSpriteLoaded()` notifications were caused by the trick inside `OnlineFileRequest::completed()` implemenation,
which causes receving of two file source updates pointing to the *same* data in case the network response is `notModified` and
the local storage has the sprite sheet already stored inside.

In the SpriteLoader` implementation, it resulted in 2 extra `parseSprite()` calls and 2 extra `onSpriteLoaded()` notifications.

Then, the excessive `onSpriteLoaded()` notifications affected style images and all the unnecessarily added image dupes (btw,
the amount of these dupes `= 2 * number of style images`) were treated by the `RenderOrchestrator` as *updated* images.

Further, `RenderOrchestrator` called `ImageManager::updateImage()` for the updated images and the `ImageManager` populated the
`updatedImageVersions` map.

Finally, at every frame all the geometry tiles iterated through the `updatedImageVersions` to check whether their uploaded
textures needed patching.

All the mentioned above had a negative performance impact.

The fix:

Now, `SpriteLoader` sends `onSpriteLoaded()` notifications only if the data in the file source response is different comparing
to the existing one.
2020-02-12 20:49:26 +02:00

106 lines
3.5 KiB
C++

#include <mbgl/actor/actor.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/sprite/sprite_loader.hpp>
#include <mbgl/sprite/sprite_loader_observer.hpp>
#include <mbgl/sprite/sprite_loader_worker.hpp>
#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/async_request.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/platform.hpp>
#include <mbgl/util/std.hpp>
#include <cassert>
namespace mbgl {
static SpriteLoaderObserver nullObserver;
struct SpriteLoader::Loader {
Loader(SpriteLoader& imageManager)
: mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
worker(Scheduler::GetBackground(), ActorRef<SpriteLoader>(imageManager, 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<SpriteLoaderWorker> worker;
};
SpriteLoader::SpriteLoader(float pixelRatio_)
: pixelRatio(pixelRatio_)
, observer(&nullObserver) {
}
SpriteLoader::~SpriteLoader() = default;
void SpriteLoader::load(const std::string& url, FileSource& fileSource) {
if (url.empty()) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
observer->onSpriteLoaded({});
return;
}
loader = std::make_unique<Loader>(*this);
loader->jsonRequest = fileSource.request(Resource::spriteJSON(url, pixelRatio), [this](Response res) {
if (res.error) {
observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
} else if (res.notModified) {
return;
} else if (res.noContent) {
loader->json = std::make_shared<std::string>();
emitSpriteLoadedIfComplete();
} else if (loader->json != res.data) { // They can be equal, see OnlineFileRequest::completed().
// Only trigger a sprite loaded event we got new data.
loader->json = std::move(res.data);
emitSpriteLoadedIfComplete();
}
});
loader->spriteRequest = fileSource.request(Resource::spriteImage(url, pixelRatio), [this](Response res) {
if (res.error) {
observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
} else if (res.notModified) {
return;
} else if (res.noContent) {
loader->image = std::make_shared<std::string>();
emitSpriteLoadedIfComplete();
} else if (loader->image != res.data) { // They can be equal - see OnlineFileRequest::completed().
loader->image = std::move(res.data);
emitSpriteLoadedIfComplete();
}
});
}
void SpriteLoader::emitSpriteLoadedIfComplete() {
assert(loader);
if (!loader->image || !loader->json) {
return;
}
loader->worker.self().invoke(&SpriteLoaderWorker::parse, loader->image, loader->json);
}
void SpriteLoader::onParsed(std::vector<Immutable<style::Image::Impl>> result) {
observer->onSpriteLoaded(std::move(result));
}
void SpriteLoader::onError(std::exception_ptr err) {
observer->onSpriteError(err);
}
void SpriteLoader::setObserver(SpriteLoaderObserver* observer_) {
observer = observer_;
}
} // namespace mbgl