Asynchronous GeometryTile deletion (#2051)
Co-authored-by: Tim Sylvester <tim@wetdogweather.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include <mbgl/actor/mailbox.hpp>
|
||||
#include <mbgl/actor/message.hpp>
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
#include <mbgl/util/scoped.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
@@ -32,6 +33,8 @@ void Mailbox::open(Scheduler& scheduler_) {
|
||||
}
|
||||
|
||||
void Mailbox::close() {
|
||||
abandon();
|
||||
|
||||
// Block until neither receive() nor push() are in progress. Two mutexes are
|
||||
// used because receive() must not block send(). Of the two, the receiving
|
||||
// mutex must be acquired first, because that is the order that an actor
|
||||
@@ -44,14 +47,37 @@ void Mailbox::close() {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
void Mailbox::abandon() {
|
||||
auto idleValue = State::Idle;
|
||||
while (!state.compare_exchange_strong(idleValue, State::Abandoned)) {
|
||||
if (state == State::Abandoned) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Mailbox::isOpen() const {
|
||||
return bool(weakScheduler);
|
||||
}
|
||||
|
||||
void Mailbox::push(std::unique_ptr<Message> message) {
|
||||
auto idleState = State::Idle;
|
||||
while (!state.compare_exchange_strong(idleState, State::Processing)) {
|
||||
if (state == State::Abandoned) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Scoped activityFlag{[this]() {
|
||||
if (state == State::Processing) {
|
||||
state = State::Idle;
|
||||
}
|
||||
}};
|
||||
|
||||
std::lock_guard<std::mutex> pushingLock(pushingMutex);
|
||||
|
||||
if (closed) {
|
||||
state = State::Abandoned;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,12 +91,25 @@ void Mailbox::push(std::unique_ptr<Message> message) {
|
||||
}
|
||||
|
||||
void Mailbox::receive() {
|
||||
auto idleState = State::Idle;
|
||||
while (!state.compare_exchange_strong(idleState, State::Processing)) {
|
||||
if (state == State::Abandoned) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Scoped activityFlag{[this]() {
|
||||
if (state == State::Processing) {
|
||||
state = State::Idle;
|
||||
}
|
||||
}};
|
||||
std::lock_guard<std::recursive_mutex> receivingLock(receivingMutex);
|
||||
|
||||
auto guard = weakScheduler.lock();
|
||||
assert(weakScheduler);
|
||||
|
||||
if (closed) {
|
||||
state = State::Abandoned;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ namespace mbgl {
|
||||
|
||||
using namespace style;
|
||||
|
||||
RenderAnnotationSource::RenderAnnotationSource(Immutable<AnnotationSource::Impl> impl_)
|
||||
: RenderTileSource(std::move(impl_)) {
|
||||
RenderAnnotationSource::RenderAnnotationSource(Immutable<AnnotationSource::Impl> impl_,
|
||||
std::shared_ptr<Scheduler> threadPool_)
|
||||
: RenderTileSource(std::move(impl_), std::move(threadPool_)) {
|
||||
assert(LayerManager::annotationsEnabled);
|
||||
tilePyramid.setObserver(this);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace mbgl {
|
||||
|
||||
class RenderAnnotationSource final : public RenderTileSource {
|
||||
public:
|
||||
explicit RenderAnnotationSource(Immutable<AnnotationSource::Impl>);
|
||||
explicit RenderAnnotationSource(Immutable<AnnotationSource::Impl>, std::shared_ptr<Scheduler>);
|
||||
|
||||
void update(Immutable<style::Source::Impl>,
|
||||
const std::vector<Immutable<style::LayerProperties>>&,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <mbgl/util/traits.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <mbgl/util/thread_pool.hpp>
|
||||
|
||||
#if MLN_DRAWABLE_RENDERER
|
||||
#include <mbgl/gl/drawable_gl.hpp>
|
||||
@@ -81,6 +82,8 @@ Context::Context(RendererBackend& backend_)
|
||||
|
||||
Context::~Context() noexcept {
|
||||
if (cleanupOnDestruction) {
|
||||
Scheduler::GetBackground()->runRenderJobs();
|
||||
|
||||
reset();
|
||||
#if !defined(NDEBUG)
|
||||
Log::Debug(Event::General, "Rendering Stats:\n" + stats.toString("\n"));
|
||||
@@ -90,6 +93,8 @@ Context::~Context() noexcept {
|
||||
}
|
||||
|
||||
void Context::beginFrame() {
|
||||
Scheduler::GetBackground()->runRenderJobs();
|
||||
|
||||
#if MLN_DRAWABLE_RENDERER
|
||||
frameInFlightFence = std::make_shared<gl::Fence>();
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <mbgl/util/traits.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <mbgl/util/thread_pool.hpp>
|
||||
|
||||
#include <Foundation/Foundation.hpp>
|
||||
#include <Metal/Metal.hpp>
|
||||
@@ -42,6 +43,7 @@ Context::Context(RendererBackend& backend_)
|
||||
|
||||
Context::~Context() noexcept {
|
||||
if (cleanupOnDestruction) {
|
||||
Scheduler::GetBackground()->runRenderJobs();
|
||||
performCleanup();
|
||||
|
||||
emptyVertexBuffer.reset();
|
||||
@@ -60,7 +62,10 @@ Context::~Context() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void Context::beginFrame() {}
|
||||
void Context::beginFrame() {
|
||||
Scheduler::GetBackground()->runRenderJobs();
|
||||
}
|
||||
|
||||
void Context::endFrame() {}
|
||||
|
||||
std::unique_ptr<gfx::CommandEncoder> Context::createCommandEncoder() {
|
||||
|
||||
@@ -21,6 +21,7 @@ void ImageManager::setObserver(ImageManagerObserver* observer_) {
|
||||
}
|
||||
|
||||
void ImageManager::setLoaded(bool loaded_) {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
if (loaded == loaded_) {
|
||||
return;
|
||||
}
|
||||
@@ -31,6 +32,7 @@ void ImageManager::setLoaded(bool loaded_) {
|
||||
for (const auto& entry : requestors) {
|
||||
checkMissingAndNotify(*entry.first, entry.second);
|
||||
}
|
||||
|
||||
requestors.clear();
|
||||
}
|
||||
}
|
||||
@@ -40,16 +42,21 @@ bool ImageManager::isLoaded() const {
|
||||
}
|
||||
|
||||
void ImageManager::addImage(Immutable<style::Image::Impl> image_) {
|
||||
std::lock_guard<std::recursive_mutex> readLock(rwLock);
|
||||
assert(images.find(image_->id) == images.end());
|
||||
|
||||
// Increase cache size if requested image was provided.
|
||||
if (requestedImages.find(image_->id) != requestedImages.end()) {
|
||||
requestedImagesCacheSize += image_->image.bytes();
|
||||
}
|
||||
|
||||
availableImages.emplace(image_->id);
|
||||
images.emplace(image_->id, std::move(image_));
|
||||
}
|
||||
|
||||
bool ImageManager::updateImage(Immutable<style::Image::Impl> image_) {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
|
||||
auto oldImage = images.find(image_->id);
|
||||
assert(oldImage != images.end());
|
||||
if (oldImage == images.end()) return false;
|
||||
@@ -74,8 +81,10 @@ bool ImageManager::updateImage(Immutable<style::Image::Impl> image_) {
|
||||
}
|
||||
|
||||
void ImageManager::removeImage(const std::string& id) {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
auto it = images.find(id);
|
||||
assert(it != images.end());
|
||||
|
||||
// Reduce cache size for requested images.
|
||||
auto requestedIt = requestedImages.find(it->second->id);
|
||||
if (requestedIt != requestedImages.end()) {
|
||||
@@ -83,12 +92,14 @@ void ImageManager::removeImage(const std::string& id) {
|
||||
requestedImagesCacheSize -= it->second->image.bytes();
|
||||
requestedImages.erase(requestedIt);
|
||||
}
|
||||
|
||||
images.erase(it);
|
||||
availableImages.erase(id);
|
||||
updatedImageVersions.erase(id);
|
||||
}
|
||||
|
||||
const style::Image::Impl* ImageManager::getImage(const std::string& id) const {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
if (auto* image = getSharedImage(id)) {
|
||||
return image->get();
|
||||
}
|
||||
@@ -96,6 +107,7 @@ const style::Image::Impl* ImageManager::getImage(const std::string& id) const {
|
||||
}
|
||||
|
||||
const Immutable<style::Image::Impl>* ImageManager::getSharedImage(const std::string& id) const {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
const auto it = images.find(id);
|
||||
if (it != images.end()) {
|
||||
return &(it->second);
|
||||
@@ -107,6 +119,8 @@ void ImageManager::getImages(ImageRequestor& requestor, ImageRequestPair&& pair)
|
||||
// remove previous requests from this tile
|
||||
removeRequestor(requestor);
|
||||
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
|
||||
// If all the icon dependencies are already present ((i.e. if they've been addeded via
|
||||
// runtime styling), then notify the requestor immediately. Otherwise, if the
|
||||
// sprite has not loaded, then wait for it. When the sprite has loaded check
|
||||
@@ -133,6 +147,8 @@ void ImageManager::getImages(ImageRequestor& requestor, ImageRequestPair&& pair)
|
||||
}
|
||||
|
||||
void ImageManager::removeRequestor(ImageRequestor& requestor) {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
|
||||
requestors.erase(&requestor);
|
||||
missingImageRequestors.erase(&requestor);
|
||||
for (auto& requestedImage : requestedImages) {
|
||||
@@ -141,6 +157,8 @@ void ImageManager::removeRequestor(ImageRequestor& requestor) {
|
||||
}
|
||||
|
||||
void ImageManager::notifyIfMissingImageAdded() {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
|
||||
for (auto it = missingImageRequestors.begin(); it != missingImageRequestors.end();) {
|
||||
ImageRequestor& requestor = *it->first;
|
||||
if (!requestor.hasPendingRequests()) {
|
||||
@@ -153,6 +171,8 @@ void ImageManager::notifyIfMissingImageAdded() {
|
||||
}
|
||||
|
||||
void ImageManager::reduceMemoryUse() {
|
||||
std::lock_guard<std::recursive_mutex> readLock(rwLock);
|
||||
|
||||
std::vector<std::string> unusedIDs;
|
||||
unusedIDs.reserve(requestedImages.size());
|
||||
|
||||
@@ -173,11 +193,18 @@ void ImageManager::reduceMemoryUseIfCacheSizeExceedsLimit() {
|
||||
}
|
||||
}
|
||||
|
||||
const std::set<std::string>& ImageManager::getAvailableImages() const {
|
||||
return availableImages;
|
||||
std::set<std::string> ImageManager::getAvailableImages() const {
|
||||
std::set<std::string> copy;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
copy = availableImages;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
void ImageManager::clear() {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
|
||||
assert(requestors.empty());
|
||||
assert(missingImageRequestors.empty());
|
||||
|
||||
@@ -229,6 +256,7 @@ void ImageManager::checkMissingAndNotify(ImageRequestor& requestor, const ImageR
|
||||
}
|
||||
|
||||
auto removePendingRequests = [this, missingImage] {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
auto existingRequest = requestedImages.find(missingImage);
|
||||
if (existingRequest == requestedImages.end()) {
|
||||
return;
|
||||
@@ -278,11 +306,11 @@ void ImageManager::dumpDebugLogs() const {
|
||||
Log::Info(Event::General, ss.str());
|
||||
}
|
||||
|
||||
ImageRequestor::ImageRequestor(ImageManager& imageManager_)
|
||||
: imageManager(imageManager_) {}
|
||||
ImageRequestor::ImageRequestor(std::shared_ptr<ImageManager> imageManager_)
|
||||
: imageManager(std::move(imageManager_)) {}
|
||||
|
||||
ImageRequestor::~ImageRequestor() {
|
||||
imageManager.removeRequestor(*this);
|
||||
imageManager->removeRequestor(*this);
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <mbgl/util/immutable.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -48,7 +49,7 @@ public:
|
||||
void notifyIfMissingImageAdded();
|
||||
void reduceMemoryUse();
|
||||
void reduceMemoryUseIfCacheSizeExceedsLimit();
|
||||
const std::set<std::string>& getAvailableImages() const;
|
||||
std::set<std::string> getAvailableImages() const;
|
||||
|
||||
ImageVersionMap updatedImageVersions;
|
||||
|
||||
@@ -57,7 +58,6 @@ public:
|
||||
private:
|
||||
void checkMissingAndNotify(ImageRequestor&, const ImageRequestPair&);
|
||||
void notify(ImageRequestor&, const ImageRequestPair&) const;
|
||||
void removePattern(const std::string&);
|
||||
|
||||
bool loaded = false;
|
||||
|
||||
@@ -70,11 +70,13 @@ private:
|
||||
std::set<std::string> availableImages;
|
||||
|
||||
ImageManagerObserver* observer = nullptr;
|
||||
|
||||
mutable std::recursive_mutex rwLock;
|
||||
};
|
||||
|
||||
class ImageRequestor {
|
||||
public:
|
||||
explicit ImageRequestor(ImageManager&);
|
||||
explicit ImageRequestor(std::shared_ptr<ImageManager>);
|
||||
virtual ~ImageRequestor();
|
||||
virtual void onImagesAvailable(ImageMap icons,
|
||||
ImageMap patterns,
|
||||
@@ -87,7 +89,7 @@ public:
|
||||
void removePendingRequest(const std::string& imageId) { pendingRequests.erase(imageId); }
|
||||
|
||||
private:
|
||||
ImageManager& imageManager;
|
||||
std::shared_ptr<ImageManager> imageManager;
|
||||
|
||||
// Pending requests are image requests that are waiting to be dispatched to the client.
|
||||
std::set<std::string> pendingRequests;
|
||||
|
||||
@@ -121,7 +121,8 @@ RenderOrchestrator::RenderOrchestrator(bool backgroundLayerAsColor_, const std::
|
||||
sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>()),
|
||||
layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>()),
|
||||
renderLight(makeMutable<Light::Impl>()),
|
||||
backgroundLayerAsColor(backgroundLayerAsColor_) {
|
||||
backgroundLayerAsColor(backgroundLayerAsColor_),
|
||||
threadPool(Scheduler::GetBackground()) {
|
||||
glyphManager->setObserver(this);
|
||||
imageManager->setObserver(this);
|
||||
}
|
||||
@@ -137,6 +138,13 @@ RenderOrchestrator::~RenderOrchestrator() {
|
||||
layer.markContextDestroyed();
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for any deferred cleanup tasks to complete before releasing and potentially
|
||||
// destroying the scheduler. Those cleanup tasks must not hold the final reference
|
||||
// to the scheduler because it cannot be destroyed from one of its own pool threads.
|
||||
constexpr auto deferredCleanupTimeout = Milliseconds{1000};
|
||||
[[maybe_unused]] const auto remaining = threadPool->waitForEmpty(deferredCleanupTimeout);
|
||||
assert(remaining == 0);
|
||||
}
|
||||
|
||||
void RenderOrchestrator::setObserver(RendererObserver* observer_) {
|
||||
@@ -180,8 +188,8 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(
|
||||
updateParameters->fileSource,
|
||||
updateParameters->mode,
|
||||
updateParameters->annotationManager,
|
||||
*imageManager,
|
||||
*glyphManager,
|
||||
imageManager,
|
||||
glyphManager,
|
||||
updateParameters->prefetchZoomDelta};
|
||||
|
||||
glyphManager->setURL(updateParameters->glyphURL);
|
||||
@@ -311,7 +319,7 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(
|
||||
|
||||
// Create render sources for newly added sources.
|
||||
for (const auto& entry : sourceDiff.added) {
|
||||
std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second);
|
||||
std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second, threadPool);
|
||||
renderSource->setObserver(this);
|
||||
renderSources.emplace(entry.first, std::move(renderSource));
|
||||
}
|
||||
|
||||
@@ -184,8 +184,8 @@ private:
|
||||
ZoomHistory zoomHistory;
|
||||
TransformState transformState;
|
||||
|
||||
std::unique_ptr<GlyphManager> glyphManager;
|
||||
std::unique_ptr<ImageManager> imageManager;
|
||||
std::shared_ptr<GlyphManager> glyphManager;
|
||||
std::shared_ptr<ImageManager> imageManager;
|
||||
std::unique_ptr<LineAtlas> lineAtlas;
|
||||
std::unique_ptr<PatternAtlas> patternAtlas;
|
||||
|
||||
@@ -210,6 +210,8 @@ private:
|
||||
RenderLayerReferences orderedLayers;
|
||||
RenderLayerReferences layersNeedPlacement;
|
||||
|
||||
std::shared_ptr<Scheduler> threadPool;
|
||||
|
||||
#if MLN_DRAWABLE_RENDERER
|
||||
std::vector<std::unique_ptr<ChangeRequest>> pendingChanges;
|
||||
|
||||
|
||||
@@ -12,28 +12,36 @@
|
||||
|
||||
#include <mbgl/layermanager/layer_manager.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
using namespace style;
|
||||
|
||||
std::unique_ptr<RenderSource> RenderSource::create(const Immutable<Source::Impl>& impl) {
|
||||
std::unique_ptr<RenderSource> RenderSource::create(const Immutable<Source::Impl>& impl,
|
||||
std::shared_ptr<Scheduler> threadPool_) {
|
||||
switch (impl->type) {
|
||||
case SourceType::Vector:
|
||||
return std::make_unique<RenderVectorSource>(staticImmutableCast<VectorSource::Impl>(impl));
|
||||
return std::make_unique<RenderVectorSource>(staticImmutableCast<VectorSource::Impl>(impl),
|
||||
std::move(threadPool_));
|
||||
case SourceType::Raster:
|
||||
return std::make_unique<RenderRasterSource>(staticImmutableCast<RasterSource::Impl>(impl));
|
||||
return std::make_unique<RenderRasterSource>(staticImmutableCast<RasterSource::Impl>(impl),
|
||||
std::move(threadPool_));
|
||||
case SourceType::RasterDEM:
|
||||
return std::make_unique<RenderRasterDEMSource>(staticImmutableCast<RasterSource::Impl>(impl));
|
||||
return std::make_unique<RenderRasterDEMSource>(staticImmutableCast<RasterSource::Impl>(impl),
|
||||
std::move(threadPool_));
|
||||
case SourceType::GeoJSON:
|
||||
return std::make_unique<RenderGeoJSONSource>(staticImmutableCast<GeoJSONSource::Impl>(impl));
|
||||
return std::make_unique<RenderGeoJSONSource>(staticImmutableCast<GeoJSONSource::Impl>(impl),
|
||||
std::move(threadPool_));
|
||||
case SourceType::Video:
|
||||
assert(false);
|
||||
return nullptr;
|
||||
case SourceType::Annotations:
|
||||
if (LayerManager::annotationsEnabled) {
|
||||
return std::make_unique<RenderAnnotationSource>(staticImmutableCast<AnnotationSource::Impl>(impl));
|
||||
return std::make_unique<RenderAnnotationSource>(staticImmutableCast<AnnotationSource::Impl>(impl),
|
||||
std::move(threadPool_));
|
||||
} else {
|
||||
assert(false);
|
||||
return nullptr;
|
||||
@@ -41,7 +49,8 @@ std::unique_ptr<RenderSource> RenderSource::create(const Immutable<Source::Impl>
|
||||
case SourceType::Image:
|
||||
return std::make_unique<RenderImageSource>(staticImmutableCast<ImageSource::Impl>(impl));
|
||||
case SourceType::CustomVector:
|
||||
return std::make_unique<RenderCustomGeometrySource>(staticImmutableCast<CustomGeometrySource::Impl>(impl));
|
||||
return std::make_unique<RenderCustomGeometrySource>(staticImmutableCast<CustomGeometrySource::Impl>(impl),
|
||||
std::move(threadPool_));
|
||||
}
|
||||
|
||||
// Not reachable, but placate GCC.
|
||||
|
||||
@@ -17,20 +17,21 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class PaintParameters;
|
||||
class TransformState;
|
||||
class RenderTile;
|
||||
class RenderLayer;
|
||||
class RenderedQueryOptions;
|
||||
class SourceQueryOptions;
|
||||
class Tile;
|
||||
class RenderSourceObserver;
|
||||
class TileParameters;
|
||||
class CollisionIndex;
|
||||
class TransformParameters;
|
||||
class ImageManager;
|
||||
class ImageSourceRenderData;
|
||||
class PaintParameters;
|
||||
class RenderedQueryOptions;
|
||||
class RenderItem;
|
||||
class RenderLayer;
|
||||
class RenderSourceObserver;
|
||||
class RenderTile;
|
||||
class Scheduler;
|
||||
class SourceQueryOptions;
|
||||
class Tile;
|
||||
class TileParameters;
|
||||
class TransformParameters;
|
||||
class TransformState;
|
||||
|
||||
namespace gfx {
|
||||
class UploadPass;
|
||||
@@ -47,7 +48,7 @@ using RenderTiles = std::shared_ptr<const std::vector<std::reference_wrapper<con
|
||||
|
||||
class RenderSource : protected TileObserver {
|
||||
public:
|
||||
static std::unique_ptr<RenderSource> create(const Immutable<style::Source::Impl>&);
|
||||
static std::unique_ptr<RenderSource> create(const Immutable<style::Source::Impl>&, std::shared_ptr<Scheduler>);
|
||||
~RenderSource() override;
|
||||
|
||||
bool isEnabled() const;
|
||||
|
||||
@@ -7,8 +7,9 @@ namespace mbgl {
|
||||
|
||||
using namespace style;
|
||||
|
||||
RenderCustomGeometrySource::RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl> impl_)
|
||||
: RenderTileSource(std::move(impl_)) {
|
||||
RenderCustomGeometrySource::RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl> impl_,
|
||||
std::shared_ptr<Scheduler> threadPool_)
|
||||
: RenderTileSource(std::move(impl_), std::move(threadPool_)) {
|
||||
tilePyramid.setObserver(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace mbgl {
|
||||
|
||||
class RenderCustomGeometrySource final : public RenderTileSource {
|
||||
public:
|
||||
explicit RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl>);
|
||||
explicit RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl>, std::shared_ptr<Scheduler>);
|
||||
|
||||
void update(Immutable<style::Source::Impl>,
|
||||
const std::vector<Immutable<style::LayerProperties>>&,
|
||||
|
||||
@@ -65,8 +65,9 @@ MAPBOX_ETERNAL_CONSTEXPR const auto extensionGetters =
|
||||
|
||||
} // namespace
|
||||
|
||||
RenderGeoJSONSource::RenderGeoJSONSource(Immutable<style::GeoJSONSource::Impl> impl_)
|
||||
: RenderTileSource(std::move(impl_)) {}
|
||||
RenderGeoJSONSource::RenderGeoJSONSource(Immutable<style::GeoJSONSource::Impl> impl_,
|
||||
std::shared_ptr<Scheduler> threadPool_)
|
||||
: RenderTileSource(std::move(impl_), std::move(threadPool_)) {}
|
||||
|
||||
RenderGeoJSONSource::~RenderGeoJSONSource() = default;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class GeoJSONData;
|
||||
|
||||
class RenderGeoJSONSource final : public RenderTileSource {
|
||||
public:
|
||||
explicit RenderGeoJSONSource(Immutable<style::GeoJSONSource::Impl>);
|
||||
explicit RenderGeoJSONSource(Immutable<style::GeoJSONSource::Impl>, std::shared_ptr<Scheduler>);
|
||||
~RenderGeoJSONSource() override;
|
||||
|
||||
void update(Immutable<style::Source::Impl>,
|
||||
|
||||
@@ -10,8 +10,9 @@ namespace mbgl {
|
||||
|
||||
using namespace style;
|
||||
|
||||
RenderRasterDEMSource::RenderRasterDEMSource(Immutable<style::RasterSource::Impl> impl_)
|
||||
: RenderTileSetSource(std::move(impl_)) {}
|
||||
RenderRasterDEMSource::RenderRasterDEMSource(Immutable<style::RasterSource::Impl> impl_,
|
||||
std::shared_ptr<Scheduler> threadPool_)
|
||||
: RenderTileSetSource(std::move(impl_), std::move(threadPool_)) {}
|
||||
|
||||
const style::RasterSource::Impl& RenderRasterDEMSource::impl() const {
|
||||
return static_cast<const style::RasterSource::Impl&>(*baseImpl);
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace mbgl {
|
||||
|
||||
class RenderRasterDEMSource final : public RenderTileSetSource {
|
||||
public:
|
||||
explicit RenderRasterDEMSource(Immutable<style::RasterSource::Impl>);
|
||||
explicit RenderRasterDEMSource(Immutable<style::RasterSource::Impl>, std::shared_ptr<Scheduler>);
|
||||
|
||||
std::unordered_map<std::string, std::vector<Feature>> queryRenderedFeatures(
|
||||
const ScreenLineString& geometry,
|
||||
|
||||
@@ -8,8 +8,9 @@ namespace mbgl {
|
||||
|
||||
using namespace style;
|
||||
|
||||
RenderRasterSource::RenderRasterSource(Immutable<style::RasterSource::Impl> impl_)
|
||||
: RenderTileSetSource(std::move(impl_)) {}
|
||||
RenderRasterSource::RenderRasterSource(Immutable<style::RasterSource::Impl> impl_,
|
||||
std::shared_ptr<Scheduler> threadPool_)
|
||||
: RenderTileSetSource(std::move(impl_), std::move(threadPool_)) {}
|
||||
|
||||
inline const style::RasterSource::Impl& RenderRasterSource::impl() const {
|
||||
return static_cast<const style::RasterSource::Impl&>(*baseImpl);
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace mbgl {
|
||||
|
||||
class RenderRasterSource final : public RenderTileSetSource {
|
||||
public:
|
||||
explicit RenderRasterSource(Immutable<style::RasterSource::Impl>);
|
||||
explicit RenderRasterSource(Immutable<style::RasterSource::Impl>, std::shared_ptr<Scheduler>);
|
||||
|
||||
private:
|
||||
void prepare(const SourcePrepareParameters&) final;
|
||||
|
||||
@@ -369,8 +369,9 @@ void TileSourceRenderItem::updateDebugDrawables(DebugLayerGroupMap& debugLayerGr
|
||||
}
|
||||
#endif
|
||||
|
||||
RenderTileSource::RenderTileSource(Immutable<style::Source::Impl> impl_)
|
||||
RenderTileSource::RenderTileSource(Immutable<style::Source::Impl> impl_, std::shared_ptr<Scheduler> threadPool_)
|
||||
: RenderSource(std::move(impl_)),
|
||||
tilePyramid(std::move(threadPool_)),
|
||||
renderTiles(makeMutable<std::vector<RenderTile>>()) {
|
||||
tilePyramid.setObserver(this);
|
||||
}
|
||||
@@ -489,8 +490,8 @@ void RenderTileSource::dumpDebugLogs() const {
|
||||
|
||||
// RenderTileSetSource implementation
|
||||
|
||||
RenderTileSetSource::RenderTileSetSource(Immutable<style::Source::Impl> impl_)
|
||||
: RenderTileSource(std::move(impl_)) {}
|
||||
RenderTileSetSource::RenderTileSetSource(Immutable<style::Source::Impl> impl_, std::shared_ptr<Scheduler> threadPool_)
|
||||
: RenderTileSource(std::move(impl_), std::move(threadPool_)) {}
|
||||
|
||||
RenderTileSetSource::~RenderTileSetSource() = default;
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
void dumpDebugLogs() const override;
|
||||
|
||||
protected:
|
||||
RenderTileSource(Immutable<style::Source::Impl>);
|
||||
RenderTileSource(Immutable<style::Source::Impl>, std::shared_ptr<Scheduler>);
|
||||
TilePyramid tilePyramid;
|
||||
Immutable<std::vector<RenderTile>> renderTiles;
|
||||
mutable RenderTiles filteredRenderTiles;
|
||||
@@ -67,7 +67,7 @@ private:
|
||||
*/
|
||||
class RenderTileSetSource : public RenderTileSource {
|
||||
protected:
|
||||
RenderTileSetSource(Immutable<style::Source::Impl>);
|
||||
RenderTileSetSource(Immutable<style::Source::Impl>, std::shared_ptr<Scheduler>);
|
||||
~RenderTileSetSource() override;
|
||||
|
||||
virtual void updateInternal(const Tileset&,
|
||||
|
||||
@@ -8,8 +8,9 @@ namespace mbgl {
|
||||
|
||||
using namespace style;
|
||||
|
||||
RenderVectorSource::RenderVectorSource(Immutable<style::VectorSource::Impl> impl_)
|
||||
: RenderTileSetSource(std::move(impl_)) {}
|
||||
RenderVectorSource::RenderVectorSource(Immutable<style::VectorSource::Impl> impl_,
|
||||
std::shared_ptr<Scheduler> threadPool_)
|
||||
: RenderTileSetSource(std::move(impl_), std::move(threadPool_)) {}
|
||||
|
||||
const std::optional<Tileset>& RenderVectorSource::getTileset() const {
|
||||
return static_cast<const style::VectorSource::Impl&>(*baseImpl).tileset;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace mbgl {
|
||||
|
||||
class RenderVectorSource final : public RenderTileSetSource {
|
||||
public:
|
||||
explicit RenderVectorSource(Immutable<style::VectorSource::Impl>);
|
||||
explicit RenderVectorSource(Immutable<style::VectorSource::Impl>, std::shared_ptr<Scheduler>);
|
||||
|
||||
private:
|
||||
void updateInternal(const Tileset&,
|
||||
|
||||
@@ -22,8 +22,8 @@ public:
|
||||
std::shared_ptr<FileSource> fileSource;
|
||||
const MapMode mode;
|
||||
mapbox::base::WeakPtr<AnnotationManager> annotationManager;
|
||||
ImageManager& imageManager;
|
||||
GlyphManager& glyphManager;
|
||||
std::shared_ptr<ImageManager> imageManager;
|
||||
std::shared_ptr<GlyphManager> glyphManager;
|
||||
const uint8_t prefetchZoomDelta;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <mbgl/renderer/query.hpp>
|
||||
#include <mbgl/map/transform.hpp>
|
||||
#include <mbgl/math/clamp.hpp>
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
#include <mbgl/util/tile_cover.hpp>
|
||||
#include <mbgl/util/tile_range.hpp>
|
||||
#include <mbgl/util/enum.hpp>
|
||||
@@ -23,8 +24,9 @@ using namespace style;
|
||||
|
||||
static TileObserver nullObserver;
|
||||
|
||||
TilePyramid::TilePyramid()
|
||||
: observer(&nullObserver) {}
|
||||
TilePyramid::TilePyramid(std::shared_ptr<Scheduler> threadPool_)
|
||||
: cache(std::move(threadPool_)),
|
||||
observer(&nullObserver) {}
|
||||
|
||||
TilePyramid::~TilePyramid() = default;
|
||||
|
||||
@@ -65,13 +67,15 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
|
||||
// If we're not going to render anything, move our existing tiles into
|
||||
// the cache (if they're not stale) or abandon them, and return.
|
||||
if (!needsRendering) {
|
||||
if (!needsRelayout) {
|
||||
for (auto& entry : tiles) {
|
||||
for (auto& entry : tiles) {
|
||||
if (!needsRelayout) {
|
||||
// These tiles are invisible, we set optional necessity
|
||||
// for them and thus suppress network requests on
|
||||
// tiles expiration (see `OnlineFileRequest`).
|
||||
entry.second->setNecessity(TileNecessity::Optional);
|
||||
cache.add(entry.first, std::move(entry.second));
|
||||
} else {
|
||||
cache.deferredRelease(std::move(entry.second));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,11 +231,17 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
|
||||
auto retainIt = retain.begin();
|
||||
while (tilesIt != tiles.end()) {
|
||||
if (retainIt == retain.end() || tilesIt->first < *retainIt) {
|
||||
if (!needsRelayout) {
|
||||
tilesIt->second->setNecessity(TileNecessity::Optional);
|
||||
cache.add(tilesIt->first, std::move(tilesIt->second));
|
||||
// Remove the tile from the map.
|
||||
// If it requires re-layout, discard it asynchronously, otherwise keep it in the cache
|
||||
const auto key = tilesIt->first;
|
||||
if (std::unique_ptr<Tile> tile = std::move(tiles.extract(tilesIt++).mapped())) {
|
||||
if (needsRelayout) {
|
||||
cache.deferredRelease(std::move(tile));
|
||||
} else {
|
||||
tile->setNecessity(TileNecessity::Optional);
|
||||
cache.add(key, std::move(tile));
|
||||
}
|
||||
}
|
||||
tiles.erase(tilesIt++);
|
||||
} else {
|
||||
if (!(*retainIt < tilesIt->first)) {
|
||||
++tilesIt;
|
||||
|
||||
@@ -29,7 +29,7 @@ class SourcePrepareParameters;
|
||||
|
||||
class TilePyramid {
|
||||
public:
|
||||
TilePyramid();
|
||||
TilePyramid(std::shared_ptr<Scheduler> threadPool_);
|
||||
~TilePyramid();
|
||||
|
||||
bool isLoaded() const;
|
||||
|
||||
@@ -21,33 +21,36 @@ GlyphManager::~GlyphManager() = default;
|
||||
void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphDependencies, FileSource& fileSource) {
|
||||
auto dependencies = std::make_shared<GlyphDependencies>(std::move(glyphDependencies));
|
||||
|
||||
// Figure out which glyph ranges need to be fetched. For each range that
|
||||
// does need to be fetched, record an entry mapping the requestor to a
|
||||
// shared pointer containing the dependencies. When the shared pointer
|
||||
// becomes unique, we know that all the dependencies for that requestor have
|
||||
// been fetched, and can notify it of completion.
|
||||
for (const auto& dependency : *dependencies) {
|
||||
const FontStack& fontStack = dependency.first;
|
||||
Entry& entry = entries[fontStack];
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
// Figure out which glyph ranges need to be fetched. For each range that
|
||||
// does need to be fetched, record an entry mapping the requestor to a
|
||||
// shared pointer containing the dependencies. When the shared pointer
|
||||
// becomes unique, we know that all the dependencies for that requestor have
|
||||
// been fetched, and can notify it of completion.
|
||||
for (const auto& dependency : *dependencies) {
|
||||
const FontStack& fontStack = dependency.first;
|
||||
Entry& entry = entries[fontStack];
|
||||
|
||||
const GlyphIDs& glyphIDs = dependency.second;
|
||||
std::unordered_set<GlyphRange> ranges;
|
||||
for (const auto& glyphID : glyphIDs) {
|
||||
if (localGlyphRasterizer->canRasterizeGlyph(fontStack, glyphID)) {
|
||||
if (entry.glyphs.find(glyphID) == entry.glyphs.end()) {
|
||||
entry.glyphs.emplace(glyphID, makeMutable<Glyph>(generateLocalSDF(fontStack, glyphID)));
|
||||
const GlyphIDs& glyphIDs = dependency.second;
|
||||
std::unordered_set<GlyphRange> ranges;
|
||||
for (const auto& glyphID : glyphIDs) {
|
||||
if (localGlyphRasterizer->canRasterizeGlyph(fontStack, glyphID)) {
|
||||
if (entry.glyphs.find(glyphID) == entry.glyphs.end()) {
|
||||
entry.glyphs.emplace(glyphID, makeMutable<Glyph>(generateLocalSDF(fontStack, glyphID)));
|
||||
}
|
||||
} else {
|
||||
ranges.insert(getGlyphRange(glyphID));
|
||||
}
|
||||
} else {
|
||||
ranges.insert(getGlyphRange(glyphID));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& range : ranges) {
|
||||
auto it = entry.ranges.find(range);
|
||||
if (it == entry.ranges.end() || !it->second.parsed) {
|
||||
GlyphRequest& request = entry.ranges[range];
|
||||
request.requestors[&requestor] = dependencies;
|
||||
requestRange(request, fontStack, range, fileSource);
|
||||
for (const auto& range : ranges) {
|
||||
auto it = entry.ranges.find(range);
|
||||
if (it == entry.ranges.end() || !it->second.parsed) {
|
||||
GlyphRequest& request = entry.ranges[range];
|
||||
request.requestors[&requestor] = dependencies;
|
||||
requestRange(request, fontStack, range, fileSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,39 +91,43 @@ void GlyphManager::processResponse(const Response& res, const FontStack& fontSta
|
||||
return;
|
||||
}
|
||||
|
||||
Entry& entry = entries[fontStack];
|
||||
GlyphRequest& request = entry.ranges[range];
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
|
||||
if (!res.noContent) {
|
||||
std::vector<Glyph> glyphs;
|
||||
Entry& entry = entries[fontStack];
|
||||
GlyphRequest& request = entry.ranges[range];
|
||||
|
||||
try {
|
||||
glyphs = parseGlyphPBF(range, *res.data);
|
||||
} catch (...) {
|
||||
observer->onGlyphsError(fontStack, range, std::current_exception());
|
||||
return;
|
||||
}
|
||||
if (!res.noContent) {
|
||||
std::vector<Glyph> glyphs;
|
||||
|
||||
for (auto& glyph : glyphs) {
|
||||
auto id = glyph.id;
|
||||
if (!localGlyphRasterizer->canRasterizeGlyph(fontStack, id)) {
|
||||
entry.glyphs.erase(id);
|
||||
entry.glyphs.emplace(id, makeMutable<Glyph>(std::move(glyph)));
|
||||
try {
|
||||
glyphs = parseGlyphPBF(range, *res.data);
|
||||
} catch (...) {
|
||||
observer->onGlyphsError(fontStack, range, std::current_exception());
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& glyph : glyphs) {
|
||||
auto id = glyph.id;
|
||||
if (!localGlyphRasterizer->canRasterizeGlyph(fontStack, id)) {
|
||||
entry.glyphs.erase(id);
|
||||
entry.glyphs.emplace(id, makeMutable<Glyph>(std::move(glyph)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request.parsed = true;
|
||||
request.parsed = true;
|
||||
|
||||
for (auto& pair : request.requestors) {
|
||||
GlyphRequestor& requestor = *pair.first;
|
||||
const std::shared_ptr<GlyphDependencies>& dependencies = pair.second;
|
||||
if (dependencies.unique()) {
|
||||
notify(requestor, *dependencies);
|
||||
for (auto& pair : request.requestors) {
|
||||
GlyphRequestor& requestor = *pair.first;
|
||||
const std::shared_ptr<GlyphDependencies>& dependencies = pair.second;
|
||||
if (dependencies.unique()) {
|
||||
notify(requestor, *dependencies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request.requestors.clear();
|
||||
request.requestors.clear();
|
||||
}
|
||||
|
||||
observer->onGlyphsLoaded(fontStack, range);
|
||||
}
|
||||
@@ -153,6 +160,7 @@ void GlyphManager::notify(GlyphRequestor& requestor, const GlyphDependencies& gl
|
||||
}
|
||||
|
||||
void GlyphManager::removeRequestor(GlyphRequestor& requestor) {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
for (auto& entry : entries) {
|
||||
for (auto& range : entry.second.ranges) {
|
||||
range.second.requestors.erase(&requestor);
|
||||
@@ -161,6 +169,7 @@ void GlyphManager::removeRequestor(GlyphRequestor& requestor) {
|
||||
}
|
||||
|
||||
void GlyphManager::evict(const std::set<FontStack>& keep) {
|
||||
std::lock_guard<std::recursive_mutex> readWriteLock(rwLock);
|
||||
util::erase_if(entries, [&](const auto& entry) { return keep.count(entry.first) == 0; });
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <mbgl/util/font_stack.hpp>
|
||||
#include <mbgl/util/immutable.hpp>
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -71,6 +72,8 @@ private:
|
||||
GlyphManagerObserver* observer = nullptr;
|
||||
|
||||
std::unique_ptr<LocalGlyphRasterizer> localGlyphRasterizer;
|
||||
|
||||
std::recursive_mutex rwLock;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <mbgl/tile/geometry_tile_worker.hpp>
|
||||
#include <mbgl/tile/tile_observer.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <mbgl/util/thread_pool.hpp>
|
||||
|
||||
#include <mbgl/gfx/upload_pass.hpp>
|
||||
#include <utility>
|
||||
@@ -159,8 +160,9 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_, std::string sourceID_, c
|
||||
: Tile(Kind::Geometry, id_),
|
||||
ImageRequestor(parameters.imageManager),
|
||||
sourceID(std::move(sourceID_)),
|
||||
threadPool(Scheduler::GetBackground()),
|
||||
mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
|
||||
worker(Scheduler::GetBackground(),
|
||||
worker(threadPool,
|
||||
ActorRef<GeometryTile>(*this, mailbox),
|
||||
id_,
|
||||
sourceID,
|
||||
@@ -175,8 +177,15 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_, std::string sourceID_, c
|
||||
showCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision) {}
|
||||
|
||||
GeometryTile::~GeometryTile() {
|
||||
glyphManager.removeRequestor(*this);
|
||||
markObsolete();
|
||||
|
||||
glyphManager->removeRequestor(*this);
|
||||
imageManager->removeRequestor(*this);
|
||||
|
||||
if (layoutResult) {
|
||||
threadPool->runOnRenderThread(
|
||||
[layoutResult_{std::move(layoutResult)}, atlasTextures_{std::move(atlasTextures)}]() {});
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryTile::cancel() {
|
||||
@@ -185,6 +194,7 @@ void GeometryTile::cancel() {
|
||||
|
||||
void GeometryTile::markObsolete() {
|
||||
obsolete = true;
|
||||
mailbox->abandon();
|
||||
}
|
||||
|
||||
void GeometryTile::setError(std::exception_ptr err) {
|
||||
@@ -193,13 +203,17 @@ void GeometryTile::setError(std::exception_ptr err) {
|
||||
}
|
||||
|
||||
void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_) {
|
||||
if (obsolete) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the tile as pending again if it was complete before to prevent
|
||||
// signaling a complete state despite pending parse operations.
|
||||
pending = true;
|
||||
|
||||
++correlationID;
|
||||
worker.self().invoke(
|
||||
&GeometryTileWorker::setData, std::move(data_), imageManager.getAvailableImages(), correlationID);
|
||||
&GeometryTileWorker::setData, std::move(data_), imageManager->getAvailableImages(), correlationID);
|
||||
}
|
||||
|
||||
void GeometryTile::reset() {
|
||||
@@ -238,7 +252,7 @@ void GeometryTile::setLayers(const std::vector<Immutable<LayerProperties>>& laye
|
||||
|
||||
++correlationID;
|
||||
worker.self().invoke(
|
||||
&GeometryTileWorker::setLayers, std::move(impls), imageManager.getAvailableImages(), correlationID);
|
||||
&GeometryTileWorker::setLayers, std::move(impls), imageManager->getAvailableImages(), correlationID);
|
||||
}
|
||||
|
||||
void GeometryTile::setShowCollisionBoxes(const bool showCollisionBoxes_) {
|
||||
@@ -278,7 +292,7 @@ void GeometryTile::onGlyphsAvailable(GlyphMap glyphs) {
|
||||
|
||||
void GeometryTile::getGlyphs(GlyphDependencies glyphDependencies) {
|
||||
if (fileSource) {
|
||||
glyphManager.getGlyphs(*this, std::move(glyphDependencies), *fileSource);
|
||||
glyphManager->getGlyphs(*this, std::move(glyphDependencies), *fileSource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +308,7 @@ void GeometryTile::onImagesAvailable(ImageMap images,
|
||||
}
|
||||
|
||||
void GeometryTile::getImages(ImageRequestPair pair) {
|
||||
imageManager.getImages(*this, std::move(pair));
|
||||
imageManager->getImages(*this, std::move(pair));
|
||||
}
|
||||
|
||||
std::shared_ptr<FeatureIndex> GeometryTile::getFeatureIndex() const {
|
||||
|
||||
@@ -106,12 +106,14 @@ private:
|
||||
// Used to signal the worker that it should abandon parsing this tile as soon as possible.
|
||||
std::atomic<bool> obsolete{false};
|
||||
|
||||
std::shared_ptr<Mailbox> mailbox;
|
||||
const std::shared_ptr<Scheduler> threadPool;
|
||||
|
||||
const std::shared_ptr<Mailbox> mailbox;
|
||||
Actor<GeometryTileWorker> worker;
|
||||
|
||||
std::shared_ptr<FileSource> fileSource;
|
||||
GlyphManager& glyphManager;
|
||||
ImageManager& imageManager;
|
||||
const std::shared_ptr<FileSource> fileSource;
|
||||
const std::shared_ptr<GlyphManager> glyphManager;
|
||||
const std::shared_ptr<ImageManager> imageManager;
|
||||
|
||||
uint64_t correlationID = 0;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/exception.hpp>
|
||||
#include <mbgl/util/stopwatch.hpp>
|
||||
#include <mbgl/util/thread_pool.hpp>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
@@ -44,7 +45,9 @@ GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_,
|
||||
pixelRatio(pixelRatio_),
|
||||
showCollisionBoxes(showCollisionBoxes_) {}
|
||||
|
||||
GeometryTileWorker::~GeometryTileWorker() = default;
|
||||
GeometryTileWorker::~GeometryTileWorker() {
|
||||
Scheduler::GetBackground()->runOnRenderThread([renderData_{std::move(renderData)}]() {});
|
||||
}
|
||||
|
||||
/*
|
||||
GeometryTileWorker is a state machine. This is its transition diagram.
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
// render data with the given properties.
|
||||
//
|
||||
// Returns `true` if the corresponding render layer data is present in this
|
||||
// tile (and i.e. it was succesfully updated); returns `false` otherwise.
|
||||
// tile (and i.e. it was successfully updated); returns `false` otherwise.
|
||||
virtual bool layerPropertiesUpdated(const Immutable<style::LayerProperties>& layerProperties) = 0;
|
||||
virtual void setShowCollisionBoxes(const bool) {}
|
||||
virtual void setLayers(const std::vector<Immutable<style::LayerProperties>>&) {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <mbgl/tile/tile_cache.hpp>
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -7,23 +8,66 @@ void TileCache::setSize(size_t size_) {
|
||||
size = size_;
|
||||
|
||||
while (orderedKeys.size() > size) {
|
||||
auto key = orderedKeys.front();
|
||||
const auto key = orderedKeys.front();
|
||||
orderedKeys.remove(key);
|
||||
tiles.erase(key);
|
||||
|
||||
auto hit = tiles.find(key);
|
||||
if (hit != tiles.end()) {
|
||||
auto tile = std::move(hit->second);
|
||||
tiles.erase(hit);
|
||||
deferredRelease(std::move(tile));
|
||||
}
|
||||
}
|
||||
|
||||
assert(orderedKeys.size() <= size);
|
||||
}
|
||||
|
||||
void TileCache::add(const OverscaledTileID& key, std::unique_ptr<Tile> tile) {
|
||||
namespace {
|
||||
|
||||
/// This exists solely to prevent a problem where temporary lambda captures
|
||||
/// are retained for the duration of the scope instead of being destroyed immediately.
|
||||
template <typename T>
|
||||
struct CaptureWrapper {
|
||||
CaptureWrapper(std::unique_ptr<T>&& item_)
|
||||
: item(std::move(item_)) {}
|
||||
CaptureWrapper(const CaptureWrapper& other)
|
||||
: item(other.item) {}
|
||||
std::shared_ptr<T> item;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void TileCache::deferredRelease(std::unique_ptr<Tile>&& tile) {
|
||||
tile->cancel();
|
||||
|
||||
// The `std::function` must be created in a separate statement from the `schedule` call.
|
||||
// Creating a `std::function` from a lambda involves a copy, which is why we must use
|
||||
// `shared_ptr` rather than `unique_ptr` for the capture. As a result, a temporary holds
|
||||
// a reference until the construction is complete and the lambda is destroyed.
|
||||
// If this temporary outlives the `schedule` call, and the function is executed immediately
|
||||
// by a waiting thread and is already complete, that temporary reference ends up being the
|
||||
// last one and the destruction actually occurs here on this thread.
|
||||
std::function<void()> func{[tile_{CaptureWrapper<Tile>{std::move(tile)}}]() {
|
||||
}};
|
||||
|
||||
threadPool->schedule(std::move(func));
|
||||
}
|
||||
|
||||
void TileCache::add(const OverscaledTileID& key, std::unique_ptr<Tile>&& tile) {
|
||||
if (!tile->isRenderable() || !size) {
|
||||
deferredRelease(std::move(tile));
|
||||
return;
|
||||
}
|
||||
|
||||
// insert new or query existing tile
|
||||
if (!tiles.emplace(key, std::move(tile)).second) {
|
||||
// remove existing tile key
|
||||
const auto result = tiles.insert(std::make_pair(key, std::unique_ptr<Tile>{}));
|
||||
if (result.second) {
|
||||
// inserted
|
||||
result.first->second = std::move(tile);
|
||||
} else {
|
||||
// already present
|
||||
// remove existing tile key to move it to the end
|
||||
orderedKeys.remove(key);
|
||||
// release the newly-provided item
|
||||
deferredRelease(std::move(tile));
|
||||
}
|
||||
|
||||
// (re-)insert tile key as newest
|
||||
@@ -31,7 +75,7 @@ void TileCache::add(const OverscaledTileID& key, std::unique_ptr<Tile> tile) {
|
||||
|
||||
// purge oldest key/tile if necessary
|
||||
if (orderedKeys.size() > size) {
|
||||
pop(orderedKeys.front());
|
||||
deferredRelease(pop(orderedKeys.front()));
|
||||
}
|
||||
|
||||
assert(orderedKeys.size() <= size);
|
||||
@@ -49,10 +93,9 @@ Tile* TileCache::get(const OverscaledTileID& key) {
|
||||
std::unique_ptr<Tile> TileCache::pop(const OverscaledTileID& key) {
|
||||
std::unique_ptr<Tile> tile;
|
||||
|
||||
auto it = tiles.find(key);
|
||||
const auto it = tiles.find(key);
|
||||
if (it != tiles.end()) {
|
||||
tile = std::move(it->second);
|
||||
tiles.erase(it);
|
||||
tile = std::move(tiles.extract(it).mapped());
|
||||
orderedKeys.remove(key);
|
||||
assert(tile->isRenderable());
|
||||
}
|
||||
@@ -65,6 +108,9 @@ bool TileCache::has(const OverscaledTileID& key) {
|
||||
}
|
||||
|
||||
void TileCache::clear() {
|
||||
for (auto& item : tiles) {
|
||||
deferredRelease(std::move(item.second));
|
||||
}
|
||||
orderedKeys.clear();
|
||||
tiles.clear();
|
||||
}
|
||||
|
||||
@@ -9,22 +9,36 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Scheduler;
|
||||
|
||||
class TileCache {
|
||||
public:
|
||||
TileCache(size_t size_ = 0)
|
||||
: size(size_) {}
|
||||
TileCache(std::shared_ptr<Scheduler> threadPool_, size_t size_ = 0)
|
||||
: threadPool(std::move(threadPool_)),
|
||||
size(size_) {}
|
||||
|
||||
/// Change the maximum size of the cache.
|
||||
void setSize(size_t);
|
||||
size_t getSize() const { return size; };
|
||||
void add(const OverscaledTileID& key, std::unique_ptr<Tile> tile);
|
||||
|
||||
/// Get the maximum size
|
||||
size_t getMaxSize() const { return size; }
|
||||
|
||||
/// Add a new tile with the given ID.
|
||||
/// If a tile with the same ID is already present, it will be retained and the new one will be discarded.
|
||||
void add(const OverscaledTileID& key, std::unique_ptr<Tile>&& tile);
|
||||
|
||||
std::unique_ptr<Tile> pop(const OverscaledTileID& key);
|
||||
Tile* get(const OverscaledTileID& key);
|
||||
bool has(const OverscaledTileID& key);
|
||||
void clear();
|
||||
|
||||
/// Destroy a tile without blocking
|
||||
void deferredRelease(std::unique_ptr<Tile>&&);
|
||||
|
||||
private:
|
||||
std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles;
|
||||
std::list<OverscaledTileID> orderedKeys;
|
||||
std::shared_ptr<Scheduler> threadPool;
|
||||
|
||||
size_t size;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
#include <mbgl/storage/resource.hpp>
|
||||
#include <mbgl/tile/tile.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class FileSource;
|
||||
@@ -48,6 +52,19 @@ private:
|
||||
std::shared_ptr<FileSource> fileSource;
|
||||
std::unique_ptr<AsyncRequest> request;
|
||||
TileUpdateParameters updateParameters{Duration::zero(), false};
|
||||
|
||||
/// @brief It's possible for async requests in flight to mess with the request
|
||||
/// object at the same time as the loader's destructor. This construct is shared
|
||||
/// with the request lambdas to ensure more tightly controlled synchronization
|
||||
/// to prevent this from happening.
|
||||
struct Shared {
|
||||
std::shared_mutex requestLock;
|
||||
std::atomic_bool aborted{false};
|
||||
};
|
||||
|
||||
// Allocated as a share_ptr so either the loader or request can outlive the
|
||||
// other and still see this.
|
||||
std::shared_ptr<Shared> shared;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -30,6 +30,9 @@ TileLoader<T>::TileLoader(T& tile_,
|
||||
Resource::LoadingMethod::CacheOnly)),
|
||||
fileSource(parameters.fileSource) {
|
||||
assert(!request);
|
||||
|
||||
shared = std::make_shared<Shared>();
|
||||
|
||||
if (!fileSource) {
|
||||
tile.setError(getCantLoadTileError());
|
||||
return;
|
||||
@@ -55,7 +58,12 @@ TileLoader<T>::TileLoader(T& tile_,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TileLoader<T>::~TileLoader() = default;
|
||||
TileLoader<T>::~TileLoader() {
|
||||
std::unique_lock<std::shared_mutex> lock(shared->requestLock);
|
||||
shared->aborted = true;
|
||||
tile.cancel();
|
||||
request.reset();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void TileLoader<T>::setNecessity(TileNecessity newNecessity) {
|
||||
@@ -90,29 +98,36 @@ void TileLoader<T>::loadFromCache() {
|
||||
}
|
||||
|
||||
resource.loadingMethod = Resource::LoadingMethod::CacheOnly;
|
||||
request = fileSource->request(resource, [this](const Response& res) {
|
||||
request.reset();
|
||||
request = fileSource->request(resource, [this, shared_{shared}](const Response& res) {
|
||||
do {
|
||||
if (shared_->requestLock.try_lock_shared()) {
|
||||
std::shared_lock<std::shared_mutex> lock(shared_->requestLock, std::adopt_lock);
|
||||
if (shared_->aborted) return;
|
||||
|
||||
tile.setTriedCache();
|
||||
request.reset();
|
||||
tile.setTriedCache();
|
||||
|
||||
if (res.error && res.error->reason == Response::Error::Reason::NotFound) {
|
||||
// When the cache-only request could not be satisfied, don't treat
|
||||
// it as an error. A cache lookup could still return data, _and_ an
|
||||
// error, in particular when we were able to find the data, but it
|
||||
// is expired and the Cache-Control headers indicated that we aren't
|
||||
// allowed to use expired responses. In this case, we still get the
|
||||
// data which we can use in our conditional network request.
|
||||
resource.priorModified = res.modified;
|
||||
resource.priorExpires = res.expires;
|
||||
resource.priorEtag = res.etag;
|
||||
resource.priorData = res.data;
|
||||
} else {
|
||||
loadedData(res);
|
||||
}
|
||||
if (res.error && res.error->reason == Response::Error::Reason::NotFound) {
|
||||
// When the cache-only request could not be satisfied, don't treat
|
||||
// it as an error. A cache lookup could still return data, _and_ an
|
||||
// error, in particular when we were able to find the data, but it
|
||||
// is expired and the Cache-Control headers indicated that we aren't
|
||||
// allowed to use expired responses. In this case, we still get the
|
||||
// data which we can use in our conditional network request.
|
||||
resource.priorModified = res.modified;
|
||||
resource.priorExpires = res.expires;
|
||||
resource.priorEtag = res.etag;
|
||||
resource.priorData = res.data;
|
||||
} else {
|
||||
loadedData(res);
|
||||
}
|
||||
|
||||
if (necessity == TileNecessity::Required) {
|
||||
loadFromNetwork();
|
||||
}
|
||||
if (necessity == TileNecessity::Required) {
|
||||
loadFromNetwork();
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (!shared_->aborted);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,7 +179,19 @@ void TileLoader<T>::loadFromNetwork() {
|
||||
resource.minimumUpdateInterval = updateParameters.minimumUpdateInterval;
|
||||
resource.storagePolicy = updateParameters.isVolatile ? Resource::StoragePolicy::Volatile
|
||||
: Resource::StoragePolicy::Permanent;
|
||||
request = fileSource->request(resource, [this](const Response& res) { loadedData(res); });
|
||||
|
||||
request = fileSource->request(resource, [this, shared_{shared}](const Response& res) {
|
||||
do {
|
||||
if (shared_->requestLock.try_lock_shared()) {
|
||||
std::shared_lock<std::shared_mutex> lock(shared_->requestLock, std::adopt_lock);
|
||||
if (shared_->aborted) return;
|
||||
|
||||
request.reset();
|
||||
loadedData(res);
|
||||
break;
|
||||
}
|
||||
} while (!shared_->aborted);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -11,7 +11,7 @@ protected:
|
||||
ThreadLocalBase();
|
||||
~ThreadLocalBase();
|
||||
|
||||
void* get();
|
||||
void* get() const;
|
||||
void set(void*);
|
||||
|
||||
private:
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
|
||||
ThreadLocal(T* val) { set(val); }
|
||||
|
||||
T* get() { return reinterpret_cast<T*>(impl::ThreadLocalBase::get()); }
|
||||
T* get() const { return reinterpret_cast<T*>(impl::ThreadLocalBase::get()); }
|
||||
|
||||
void set(T* ptr) { impl::ThreadLocalBase::set(ptr); }
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <mbgl/platform/settings.hpp>
|
||||
#include <mbgl/platform/thread.hpp>
|
||||
#include <mbgl/util/monotonic_timer.hpp>
|
||||
#include <mbgl/util/platform.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
|
||||
@@ -10,11 +11,16 @@ namespace mbgl {
|
||||
ThreadedSchedulerBase::~ThreadedSchedulerBase() = default;
|
||||
|
||||
void ThreadedSchedulerBase::terminate() {
|
||||
// Run any leftover render jobs
|
||||
runRenderJobs();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
terminated = true;
|
||||
}
|
||||
cv.notify_all();
|
||||
|
||||
// Wake up all threads so that they shut down
|
||||
cvAvailable.notify_all();
|
||||
}
|
||||
|
||||
std::thread ThreadedSchedulerBase::makeSchedulerThread(size_t index) {
|
||||
@@ -25,13 +31,18 @@ std::thread ThreadedSchedulerBase::makeSchedulerThread(size_t index) {
|
||||
platform::setCurrentThreadPriority(*priority);
|
||||
}
|
||||
|
||||
platform::setCurrentThreadName(std::string{"Worker "} + util::toString(index + 1));
|
||||
platform::setCurrentThreadName("Worker " + util::toString(index + 1));
|
||||
platform::attachThread();
|
||||
|
||||
owningThreadPool.set(this);
|
||||
|
||||
while (true) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (queue.empty() && !pendingItems) {
|
||||
cvEmpty.notify_all();
|
||||
}
|
||||
|
||||
cv.wait(lock, [this] { return !queue.empty() || terminated; });
|
||||
cvAvailable.wait(lock, [this] { return !queue.empty() || terminated; });
|
||||
|
||||
if (terminated) {
|
||||
platform::detachThread();
|
||||
@@ -40,20 +51,83 @@ std::thread ThreadedSchedulerBase::makeSchedulerThread(size_t index) {
|
||||
|
||||
auto function = std::move(queue.front());
|
||||
queue.pop();
|
||||
|
||||
if (function) {
|
||||
pendingItems++;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
if (function) function();
|
||||
|
||||
if (function) {
|
||||
const auto cleanup = [&] {
|
||||
// destroy the function and release its captures before unblocking `waitForEmpty`
|
||||
function = {};
|
||||
pendingItems--;
|
||||
if (queue.empty() && !pendingItems) {
|
||||
cvEmpty.notify_all();
|
||||
}
|
||||
};
|
||||
try {
|
||||
function();
|
||||
cleanup();
|
||||
} catch (...) {
|
||||
lock.lock();
|
||||
if (handler) {
|
||||
handler(std::current_exception());
|
||||
}
|
||||
cleanup();
|
||||
if (handler) {
|
||||
continue;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ThreadedSchedulerBase::schedule(std::function<void()> fn) {
|
||||
void ThreadedSchedulerBase::schedule(std::function<void()>&& fn) {
|
||||
assert(fn);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
queue.push(std::move(fn));
|
||||
if (fn) {
|
||||
{
|
||||
// We need to block if adding adding a new task from a thread not controlled by this
|
||||
// pool. Tasks are added by other tasks, so we must not block a thread we do control
|
||||
// or `waitForEmpty` will deadlock.
|
||||
std::unique_lock<std::mutex> addLock(addMutex, std::defer_lock);
|
||||
if (!thisThreadIsOwned()) {
|
||||
addLock.lock();
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
queue.push(std::move(fn));
|
||||
}
|
||||
cvAvailable.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
cv.notify_one();
|
||||
std::size_t ThreadedSchedulerBase::waitForEmpty(Milliseconds timeout) {
|
||||
// Must not be called from a thread in our pool, or we would deadlock
|
||||
assert(!thisThreadIsOwned());
|
||||
if (!thisThreadIsOwned()) {
|
||||
const auto startTime = util::MonotonicTimer::now();
|
||||
const auto isDone = [&] {
|
||||
return queue.empty() && pendingItems == 0;
|
||||
};
|
||||
// Block any other threads from adding new items
|
||||
std::scoped_lock<std::mutex> addLock(addMutex);
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
while (!isDone()) {
|
||||
if (timeout > Milliseconds::zero()) {
|
||||
const auto elapsed = util::MonotonicTimer::now() - startTime;
|
||||
if (timeout <= elapsed || !cvEmpty.wait_for(lock, timeout - elapsed, isDone)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cvEmpty.wait(lock, isDone);
|
||||
}
|
||||
}
|
||||
return queue.size() + pendingItems;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -2,18 +2,20 @@
|
||||
|
||||
#include <mbgl/actor/mailbox.hpp>
|
||||
#include <mbgl/actor/scheduler.hpp>
|
||||
#include <mbgl/util/thread_local.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class ThreadedSchedulerBase : public Scheduler {
|
||||
public:
|
||||
void schedule(std::function<void()>) override;
|
||||
void schedule(std::function<void()>&&) override;
|
||||
|
||||
protected:
|
||||
ThreadedSchedulerBase() = default;
|
||||
@@ -22,9 +24,27 @@ protected:
|
||||
void terminate();
|
||||
std::thread makeSchedulerThread(size_t index);
|
||||
|
||||
/// Wait until there's nothing pending or in process
|
||||
/// Must not be called from a task provided to this scheduler.
|
||||
/// @param timeout Time to wait, or zero to wait forever.
|
||||
std::size_t waitForEmpty(Milliseconds timeout) override;
|
||||
|
||||
/// Returns true if called from a thread managed by the scheduler
|
||||
bool thisThreadIsOwned() const { return owningThreadPool.get() == this; }
|
||||
|
||||
std::queue<std::function<void()>> queue;
|
||||
// protects `queue`
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
// Used to block addition of new items while waiting
|
||||
std::mutex addMutex;
|
||||
// Signal when an item is added to the queue
|
||||
std::condition_variable cvAvailable;
|
||||
// Signal when the queue becomes empty
|
||||
std::condition_variable cvEmpty;
|
||||
// Count of functions removed from the queue but still executing
|
||||
std::atomic<std::size_t> pendingItems{0};
|
||||
// Points to the owning pool in owned threads
|
||||
util::ThreadLocal<ThreadedSchedulerBase> owningThreadPool;
|
||||
bool terminated{false};
|
||||
};
|
||||
|
||||
@@ -36,16 +56,17 @@ protected:
|
||||
* Note: If N == 1 all scheduled tasks are guaranteed to execute consequently;
|
||||
* otherwise, some of the scheduled tasks might be executed in parallel.
|
||||
*/
|
||||
template <std::size_t N>
|
||||
class ThreadedScheduler : public ThreadedSchedulerBase {
|
||||
public:
|
||||
ThreadedScheduler() {
|
||||
for (std::size_t i = 0u; i < N; ++i) {
|
||||
ThreadedScheduler(std::size_t n)
|
||||
: threads(n) {
|
||||
for (std::size_t i = 0u; i < threads.size(); ++i) {
|
||||
threads[i] = makeSchedulerThread(i);
|
||||
}
|
||||
}
|
||||
|
||||
~ThreadedScheduler() override {
|
||||
assert(!thisThreadIsOwned());
|
||||
terminate();
|
||||
for (auto& thread : threads) {
|
||||
assert(std::this_thread::get_id() != thread.get_id());
|
||||
@@ -53,19 +74,48 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void runOnRenderThread(std::function<void()>&& fn) override {
|
||||
std::lock_guard<std::mutex> lock(renderMutex);
|
||||
renderThreadQueue.push(std::move(fn));
|
||||
}
|
||||
|
||||
void runRenderJobs() override {
|
||||
std::lock_guard<std::mutex> lock(renderMutex);
|
||||
while (renderThreadQueue.size()) {
|
||||
auto fn = std::move(renderThreadQueue.front());
|
||||
renderThreadQueue.pop();
|
||||
if (fn) {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mapbox::base::WeakPtr<Scheduler> makeWeakPtr() override { return weakFactory.makeWeakPtr(); }
|
||||
|
||||
private:
|
||||
std::array<std::thread, N> threads;
|
||||
std::vector<std::thread> threads;
|
||||
mapbox::base::WeakPtrFactory<Scheduler> weakFactory{this};
|
||||
static_assert(N > 0, "Thread count must be more than zero.");
|
||||
|
||||
std::queue<std::function<void()>> renderThreadQueue;
|
||||
std::mutex renderMutex;
|
||||
};
|
||||
|
||||
class SequencedScheduler : public ThreadedScheduler<1> {};
|
||||
class SequencedScheduler : public ThreadedScheduler {
|
||||
public:
|
||||
SequencedScheduler()
|
||||
: ThreadedScheduler(1) {}
|
||||
};
|
||||
|
||||
template <std::size_t extra>
|
||||
using ParallelScheduler = ThreadedScheduler<1 + extra>;
|
||||
class ParallelScheduler : public ThreadedScheduler {
|
||||
public:
|
||||
ParallelScheduler(std::size_t extra)
|
||||
: ThreadedScheduler(1 + extra) {}
|
||||
};
|
||||
|
||||
class ThreadPool : public ParallelScheduler<3> {};
|
||||
class ThreadPool : public ParallelScheduler {
|
||||
public:
|
||||
ThreadPool()
|
||||
: ParallelScheduler(3) {}
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
Reference in New Issue
Block a user