Skip to content

Commit

Permalink
Add ability for dispatched work to requeue a request
Browse files Browse the repository at this point in the history
This allows Octree and QuadtreeLoader to use a single function to queue / process work. This also reduces a lot of duplicated code.
  • Loading branch information
csciguy8 committed Jan 10, 2024
1 parent 164dc24 commit 8c1566f
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 146 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "RasterOverlayDetails.h"
#include "TileContent.h"

#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumAsync/IAssetRequest.h>
#include <CesiumGeometry/Axis.h>
#include <CesiumGltf/Model.h>
Expand All @@ -17,6 +18,11 @@ namespace Cesium3DTilesSelection {

class Tile;

struct RequestData {
std::string url;
std::vector<CesiumAsync::IAssetAccessor::THeader> headers;
};

/**
* @brief Store the content of the tile after finishing
* loading tile using {@link TilesetContentLoader::loadTileContent}:
Expand Down Expand Up @@ -62,7 +68,9 @@ enum class TileLoadResultState {
* background work happenning and
* __none__ of the fields in {@link TileLoadResult} or {@link TileChildrenResult} are applied to the tile
*/
RetryLater
RetryLater,

RequestRequired
};

/**
Expand Down Expand Up @@ -113,6 +121,8 @@ struct CESIUM3DTILESSELECTION_API TileLoadResult {
*/
std::function<void(Tile&)> tileInitializer;

RequestData requestData;

/**
* @brief The result of loading a tile. Note that if the state is Failed or
* RetryLater, __none__ of the fields above (including {@link TileLoadResult::tileInitializer}) will be
Expand All @@ -131,6 +141,8 @@ struct CESIUM3DTILESSELECTION_API TileLoadResult {
*
*/
static TileLoadResult createRetryLaterResult();

static TileLoadResult createRequestResult(const RequestData& request);
};

} // namespace Cesium3DTilesSelection
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ class TileWorkManager {
_pLogger(pLogger) {}
~TileWorkManager() noexcept;

void QueueWork(
void SetMaxSimultaneousRequests(size_t max);

void QueueBatch(
const std::vector<TileLoadWork*>& requestWork,
const std::vector<TileLoadWork*>& processingWork,
size_t maxSimultaneousRequests);
const std::vector<TileLoadWork*>& processingWork);

void QueueProcessingWork(const std::vector<TileLoadWork*>& processingWork);
void QueueSingleRequest(const TileLoadWork& requestWork);

void TakeProcessingWork(
size_t maxCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ enum class TileLoadPriorityGroup {
Urgent = 2
};

struct RequestData {
std::string url;
std::vector<CesiumAsync::IAssetAccessor::THeader> headers;
};

typedef std::vector<RequestData> RequestDataVec;

struct ResponseData {
Expand Down
61 changes: 15 additions & 46 deletions Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ CesiumAsync::Future<TileLoadResult> requestTileContent(
std::nullopt,
tileUrl,
{},
RequestData{},
TileLoadResultState::Success};
}

Expand Down Expand Up @@ -247,8 +248,12 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) {
std::string subtreeUrl =
resolveUrl(this->_baseUrl, this->_subtreeUrlTemplate, subtreeID);

// If subtree url is not loaded, request it and come back later
ResponseDataMap::const_iterator foundIt = responsesByUrl.find(subtreeUrl);
assert(foundIt != responsesByUrl.end());
if (foundIt == responsesByUrl.end()) {
return asyncSystem.createResolvedFuture<TileLoadResult>(
TileLoadResult::createRequestResult(RequestData{ subtreeUrl }));
}

return SubtreeAvailability::loadSubtree(
3,
Expand Down Expand Up @@ -280,14 +285,19 @@ ImplicitOctreeLoader::loadTileContent(const TileLoadInput& loadInput) {
std::nullopt,
std::string(),
{},
RequestData{},
TileLoadResultState::Success});
}

std::string tileUrl =
resolveUrl(this->_baseUrl, this->_contentUrlTemplate, *pOctreeID);

// If tile url is not loaded, request it and come back later
ResponseDataMap::const_iterator foundIt = responsesByUrl.find(tileUrl);
assert(foundIt != responsesByUrl.end());
if (foundIt == responsesByUrl.end()) {
return asyncSystem.createResolvedFuture<TileLoadResult>(
TileLoadResult::createRequestResult(RequestData{ tileUrl }));
}

return requestTileContent(
pLogger,
Expand All @@ -305,51 +315,10 @@ CesiumAsync::Future<TileLoadResult> ImplicitOctreeLoader::doProcessing(
}

void ImplicitOctreeLoader::getLoadWork(
Tile* pTile,
RequestData& outRequest,
Tile*,
RequestData&,
TileProcessingCallback& outCallback) {

// make sure the tile is a octree tile
const CesiumGeometry::OctreeTileID* pOctreeID =
std::get_if<CesiumGeometry::OctreeTileID>(&pTile->getTileID());
if (!pOctreeID)
return;

// find the subtree ID
uint32_t subtreeLevelIdx = pOctreeID->level / this->_subtreeLevels;
if (subtreeLevelIdx >= this->_loadedSubtrees.size())
return;

uint64_t levelLeft = pOctreeID->level % this->_subtreeLevels;
uint32_t subtreeLevel = this->_subtreeLevels * subtreeLevelIdx;
uint32_t subtreeX = pOctreeID->x >> levelLeft;
uint32_t subtreeY = pOctreeID->y >> levelLeft;
uint32_t subtreeZ = pOctreeID->z >> levelLeft;
CesiumGeometry::OctreeTileID subtreeID{
subtreeLevel,
subtreeX,
subtreeY,
subtreeZ};

uint64_t subtreeMortonIdx =
libmorton::morton3D_64_encode(subtreeX, subtreeY, subtreeZ);
auto subtreeIt =
this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx);
if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) {
// subtree is not loaded, so load it now.
outRequest.url =
resolveUrl(this->_baseUrl, this->_subtreeUrlTemplate, subtreeID);
return;
}

// subtree is available, so check if tile has content or not. If it has, then
// request it
if (!isTileContentAvailable(subtreeID, *pOctreeID, subtreeIt->second))
return;

outRequest.url =
resolveUrl(this->_baseUrl, this->_contentUrlTemplate, *pOctreeID);

// LoadTileContent will control request / processing flow
outCallback = doProcessing;
}

