mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-20 19:21:00 -06:00
213 lines
8.3 KiB
C++
213 lines
8.3 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2021 *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
|
* software and associated documentation files (the "Software"), to deal in the Software *
|
|
* without restriction, including without limitation the rights to use, copy, modify, *
|
|
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
|
* permit persons to whom the Software is furnished to do so, subject to the following *
|
|
* conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included in all copies *
|
|
* or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
****************************************************************************************/
|
|
|
|
#include <modules/globebrowsing/src/asynctiledataprovider.h>
|
|
|
|
#include <modules/globebrowsing/src/memoryawaretilecache.h>
|
|
#include <modules/globebrowsing/src/rawtiledatareader.h>
|
|
#include <modules/globebrowsing/src/tileloadjob.h>
|
|
#include <openspace/engine/moduleengine.h>
|
|
#include <openspace/engine/globals.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/misc/profiling.h>
|
|
#include <ghoul/opengl/ghoul_gl.h>
|
|
|
|
namespace openspace::globebrowsing {
|
|
|
|
namespace {
|
|
constexpr const char* _loggerCat = "AsyncTileDataProvider";
|
|
} // namespace
|
|
|
|
AsyncTileDataProvider::AsyncTileDataProvider(std::string name,
|
|
std::unique_ptr<RawTileDataReader> rawTileDataReader)
|
|
: _name(std::move(name))
|
|
, _rawTileDataReader(std::move(rawTileDataReader))
|
|
, _concurrentJobManager(LRUThreadPool<TileIndex::TileHashKey>(1, 10))
|
|
{
|
|
ZoneScoped
|
|
|
|
performReset(ResetRawTileDataReader::No);
|
|
}
|
|
|
|
AsyncTileDataProvider::~AsyncTileDataProvider() {} // NOLINT
|
|
|
|
const RawTileDataReader& AsyncTileDataProvider::rawTileDataReader() const {
|
|
return *_rawTileDataReader;
|
|
}
|
|
|
|
bool AsyncTileDataProvider::enqueueTileIO(const TileIndex& tileIndex) {
|
|
ZoneScoped
|
|
|
|
if (_resetMode == ResetMode::ShouldNotReset && satisfiesEnqueueCriteria(tileIndex)) {
|
|
auto job = std::make_unique<TileLoadJob>(*_rawTileDataReader, tileIndex);
|
|
_concurrentJobManager.enqueueJob(std::move(job), tileIndex.hashKey());
|
|
_enqueuedTileRequests.insert(tileIndex.hashKey());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void AsyncTileDataProvider::clearTiles() {
|
|
std::optional<RawTile> finishedJob = popFinishedRawTile();
|
|
while (finishedJob) {
|
|
finishedJob = popFinishedRawTile();
|
|
}
|
|
}
|
|
|
|
std::optional<RawTile> AsyncTileDataProvider::popFinishedRawTile() {
|
|
if (_concurrentJobManager.numFinishedJobs() > 0) {
|
|
// Now the tile load job looses ownerwhip of the data pointer
|
|
RawTile product = _concurrentJobManager.popFinishedJob()->product();
|
|
|
|
const TileIndex::TileHashKey key = product.tileIndex.hashKey();
|
|
// No longer enqueued. Remove from set of enqueued tiles
|
|
_enqueuedTileRequests.erase(key);
|
|
// Pbo is still mapped. Set the id for the raw tile
|
|
if (product.error != RawTile::ReadError::None) {
|
|
product.imageData = nullptr;
|
|
return std::nullopt;
|
|
}
|
|
|
|
return product;
|
|
}
|
|
else {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
bool AsyncTileDataProvider::satisfiesEnqueueCriteria(const TileIndex& tileIndex) {
|
|
ZoneScoped
|
|
|
|
// Only satisfies if it is not already enqueued. Also bumps the request to the top.
|
|
const bool alreadyEnqueued = _concurrentJobManager.touch(tileIndex.hashKey());
|
|
// Early out so we don't need to check the already enqueued requests
|
|
if (alreadyEnqueued) {
|
|
return false;
|
|
}
|
|
|
|
// Concurrent job manager can start jobs which will pop them from enqueued, however
|
|
// they are still in _enqueuedTileRequests until finished
|
|
const auto it = _enqueuedTileRequests.find(tileIndex.hashKey());
|
|
const bool notFoundAmongEnqueued = it == _enqueuedTileRequests.end();
|
|
|
|
return !alreadyEnqueued && notFoundAmongEnqueued;
|
|
}
|
|
|
|
void AsyncTileDataProvider::endUnfinishedJobs() {
|
|
std::vector<TileIndex::TileHashKey> unfinishedJobs =
|
|
_concurrentJobManager.keysToUnfinishedJobs();
|
|
for (const TileIndex::TileHashKey& unfinishedJob : unfinishedJobs) {
|
|
// When erasing the job before
|
|
_enqueuedTileRequests.erase(unfinishedJob);
|
|
}
|
|
}
|
|
|
|
void AsyncTileDataProvider::endEnqueuedJobs() {
|
|
std::vector<TileIndex::TileHashKey> enqueuedJobs =
|
|
_concurrentJobManager.keysToEnqueuedJobs();
|
|
for (const TileIndex::TileHashKey& enqueuedJob : enqueuedJobs) {
|
|
// When erasing the job before
|
|
_enqueuedTileRequests.erase(enqueuedJob);
|
|
}
|
|
}
|
|
|
|
void AsyncTileDataProvider::update() {
|
|
endUnfinishedJobs();
|
|
|
|
// May reset
|
|
switch (_resetMode) {
|
|
case ResetMode::ShouldResetAll: {
|
|
// Clean all finished jobs
|
|
clearTiles();
|
|
// Only allow resetting if there are no jobs currently running
|
|
if (_enqueuedTileRequests.empty()) {
|
|
performReset(ResetRawTileDataReader::Yes);
|
|
LINFO(fmt::format("Tile data reader '{}' reset successfully", _name));
|
|
}
|
|
break;
|
|
}
|
|
case ResetMode::ShouldResetAllButRawTileDataReader: {
|
|
// Clean all finished jobs
|
|
clearTiles();
|
|
// Only allow resetting if there are no jobs currently running
|
|
if (_enqueuedTileRequests.empty()) {
|
|
performReset(ResetRawTileDataReader::No);
|
|
LINFO(fmt::format("Tile data reader '{}' reset successfully", _name));
|
|
}
|
|
break;
|
|
}
|
|
case ResetMode::ShouldBeDeleted: {
|
|
// Clean all finished jobs
|
|
clearTiles();
|
|
// Only allow resetting if there are no jobs currently running
|
|
if (_enqueuedTileRequests.empty()) {
|
|
_shouldBeDeleted = true;
|
|
}
|
|
break;
|
|
}
|
|
case ResetMode::ShouldNotReset: {
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AsyncTileDataProvider::reset() {
|
|
// Can not clear concurrent job manager in case there are threads running. therefore
|
|
// we need to wait until _enqueuedTileRequests is empty before finishing up.
|
|
_resetMode = ResetMode::ShouldResetAll;
|
|
endEnqueuedJobs();
|
|
LINFO(fmt::format("Prepairing for resetting of tile reader '{}'", _name));
|
|
}
|
|
|
|
void AsyncTileDataProvider::prepareToBeDeleted() {
|
|
_resetMode = ResetMode::ShouldBeDeleted;
|
|
endEnqueuedJobs();
|
|
}
|
|
|
|
bool AsyncTileDataProvider::shouldBeDeleted() {
|
|
return _shouldBeDeleted;
|
|
}
|
|
|
|
void AsyncTileDataProvider::performReset(ResetRawTileDataReader resetRawTileDataReader) {
|
|
ZoneScoped
|
|
|
|
ghoul_assert(_enqueuedTileRequests.empty(), "No enqueued requests left");
|
|
|
|
// Reset raw tile data reader
|
|
if (resetRawTileDataReader == ResetRawTileDataReader::Yes) {
|
|
_rawTileDataReader->reset();
|
|
}
|
|
|
|
// Finished resetting
|
|
_resetMode = ResetMode::ShouldNotReset;
|
|
}
|
|
|
|
float AsyncTileDataProvider::noDataValueAsFloat() const {
|
|
return _rawTileDataReader->noDataValueAsFloat();
|
|
}
|
|
|
|
} // namespace openspace::globebrowsing
|