OSMAND_CORE_API void OSMAND_CORE_CALL OsmAnd::ReleaseCore() { if (_qCoreApplicationThread) { QCoreApplication::exit(); REPEAT_UNTIL(_qCoreApplicationThread->wait()); _qCoreApplicationThread.reset(); } else { releaseInAppThread(); } // MapSymbol intersection classes registry MapSymbolIntersectionClassesRegistry_releaseGlobalInstance(); // Text rasterizer TextRasterizer_releaseGlobalInstance(); // ICU ICU::release(); // SKIA SKIA::release(); Logger::get()->flush(); Logger::get()->removeAllLogSinks(); }
OSMAND_CORE_API bool OSMAND_CORE_CALL OsmAnd::InitializeCore(const std::shared_ptr<const ICoreResourcesProvider>& coreResourcesProvider) { if (!coreResourcesProvider) { std::cerr << "OsmAnd core requires non-null core resources provider!" << std::endl; return false; } gCoreResourcesProvider = coreResourcesProvider; Logger::get()->addLogSink(std::shared_ptr<ILogSink>(new DefaultLogSink())); InflateExplicitReferences(); if (!QCoreApplication::instance()) { LogPrintf(LogSeverityLevel::Info, "OsmAnd Core is initialized standalone, so going to create 'application' thread"); _qCoreApplicationThread.reset(new QCoreApplicationThread()); gMainThread = _qCoreApplicationThread.get(); _qCoreApplicationThread->start(); // Wait until global initialization will pass in that thread { QMutexLocker scopeLock(&_qCoreApplicationThreadMutex); while (!_qCoreApplicationThread->wasInitialized) REPEAT_UNTIL(_qCoreApplicationThreadWaitCondition.wait(&_qCoreApplicationThreadMutex)); } } else { LogPrintf(LogSeverityLevel::Info, "OsmAnd Core is initialized inside a Qt application, so assuming that got called from application thread"); gMainThread = QThread::currentThread(); initializeInAppThread(); } // GDAL GDALAllRegister(); // SKIA if (!SKIA::initialize()) return false; // ICU if (!ICU::initialize()) return false; // Qt (void)QLocale::system(); // This will initialize system locale, since it fails to initialize concurrently // Text rasterizer TextRasterizer_initializeGlobalInstance(); // MapSymbol intersection classes registry MapSymbolIntersectionClassesRegistry_initializeGlobalInstance(); return true; }
void OsmAnd::Concurrent::Dispatcher::shutdown() { QMutexLocker scopedLocker(&_performedShutdownConditionMutex); shutdownAsync(); REPEAT_UNTIL(_performedShutdown.wait(&_performedShutdownConditionMutex)); }
void OsmAnd::ObfFile_P::lockForWriting() const { QMutexLocker scopedLocker(&_lockCounterMutex); while(_lockCounter > 0) REPEAT_UNTIL(_lockCounterWaitCondition.wait(&_lockCounterMutex)); _lockCounter--; _lockCounterWaitCondition.wakeAll(); }
void OsmAnd::Concurrent::TaskHost::onOwnerIsBeingDestructed() { // Mark that owner is being destructed _ownerIsBeingDestructed = true; // Ask all tasks to cancel { QReadLocker scopedLock(&_hostedTasksLock); for(const auto& task : constOf(_hostedTasks)) task->requestCancellation(); } // Hold until all tasks are released { QReadLocker scopedLocker(&_hostedTasksLock); while(_hostedTasks.size() != 0) REPEAT_UNTIL(_unlockedCondition.wait(&_hostedTasksLock)); } }
void OsmAnd::Concurrent::Dispatcher::invoke(const Delegate method) { assert(method != nullptr); QMutex waitMutex; QWaitCondition waitCondition; invokeAsync([&waitCondition, &waitMutex, method] { method(); { QMutexLocker scopedLocker(&waitMutex); waitCondition.wakeAll(); } }); { QMutexLocker scopedLocker(&waitMutex); REPEAT_UNTIL(waitCondition.wait(&waitMutex)); } }
bool OsmAnd::MapPrimitivesProvider_P::obtainData( const TileId tileId, const ZoomLevel zoom, std::shared_ptr<MapPrimitivesProvider::Data>& outTiledData, MapPrimitivesProvider_Metrics::Metric_obtainData* const metric_, const IQueryController* const queryController) { #if OSMAND_PERFORMANCE_METRICS MapPrimitivesProvider_Metrics::Metric_obtainData localMetric; const auto metric = metric_ ? metric_ : &localMetric; #else const auto metric = metric_; #endif const Stopwatch totalStopwatch(metric != nullptr); std::shared_ptr<TileEntry> tileEntry; for (;;) { // Try to obtain previous instance of tile _tileReferences.obtainOrAllocateEntry(tileEntry, tileId, zoom, [] (const TiledEntriesCollection<TileEntry>& collection, const TileId tileId, const ZoomLevel zoom) -> TileEntry* { return new TileEntry(collection, tileId, zoom); }); // If state is "Undefined", change it to "Loading" and proceed with loading if (tileEntry->setStateIf(TileState::Undefined, TileState::Loading)) break; // In case tile entry is being loaded, wait until it will finish loading if (tileEntry->getState() == TileState::Loading) { QReadLocker scopedLcoker(&tileEntry->loadedConditionLock); // If tile is in 'Loading' state, wait until it will become 'Loaded' while (tileEntry->getState() != TileState::Loaded) REPEAT_UNTIL(tileEntry->loadedCondition.wait(&tileEntry->loadedConditionLock)); } if (!tileEntry->dataIsPresent) { // If there was no data, return same outTiledData.reset(); return true; } else { // Otherwise, try to lock tile reference outTiledData = tileEntry->dataWeakRef.lock(); // If successfully locked, just return it if (outTiledData) return true; // Otherwise consider this tile entry as expired, remove it from collection (it's safe to do that right now) // This will enable creation of new entry on next loop cycle _tileReferences.removeEntry(tileId, zoom); tileEntry.reset(); } } const Stopwatch totalTimeStopwatch( #if OSMAND_PERFORMANCE_METRICS true #else metric != nullptr #endif // OSMAND_PERFORMANCE_METRICS ); // Obtain map objects data tile std::shared_ptr<IMapObjectsProvider::Data> dataTile; std::shared_ptr<Metric> submetric; owner->mapObjectsProvider->obtainData( tileId, zoom, dataTile, metric ? &submetric : nullptr, nullptr); if (metric && submetric) metric->addOrReplaceSubmetric(submetric); if (!dataTile) { // Store flag that there was no data and mark tile entry as 'Loaded' tileEntry->dataIsPresent = false; tileEntry->setState(TileState::Loaded); // Notify that tile has been loaded { QWriteLocker scopedLcoker(&tileEntry->loadedConditionLock); tileEntry->loadedCondition.wakeAll(); } outTiledData.reset(); return true; } // Get primitivised objects std::shared_ptr<MapPrimitiviser::PrimitivisedObjects> primitivisedObjects; if (owner->mode == MapPrimitivesProvider::Mode::AllObjectsWithoutPolygonFiltering) { primitivisedObjects = owner->primitiviser->primitiviseAllMapObjects( zoom, dataTile->mapObjects, //NOTE: So far it's safe to turn off this cache. But it has to be rewritten. Since lock/unlock occurs too often, this kills entire performance //NOTE: Maybe a QuadTree-based cache with leaf-only locking will save up much. Or use supernodes, like DataBlock nullptr, //_primitiviserCache, nullptr, metric ? metric->findOrAddSubmetricOfType<MapPrimitiviser_Metrics::Metric_primitiviseAllMapObjects>().get() : nullptr); } else if (owner->mode == MapPrimitivesProvider::Mode::AllObjectsWithPolygonFiltering) { primitivisedObjects = owner->primitiviser->primitiviseAllMapObjects( Utilities::getScaleDivisor31ToPixel(PointI(owner->tileSize, owner->tileSize), zoom), zoom, dataTile->mapObjects, //NOTE: So far it's safe to turn off this cache. But it has to be rewritten. Since lock/unlock occurs too often, this kills entire performance //NOTE: Maybe a QuadTree-based cache with leaf-only locking will save up much. Or use supernodes, like DataBlock nullptr, //_primitiviserCache, nullptr, metric ? metric->findOrAddSubmetricOfType<MapPrimitiviser_Metrics::Metric_primitiviseAllMapObjects>().get() : nullptr); } else if (owner->mode == MapPrimitivesProvider::Mode::WithoutSurface) { primitivisedObjects = owner->primitiviser->primitiviseWithoutSurface( Utilities::getScaleDivisor31ToPixel(PointI(owner->tileSize, owner->tileSize), zoom), zoom, dataTile->mapObjects, //NOTE: So far it's safe to turn off this cache. But it has to be rewritten. Since lock/unlock occurs too often, this kills entire performance //NOTE: Maybe a QuadTree-based cache with leaf-only locking will save up much. Or use supernodes, like DataBlock nullptr, //_primitiviserCache, nullptr, metric ? metric->findOrAddSubmetricOfType<MapPrimitiviser_Metrics::Metric_primitiviseWithoutSurface>().get() : nullptr); } else // if (owner->mode == MapPrimitivesProvider::Mode::WithSurface) { const auto tileBBox31 = Utilities::tileBoundingBox31(tileId, zoom); primitivisedObjects = owner->primitiviser->primitiviseWithSurface( tileBBox31, PointI(owner->tileSize, owner->tileSize), zoom, dataTile->tileSurfaceType, dataTile->mapObjects, //NOTE: So far it's safe to turn off this cache. But it has to be rewritten. Since lock/unlock occurs too often, this kills entire performance //NOTE: Maybe a QuadTree-based cache with leaf-only locking will save up much. Or use supernodes, like DataBlock nullptr, //_primitiviserCache, nullptr, metric ? metric->findOrAddSubmetricOfType<MapPrimitiviser_Metrics::Metric_primitiviseWithSurface>().get() : nullptr); } // Create tile const std::shared_ptr<MapPrimitivesProvider::Data> newTiledData(new MapPrimitivesProvider::Data( tileId, zoom, dataTile, primitivisedObjects, new RetainableCacheMetadata(tileEntry, dataTile->retainableCacheMetadata))); // Publish new tile outTiledData = newTiledData; // Store weak reference to new tile and mark it as 'Loaded' tileEntry->dataIsPresent = true; tileEntry->dataWeakRef = newTiledData; tileEntry->setState(TileState::Loaded); // Notify that tile has been loaded { QWriteLocker scopedLcoker(&tileEntry->loadedConditionLock); tileEntry->loadedCondition.wakeAll(); } if (metric) metric->elapsedTime = totalStopwatch.elapsed(); #if OSMAND_PERFORMANCE_METRICS #if OSMAND_PERFORMANCE_METRICS <= 1 LogPrintf(LogSeverityLevel::Info, "%d polygons, %d polylines, %d points primitivised from %dx%d@%d in %fs", primitivisedObjects->polygons.size(), primitivisedObjects->polylines.size(), primitivisedObjects->polygons.size(), tileId.x, tileId.y, zoom, totalStopwatch.elapsed()); #else LogPrintf(LogSeverityLevel::Info, "%d polygons, %d polylines, %d points primitivised from %dx%d@%d in %fs:\n%s", primitivisedObjects->polygons.size(), primitivisedObjects->polylines.size(), primitivisedObjects->polygons.size(), tileId.x, tileId.y, zoom, totalStopwatch.elapsed(), qPrintable(metric ? metric->toString(QLatin1String("\t - ")) : QLatin1String("(null)"))); #endif // OSMAND_PERFORMANCE_METRICS <= 1 #endif // OSMAND_PERFORMANCE_METRICS return true; }
bool OsmAnd::BinaryMapDataProvider_P::obtainData( const TileId tileId, const ZoomLevel zoom, std::shared_ptr<MapTiledData>& outTiledData, const IQueryController* const queryController) { std::shared_ptr<TileEntry> tileEntry; for (;;) { // Try to obtain previous instance of tile _tileReferences.obtainOrAllocateEntry(tileEntry, tileId, zoom, [] (const TiledEntriesCollection<TileEntry>& collection, const TileId tileId, const ZoomLevel zoom) -> TileEntry* { return new TileEntry(collection, tileId, zoom); }); // If state is "Undefined", change it to "Loading" and proceed with loading if (tileEntry->setStateIf(TileState::Undefined, TileState::Loading)) break; // In case tile entry is being loaded, wait until it will finish loading if (tileEntry->getState() == TileState::Loading) { QReadLocker scopedLcoker(&tileEntry->_loadedConditionLock); // If tile is in 'Loading' state, wait until it will become 'Loaded' while (tileEntry->getState() != TileState::Loaded) REPEAT_UNTIL(tileEntry->_loadedCondition.wait(&tileEntry->_loadedConditionLock)); } // Try to lock tile reference outTiledData = tileEntry->_tile.lock(); // If successfully locked, just return it if (outTiledData) return true; // Otherwise consider this tile entry as expired, remove it from collection (it's safe to do that right now) // This will enable creation of new entry on next loop cycle _tileReferences.removeEntry(tileId, zoom); tileEntry.reset(); } #if OSMAND_PERFORMANCE_METRICS const auto total_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS // Obtain OBF data interface #if OSMAND_PERFORMANCE_METRICS const auto obtainDataInterface_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS const auto& dataInterface = owner->obfsCollection->obtainDataInterface(); #if OSMAND_PERFORMANCE_METRICS const auto obtainDataInterface_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> obtainDataInterface_Elapsed = obtainDataInterface_End - obtainDataInterface_Begin; #endif // OSMAND_PERFORMANCE_METRICS // Get bounding box that covers this tile const auto tileBBox31 = Utilities::tileBoundingBox31(tileId, zoom); // Perform read-out QList< std::shared_ptr<const Model::BinaryMapObject> > referencedMapObjects; QList< proper::shared_future< std::shared_ptr<const Model::BinaryMapObject> > > futureReferencedMapObjects; QList< std::shared_ptr<const Model::BinaryMapObject> > loadedMapObjects; QSet< uint64_t > loadedSharedMapObjects; MapFoundationType tileFoundation; #if OSMAND_PERFORMANCE_METRICS float dataFilter = 0.0f; const auto dataRead_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS #if OSMAND_PERFORMANCE_METRICS > 1 ObfMapSectionReader_Metrics::Metric_loadMapObjects dataRead_Metric; #endif // OSMAND_PERFORMANCE_METRICS > 1 dataInterface->loadMapObjects(&loadedMapObjects, &tileFoundation, tileBBox31, zoom, nullptr, [this, zoom, &referencedMapObjects, &futureReferencedMapObjects, &loadedSharedMapObjects, tileBBox31 #if OSMAND_PERFORMANCE_METRICS , &dataFilter #endif // OSMAND_PERFORMANCE_METRICS ] (const std::shared_ptr<const ObfMapSectionInfo>& section, const uint64_t id, const AreaI& bbox, const ZoomLevel firstZoomLevel, const ZoomLevel lastZoomLevel) -> bool { #if OSMAND_PERFORMANCE_METRICS const auto dataFilter_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS // This map object may be shared only in case it crosses bounds of a tile const auto canNotBeShared = tileBBox31.contains(bbox); // If map object can not be shared, just read it if (canNotBeShared) { #if OSMAND_PERFORMANCE_METRICS const auto dataFilter_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> dataRead_Elapsed = dataFilter_End - dataFilter_Begin; dataFilter += dataRead_Elapsed.count(); #endif // OSMAND_PERFORMANCE_METRICS return true; } // Otherwise, this map object can be shared, so it should be checked for // being present in shared mapObjects storage, or be reserved there std::shared_ptr<const Model::BinaryMapObject> sharedMapObjectReference; proper::shared_future< std::shared_ptr<const Model::BinaryMapObject> > futureSharedMapObjectReference; if (_sharedMapObjects.obtainReferenceOrFutureReferenceOrMakePromise(id, zoom, Utilities::enumerateZoomLevels(firstZoomLevel, lastZoomLevel), sharedMapObjectReference, futureSharedMapObjectReference)) { if (sharedMapObjectReference) { // If map object is already in shared objects cache and is available, use that one referencedMapObjects.push_back(qMove(sharedMapObjectReference)); } else { futureReferencedMapObjects.push_back(qMove(futureSharedMapObjectReference)); } #if OSMAND_PERFORMANCE_METRICS const auto dataFilter_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> dataRead_Elapsed = dataFilter_End - dataFilter_Begin; dataFilter += dataRead_Elapsed.count(); #endif // OSMAND_PERFORMANCE_METRICS return false; } // This map object was reserved, and is going to be shared, but needs to be loaded loadedSharedMapObjects.insert(id); return true; }, #if OSMAND_PERFORMANCE_METRICS > 1 & dataRead_Metric #else nullptr #endif // OSMAND_PERFORMANCE_METRICS > 1 ); #if OSMAND_PERFORMANCE_METRICS const auto dataRead_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> dataRead_Elapsed = dataRead_End - dataRead_Begin; const auto dataIdsProcess_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS // Process loaded-and-shared map objects for (auto& mapObject : loadedMapObjects) { // Check if this map object is shared if (!loadedSharedMapObjects.contains(mapObject->id)) continue; // Add unique map object under lock to all zoom levels, for which this map object is valid assert(mapObject->level); _sharedMapObjects.fulfilPromiseAndReference( mapObject->id, Utilities::enumerateZoomLevels(mapObject->level->minZoom, mapObject->level->maxZoom), mapObject); } for (auto& futureMapObject : futureReferencedMapObjects) { auto mapObject = futureMapObject.get(); referencedMapObjects.push_back(qMove(mapObject)); } #if OSMAND_PERFORMANCE_METRICS const auto dataIdsProcess_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> dataIdsProcess_Elapsed = dataIdsProcess_End - dataIdsProcess_Begin; const auto dataProcess_Begin = std::chrono::high_resolution_clock::now(); #endif // OSMAND_PERFORMANCE_METRICS #if OSMAND_PERFORMANCE_METRICS > 1 Rasterizer_Metrics::Metric_prepareContext dataProcess_metric; #endif // OSMAND_PERFORMANCE_METRICS > 1 // Prepare data for the tile const auto sharedMapObjectsCount = referencedMapObjects.size() + loadedSharedMapObjects.size(); const auto allMapObjects = loadedMapObjects + referencedMapObjects; // Allocate and prepare rasterizer context bool nothingToRasterize = false; std::shared_ptr<RasterizerContext> rasterizerContext(new RasterizerContext(owner->rasterizerEnvironment, owner->rasterizerSharedContext)); Rasterizer::prepareContext(*rasterizerContext, tileBBox31, zoom, tileFoundation, allMapObjects, ¬hingToRasterize, nullptr, #if OSMAND_PERFORMANCE_METRICS > 1 & dataProcess_metric #else nullptr #endif // OSMAND_PERFORMANCE_METRICS > 1 ); #if OSMAND_PERFORMANCE_METRICS const auto dataProcess_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> dataProcess_Elapsed = dataProcess_End - dataProcess_Begin; #endif // OSMAND_PERFORMANCE_METRICS // Create tile const std::shared_ptr<BinaryMapDataTile> newTile(new BinaryMapDataTile( tileFoundation, allMapObjects, rasterizerContext, nothingToRasterize, tileId, zoom)); newTile->_p->_weakLink = _link->getWeak(); newTile->_p->_refEntry = tileEntry; // Publish new tile outTiledData = newTile; // Store weak reference to new tile and mark it as 'Loaded' tileEntry->_tile = newTile; tileEntry->setState(TileState::Loaded); // Notify that tile has been loaded { QWriteLocker scopedLcoker(&tileEntry->_loadedConditionLock); tileEntry->_loadedCondition.wakeAll(); } #if OSMAND_PERFORMANCE_METRICS const auto total_End = std::chrono::high_resolution_clock::now(); const std::chrono::duration<float> total_Elapsed = total_End - total_Begin; #if OSMAND_PERFORMANCE_METRICS <= 1 LogPrintf(LogSeverityLevel::Info, "%d map objects (%d unique, %d shared) from %dx%d@%d in %fs", allMapObjects.size(), allMapObjects.size() - sharedMapObjectsCount, sharedMapObjectsCount, tileId.x, tileId.y, zoom, total_Elapsed.count()); #else LogPrintf(LogSeverityLevel::Info, "%d map objects (%d unique, %d shared) from %dx%d@%d in %fs:\n" "\topen %fs\n" "\tread %fs (filter-by-id %fs):\n" "\t - visitedLevels = %d\n" "\t - acceptedLevels = %d\n" "\t - visitedNodes = %d\n" "\t - acceptedNodes = %d\n" "\t - elapsedTimeForNodes = %fs\n" "\t - mapObjectsBlocksRead = %d\n" "\t - visitedMapObjects = %d\n" "\t - acceptedMapObjects = %d\n" "\t - elapsedTimeForMapObjectsBlocks = %fs\n" "\t - elapsedTimeForOnlyVisitedMapObjects = %fs\n" "\t - elapsedTimeForOnlyAcceptedMapObjects = %fs\n" "\t - average time per 1K only-visited map objects = %fms\n" "\t - average time per 1K only-accepted map objects = %fms\n" "\tprocess-ids %fs\n" "\tprocess-content %fs:\n" "\t - elapsedTimeForSortingObjects = %fs\n" "\t - elapsedTimeForPolygonizingCoastlines = %fs\n" "\t - polygonizedCoastlines = %d\n" "\t - elapsedTimeForObtainingPrimitives = %fs\n" "\t - elapsedTimeForOrderEvaluation = %fs\n" "\t - orderEvaluations = %d\n" "\t - average time per 1K order evaluations = %fms\n" "\t - elapsedTimeForPolygonEvaluation = %fs\n" "\t - polygonEvaluations = %d\n" "\t - average time per 1K polygon evaluations = %fms\n" "\t - polygonPrimitives = %d\n" "\t - elapsedTimeForPolylineEvaluation = %fs\n" "\t - polylineEvaluations = %d\n" "\t - average time per 1K polyline evaluations = %fms\n" "\t - polylinePrimitives = %d\n" "\t - elapsedTimeForPointEvaluation = %fs\n" "\t - pointEvaluations = %d\n" "\t - average time per 1K point evaluations = %fms\n" "\t - pointPrimitives = %d\n" "\t - elapsedTimeForObtainingPrimitivesSymbols = %fs", allMapObjects.size(), allMapObjects.size() - sharedMapObjectsCount, sharedMapObjectsCount, tileId.x, tileId.y, zoom, total_Elapsed.count(), obtainDataInterface_Elapsed.count(), dataRead_Elapsed.count(), dataFilter, dataRead_Metric.visitedLevels, dataRead_Metric.acceptedLevels, dataRead_Metric.visitedNodes, dataRead_Metric.acceptedNodes, dataRead_Metric.elapsedTimeForNodes, dataRead_Metric.mapObjectsBlocksRead, dataRead_Metric.visitedMapObjects, dataRead_Metric.acceptedMapObjects, dataRead_Metric.elapsedTimeForMapObjectsBlocks, dataRead_Metric.elapsedTimeForOnlyVisitedMapObjects, dataRead_Metric.elapsedTimeForOnlyAcceptedMapObjects, (dataRead_Metric.elapsedTimeForOnlyVisitedMapObjects * 1000.0f) / (static_cast<float>(dataRead_Metric.visitedMapObjects - dataRead_Metric.acceptedMapObjects) / 1000.0f), (dataRead_Metric.elapsedTimeForOnlyAcceptedMapObjects * 1000.0f) / (static_cast<float>(dataRead_Metric.acceptedMapObjects) / 1000.0f), dataIdsProcess_Elapsed.count(), dataProcess_Elapsed.count(), dataProcess_metric.elapsedTimeForSortingObjects, dataProcess_metric.elapsedTimeForPolygonizingCoastlines, dataProcess_metric.polygonizedCoastlines, dataProcess_metric.elapsedTimeForObtainingPrimitives, dataProcess_metric.elapsedTimeForOrderEvaluation, dataProcess_metric.orderEvaluations, (dataProcess_metric.elapsedTimeForOrderEvaluation * 1000.0f / static_cast<float>(dataProcess_metric.orderEvaluations)) * 1000.0f, dataProcess_metric.elapsedTimeForPolygonEvaluation, dataProcess_metric.polygonEvaluations, (dataProcess_metric.elapsedTimeForPolygonEvaluation * 1000.0f / static_cast<float>(dataProcess_metric.polygonEvaluations)) * 1000.0f, dataProcess_metric.polygonPrimitives, dataProcess_metric.elapsedTimeForPolylineEvaluation, dataProcess_metric.polylineEvaluations, (dataProcess_metric.elapsedTimeForPolylineEvaluation * 1000.0f / static_cast<float>(dataProcess_metric.polylineEvaluations)) * 1000.0f, dataProcess_metric.polylinePrimitives, dataProcess_metric.elapsedTimeForPointEvaluation, dataProcess_metric.pointEvaluations, (dataProcess_metric.elapsedTimeForPointEvaluation * 1000.0f / static_cast<float>(dataProcess_metric.pointEvaluations)) * 1000.0f, dataProcess_metric.pointPrimitives, dataProcess_metric.elapsedTimeForObtainingPrimitivesSymbols); #endif // OSMAND_PERFORMANCE_METRICS <= 1 #endif // OSMAND_PERFORMANCE_METRICS return true; }
bool OsmAnd::BinaryMapDataProvider_P::obtainData( const TileId tileId, const ZoomLevel zoom, std::shared_ptr<MapTiledData>& outTiledData, BinaryMapDataProvider_Metrics::Metric_obtainData* const metric_, const IQueryController* const queryController) { #if OSMAND_PERFORMANCE_METRICS BinaryMapDataProvider_Metrics::Metric_obtainData localMetric; const auto metric = metric_ ? metric_ : &localMetric; #else const auto metric = metric_; #endif std::shared_ptr<TileEntry> tileEntry; for (;;) { // Try to obtain previous instance of tile _tileReferences.obtainOrAllocateEntry(tileEntry, tileId, zoom, [] (const TiledEntriesCollection<TileEntry>& collection, const TileId tileId, const ZoomLevel zoom) -> TileEntry* { return new TileEntry(collection, tileId, zoom); }); // If state is "Undefined", change it to "Loading" and proceed with loading if (tileEntry->setStateIf(TileState::Undefined, TileState::Loading)) break; // In case tile entry is being loaded, wait until it will finish loading if (tileEntry->getState() == TileState::Loading) { QReadLocker scopedLcoker(&tileEntry->_loadedConditionLock); // If tile is in 'Loading' state, wait until it will become 'Loaded' while (tileEntry->getState() != TileState::Loaded) REPEAT_UNTIL(tileEntry->_loadedCondition.wait(&tileEntry->_loadedConditionLock)); } // Try to lock tile reference outTiledData = tileEntry->_tile.lock(); // If successfully locked, just return it if (outTiledData) return true; // Otherwise consider this tile entry as expired, remove it from collection (it's safe to do that right now) // This will enable creation of new entry on next loop cycle _tileReferences.removeEntry(tileId, zoom); tileEntry.reset(); } const Stopwatch totalTimeStopwatch( #if OSMAND_PERFORMANCE_METRICS true #else metric != nullptr #endif // OSMAND_PERFORMANCE_METRICS ); // Obtain OBF data interface const Stopwatch obtainObfInterfaceStopwatch(metric != nullptr); const auto& dataInterface = owner->obfsCollection->obtainDataInterface(); if (metric) metric->elapsedTimeForObtainingObfInterface += obtainObfInterfaceStopwatch.elapsed(); // Get bounding box that covers this tile const auto tileBBox31 = Utilities::tileBoundingBox31(tileId, zoom); // Perform read-out const Stopwatch totalReadTimeStopwatch(metric != nullptr); QList< std::shared_ptr< const ObfMapSectionReader::DataBlock > > referencedMapDataBlocks; QList< std::shared_ptr<const Model::BinaryMapObject> > referencedMapObjects; QList< proper::shared_future< std::shared_ptr<const Model::BinaryMapObject> > > futureReferencedMapObjects; QList< std::shared_ptr<const Model::BinaryMapObject> > loadedMapObjects; QSet< uint64_t > loadedSharedMapObjects; auto tileFoundation = MapFoundationType::Undefined; dataInterface->loadMapObjects( &loadedMapObjects, &tileFoundation, zoom, &tileBBox31, [this, zoom, &referencedMapObjects, &futureReferencedMapObjects, &loadedSharedMapObjects, tileBBox31, metric] (const std::shared_ptr<const ObfMapSectionInfo>& section, const uint64_t id, const AreaI& bbox, const ZoomLevel firstZoomLevel, const ZoomLevel lastZoomLevel) -> bool { const Stopwatch objectsFilteringStopwatch(metric != nullptr); // This map object may be shared only in case it crosses bounds of a tile const auto canNotBeShared = tileBBox31.contains(bbox); // If map object can not be shared, just read it if (canNotBeShared) { if (metric) metric->elapsedTimeForObjectsFiltering += objectsFilteringStopwatch.elapsed(); return true; } // Otherwise, this map object can be shared, so it should be checked for // being present in shared mapObjects storage, or be reserved there std::shared_ptr<const Model::BinaryMapObject> sharedMapObjectReference; proper::shared_future< std::shared_ptr<const Model::BinaryMapObject> > futureSharedMapObjectReference; if (_sharedMapObjects.obtainReferenceOrFutureReferenceOrMakePromise(id, zoom, Utilities::enumerateZoomLevels(firstZoomLevel, lastZoomLevel), sharedMapObjectReference, futureSharedMapObjectReference)) { if (sharedMapObjectReference) { // If map object is already in shared objects cache and is available, use that one referencedMapObjects.push_back(qMove(sharedMapObjectReference)); } else { futureReferencedMapObjects.push_back(qMove(futureSharedMapObjectReference)); } if (metric) metric->elapsedTimeForObjectsFiltering += objectsFilteringStopwatch.elapsed(); return false; } // This map object was reserved, and is going to be shared, but needs to be loaded loadedSharedMapObjects.insert(id); return true; }, _dataBlocksCache.get(), &referencedMapDataBlocks, nullptr,// query controller metric ? &metric->loadMapObjectsMetric : nullptr); // Process loaded-and-shared map objects for (auto& mapObject : loadedMapObjects) { // Check if this map object is shared if (!loadedSharedMapObjects.contains(mapObject->id)) continue; // Add unique map object under lock to all zoom levels, for which this map object is valid assert(mapObject->level); _sharedMapObjects.fulfilPromiseAndReference( mapObject->id, Utilities::enumerateZoomLevels(mapObject->level->minZoom, mapObject->level->maxZoom), mapObject); } for (auto& futureMapObject : futureReferencedMapObjects) { auto mapObject = futureMapObject.get(); referencedMapObjects.push_back(qMove(mapObject)); } if (metric) metric->elapsedTimeForRead += totalReadTimeStopwatch.elapsed(); // Prepare data for the tile const auto sharedMapObjectsCount = referencedMapObjects.size() + loadedSharedMapObjects.size(); const auto allMapObjects = loadedMapObjects + referencedMapObjects; // Create tile const std::shared_ptr<BinaryMapDataTile> newTile(new BinaryMapDataTile( _dataBlocksCache, referencedMapDataBlocks, tileFoundation, allMapObjects, tileId, zoom)); newTile->_p->_weakLink = _link->getWeak(); newTile->_p->_refEntry = tileEntry; // Publish new tile outTiledData = newTile; // Store weak reference to new tile and mark it as 'Loaded' tileEntry->_tile = newTile; tileEntry->setState(TileState::Loaded); // Notify that tile has been loaded { QWriteLocker scopedLcoker(&tileEntry->_loadedConditionLock); tileEntry->_loadedCondition.wakeAll(); } if (metric) { metric->elapsedTime += totalTimeStopwatch.elapsed(); metric->objectsCount += allMapObjects.size(); metric->uniqueObjectsCount += allMapObjects.size() - sharedMapObjectsCount; metric->sharedObjectsCount += sharedMapObjectsCount; } #if OSMAND_PERFORMANCE_METRICS #if OSMAND_PERFORMANCE_METRICS <= 1 LogPrintf(LogSeverityLevel::Info, "%d map objects (%d unique, %d shared) read from %dx%d@%d in %fs", allMapObjects.size(), allMapObjects.size() - sharedMapObjectsCount, sharedMapObjectsCount, tileId.x, tileId.y, zoom, totalTimeStopwatch.elapsed()); #else LogPrintf(LogSeverityLevel::Info, "%d map objects (%d unique, %d shared) read from %dx%d@%d in %fs:\n%s", allMapObjects.size(), allMapObjects.size() - sharedMapObjectsCount, sharedMapObjectsCount, tileId.x, tileId.y, zoom, totalTimeStopwatch.elapsed(), qPrintable(metric ? metric->toString(QLatin1String("\t - ")) : QLatin1String("(null)"))); #endif // OSMAND_PERFORMANCE_METRICS <= 1 #endif // OSMAND_PERFORMANCE_METRICS return true; }