Expand Down
67 changes: 15 additions & 52 deletions Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ CesiumAsync::Future<TileLoadResult> requestTileContent(
std::nullopt,
tileUrl,
{},
RequestData{},
TileLoadResultState::Success};
}

Expand Down Expand Up @@ -266,12 +267,15 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) {
auto subtreeIt =
this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx);
if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) {
// subtree is not loaded, so load it now.
std::string subtreeUrl =
resolveUrl(this->_baseUrl, this->_subtreeUrlTemplate, subtreeID);

// If subtree url is not loaded, request it and come back later
ResponseDataMap::const_iterator foundIt = responsesByUrl.find(subtreeUrl);
assert(foundIt != responsesByUrl.end());
if (foundIt == responsesByUrl.end()) {
return asyncSystem.createResolvedFuture<TileLoadResult>(
TileLoadResult::createRequestResult(RequestData{subtreeUrl}));
}

return SubtreeAvailability::loadSubtree(
2,
Expand Down Expand Up @@ -303,14 +307,19 @@ ImplicitQuadtreeLoader::loadTileContent(const TileLoadInput& loadInput) {
std::nullopt,
std::string(),
{},
RequestData{},
TileLoadResultState::Success});
}

std::string tileUrl =
resolveUrl(this->_baseUrl, this->_contentUrlTemplate, *pQuadtreeID);

// If tile url is not loaded, request it and come back later
ResponseDataMap::const_iterator foundIt = responsesByUrl.find(tileUrl);
assert(foundIt != responsesByUrl.end());
if (foundIt == responsesByUrl.end()) {
return asyncSystem.createResolvedFuture<TileLoadResult>(
TileLoadResult::createRequestResult(RequestData{ tileUrl }));
}

return requestTileContent(
pLogger,
Expand All @@ -329,56 +338,10 @@ CesiumAsync::Future<TileLoadResult> ImplicitQuadtreeLoader::doProcessing(
}

void ImplicitQuadtreeLoader::getLoadWork(
Tile* pTile,
RequestData& outRequest,
Tile*,
RequestData&,
TileProcessingCallback& outCallback) {

// make sure the tile is a quadtree tile
const CesiumGeometry::QuadtreeTileID* pQuadtreeID =
std::get_if<CesiumGeometry::QuadtreeTileID>(&pTile->getTileID());
if (!pQuadtreeID)
return;

// find the subtree ID
uint32_t subtreeLevelIdx = pQuadtreeID->level / this->_subtreeLevels;
if (subtreeLevelIdx >= _loadedSubtrees.size())
return;

uint64_t levelLeft = pQuadtreeID->level % this->_subtreeLevels;
uint32_t subtreeLevel = this->_subtreeLevels * subtreeLevelIdx;
uint32_t subtreeX = pQuadtreeID->x >> levelLeft;
uint32_t subtreeY = pQuadtreeID->y >> levelLeft;
CesiumGeometry::QuadtreeTileID subtreeID{subtreeLevel, subtreeX, subtreeY};

// the below morton index hash to the subtree assumes that tileID's components
// x and y never exceed 32-bit. In other words, the max levels this loader can
// support is 33 which will have 4^32 tiles in the level 32th. The 64-bit
// morton index below can support that much tiles without overflow. More than
// 33 levels, this loader will fail. One solution for that is to create
// multiple new ImplicitQuadtreeLoaders and assign them to any tiles that have
// levels exceeding the maximum 33. Those new loaders will be added to the
// current loader, and thus, create a hierarchical tree of loaders where each
// loader will serve up to 33 levels with the level 0 being relative to the
// parent loader. The solution isn't implemented at the moment, as implicit
// tilesets that exceeds 33 levels are expected to be very rare
uint64_t subtreeMortonIdx = libmorton::morton2D_64_encode(subtreeX, subtreeY);
auto subtreeIt =
this->_loadedSubtrees[subtreeLevelIdx].find(subtreeMortonIdx);
if (subtreeIt == this->_loadedSubtrees[subtreeLevelIdx].end()) {
// subtree is not loaded, so load it now.
outRequest.url =
resolveUrl(this->_baseUrl, this->_subtreeUrlTemplate, subtreeID);
return;
}

// subtree is available, so check if tile has content or not. If it has, then
// request it
if (!isTileContentAvailable(subtreeID, *pQuadtreeID, subtreeIt->second))
return;

outRequest.url =
resolveUrl(this->_baseUrl, this->_contentUrlTemplate, *pQuadtreeID);

// LoadTileContent will control request / processing flow
outCallback = doProcessing;
}

Expand Down
2 changes: 2 additions & 0 deletions Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ TileLoadResult convertToTileLoadResult(QuantizedMeshLoadResult&& loadResult) {
std::nullopt,
std::string(),
{},
RequestData{},
TileLoadResultState::Success};
}

Expand Down Expand Up @@ -1195,6 +1196,7 @@ CesiumAsync::Future<TileLoadResult> LayerJsonTerrainLoader::upsampleParentTile(
std::nullopt,
std::string(),
{},
RequestData{},
TileLoadResultState::Success};
});
}
1 change: 1 addition & 0 deletions Cesium3DTilesSelection/src/RasterOverlayUpsampler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ RasterOverlayUpsampler::loadTileContent(const TileLoadInput& loadInput) {
std::nullopt,
std::string(),
{},
RequestData{},
TileLoadResultState::Success};
});
}
Expand Down
1 change: 1 addition & 0 deletions Cesium3DTilesSelection/src/SubtreeAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ SubtreeAvailability::loadSubtree(
asyncSystem = asyncSystem,
pLogger = pLogger,
responseData = responseData]() mutable {
// TODO, put response status code check back in
return parseSubtreeRequest(
powerOf2,
std::move(asyncSystem),
Expand Down
27 changes: 14 additions & 13 deletions Cesium3DTilesSelection/src/TileWorkManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ TileWorkManager::~TileWorkManager() noexcept {
}
}

void TileWorkManager::QueueWork(
void TileWorkManager::SetMaxSimultaneousRequests(size_t max) {
std::lock_guard<std::mutex> lock(_requestsLock);
_maxSimultaneousRequests = max;
}

void TileWorkManager::QueueBatch(
const std::vector<TileLoadWork*>& requestWork,
const std::vector<TileLoadWork*>& processingWork,
size_t maxSimultaneousRequests) {
const std::vector<TileLoadWork*>& processingWork) {
if (requestWork.empty() && processingWork.empty())
return;

Expand All @@ -30,21 +34,18 @@ void TileWorkManager::QueueWork(

for (TileLoadWork* element : processingWork)
_processingQueue.push_back(std::move(*element));

assert(maxSimultaneousRequests > 0);
_maxSimultaneousRequests = maxSimultaneousRequests;
}

transitionQueuedWork();
}

void TileWorkManager::QueueProcessingWork(
const std::vector<TileLoadWork*>& processingWork) {
if (processingWork.empty())
return;
std::lock_guard<std::mutex> lock(_requestsLock);
for (TileLoadWork* element : processingWork)
_processingQueue.push_back(std::move(*element));
void TileWorkManager::QueueSingleRequest(const TileLoadWork& requestWork) {
{
std::lock_guard<std::mutex> lock(_requestsLock);
_requestQueue.push_back(std::move(requestWork));
}

transitionQueuedWork();
}

void TileWorkManager::onRequestFinished(
Expand Down
16 changes: 16 additions & 0 deletions Cesium3DTilesSelection/src/TilesetContentLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ TileLoadResult TileLoadResult::createFailedResult() {
std::nullopt,
std::string(),
{},
RequestData{},
TileLoadResultState::Failed};
}

Expand All @@ -34,6 +35,21 @@ TileLoadResult TileLoadResult::createRetryLaterResult() {
std::nullopt,
std::string(),
{},
RequestData{},
TileLoadResultState::RetryLater};
}

TileLoadResult TileLoadResult::createRequestResult(const RequestData& request) {
return TileLoadResult{
TileUnknownContent{},
CesiumGeometry::Axis::Y,
std::nullopt,
std::nullopt,
std::nullopt,
std::string(),
{},
request,
TileLoadResultState::RequestRequired};
}

} // namespace Cesium3DTilesSelection
Loading

0 comments on commit 8c1566f

Please sign in to comment.