From 276375a7992bf235055e22ab7001d3cf15f89bf3 Mon Sep 17 00:00:00 2001 From: Erik Broberg Date: Fri, 13 May 2016 19:53:45 -0400 Subject: [PATCH 1/6] Added gui bool for toggling merge cullable chuch nodes, and made culling view frustum static --- modules/globebrowsing/globes/chunk.cpp | 3 ++- modules/globebrowsing/globes/chunkedlodglobe.h | 2 +- modules/globebrowsing/globes/renderableglobe.cpp | 6 +++--- modules/globebrowsing/globes/renderableglobe.h | 2 +- modules/globebrowsing/rendering/culling.cpp | 5 ++++- modules/globebrowsing/rendering/culling.h | 2 ++ modules/globebrowsing/shaders/globalchunkedlodpatch_fs.glsl | 2 +- modules/globebrowsing/shaders/localchunkedlodpatch_fs.glsl | 2 +- 8 files changed, 15 insertions(+), 9 deletions(-) diff --git a/modules/globebrowsing/globes/chunk.cpp b/modules/globebrowsing/globes/chunk.cpp index ee68d162ee..9ee0df61b1 100644 --- a/modules/globebrowsing/globes/chunk.cpp +++ b/modules/globebrowsing/globes/chunk.cpp @@ -102,7 +102,8 @@ namespace openspace { _isVisible &= HorizonCuller::isVisible(myRenderData, _surfacePatch, ellipsoid, maxHeight); } - if (!_isVisible) return WANT_MERGE; + if(!_isVisible && owner()->mergeInvisible) + return WANT_MERGE; Vec3 cameraPosition = myRenderData.camera.position().dvec3(); diff --git a/modules/globebrowsing/globes/chunkedlodglobe.h b/modules/globebrowsing/globes/chunkedlodglobe.h index 0e6be5e958..f0c0611a65 100644 --- a/modules/globebrowsing/globes/chunkedlodglobe.h +++ b/modules/globebrowsing/globes/chunkedlodglobe.h @@ -87,7 +87,7 @@ namespace openspace { bool doHorizonCulling = true; bool doFrustumCulling = true; - int numPosZthres; + bool mergeInvisible; float lodScaleFactor; bool initChunkVisible; diff --git a/modules/globebrowsing/globes/renderableglobe.cpp b/modules/globebrowsing/globes/renderableglobe.cpp index 419c670744..baac1ecf10 100644 --- a/modules/globebrowsing/globes/renderableglobe.cpp +++ b/modules/globebrowsing/globes/renderableglobe.cpp @@ -56,7 +56,7 @@ namespace openspace { , _saveOrThrowCamera(properties::BoolProperty("saveOrThrowCamera", "saveOrThrowCamera")) , doFrustumCulling(properties::BoolProperty("doFrustumCulling", "doFrustumCulling")) , doHorizonCulling(properties::BoolProperty("doHorizonCulling", "doHorizonCulling")) - , numPosZthres(properties::IntProperty("numPosZthres", "numPosZthres", 0, 0, 9)) + , mergeInvisible(properties::BoolProperty("mergeInvisible", "mergeInvisible", true)) , lodScaleFactor(properties::FloatProperty("lodScaleFactor", "lodScaleFactor", 10.0f, 0.0f, 100.0f)) , initChunkVisible(properties::BoolProperty("initChunkVisible", "initChunkVisible", true)) @@ -67,7 +67,7 @@ namespace openspace { addProperty(_saveOrThrowCamera); addProperty(doFrustumCulling); addProperty(doHorizonCulling); - addProperty(numPosZthres); + addProperty(mergeInvisible); addProperty(lodScaleFactor); addProperty(initChunkVisible); @@ -158,7 +158,7 @@ namespace openspace { } _chunkedLodGlobe->doFrustumCulling = doFrustumCulling.value(); _chunkedLodGlobe->doHorizonCulling = doHorizonCulling.value(); - _chunkedLodGlobe->numPosZthres = numPosZthres.value(); + _chunkedLodGlobe->mergeInvisible = mergeInvisible.value(); _chunkedLodGlobe->lodScaleFactor= lodScaleFactor.value(); _chunkedLodGlobe->initChunkVisible = initChunkVisible.value(); _distanceSwitch.render(data); diff --git a/modules/globebrowsing/globes/renderableglobe.h b/modules/globebrowsing/globes/renderableglobe.h index 0137e60da2..7ee16227e9 100644 --- a/modules/globebrowsing/globes/renderableglobe.h +++ b/modules/globebrowsing/globes/renderableglobe.h @@ -65,7 +65,7 @@ public: properties::BoolProperty doFrustumCulling; properties::BoolProperty doHorizonCulling; - properties::IntProperty numPosZthres; + properties::BoolProperty mergeInvisible; properties::FloatProperty lodScaleFactor; properties::BoolProperty initChunkVisible; private: diff --git a/modules/globebrowsing/rendering/culling.cpp b/modules/globebrowsing/rendering/culling.cpp index ba8249c4a5..9e50a7accb 100644 --- a/modules/globebrowsing/rendering/culling.cpp +++ b/modules/globebrowsing/rendering/culling.cpp @@ -48,6 +48,9 @@ namespace openspace { } + const AABB3 viewFrustum = AABB3(vec3(-1, -1, 0), vec3(1, 1, 1e35)); + + bool FrustumCuller::isVisible( const RenderData& data, const vec3& point) { @@ -143,7 +146,7 @@ namespace openspace { bounds.expand(cornerScreenSpace); } - AABB3 viewFrustum(vec3(-1, -1, 0), vec3(1, 1, 1e35)); + return bounds.intersects(viewFrustum); /* diff --git a/modules/globebrowsing/rendering/culling.h b/modules/globebrowsing/rendering/culling.h index 24db4d0ebd..b47661908a 100644 --- a/modules/globebrowsing/rendering/culling.h +++ b/modules/globebrowsing/rendering/culling.h @@ -58,6 +58,8 @@ namespace openspace { FrustumCuller(); ~FrustumCuller(); + static const AABB3 viewFrustum; + /** Returns true if the point is inside the view frustrum defined in RenderData. The third argument marginScreenSpace is added to the default screen space diff --git a/modules/globebrowsing/shaders/globalchunkedlodpatch_fs.glsl b/modules/globebrowsing/shaders/globalchunkedlodpatch_fs.glsl index 6d7b93701c..488cc35fa3 100644 --- a/modules/globebrowsing/shaders/globalchunkedlodpatch_fs.glsl +++ b/modules/globebrowsing/shaders/globalchunkedlodpatch_fs.glsl @@ -45,7 +45,7 @@ Fragment getFragment() { //frag.color = frag.color * 0.9 + 0.2*vec4(samplePos, 0, 1); // Border overlay - frag.color = frag.color + patchBorderOverlay(fs_uv, vec3(0.5, 0.5, 0.5), 0.02); + //frag.color = frag.color + patchBorderOverlay(fs_uv, vec3(0.5, 0.5, 0.5), 0.02); frag.depth = fs_position.w; diff --git a/modules/globebrowsing/shaders/localchunkedlodpatch_fs.glsl b/modules/globebrowsing/shaders/localchunkedlodpatch_fs.glsl index 6970a17f92..8f935d9225 100644 --- a/modules/globebrowsing/shaders/localchunkedlodpatch_fs.glsl +++ b/modules/globebrowsing/shaders/localchunkedlodpatch_fs.glsl @@ -45,7 +45,7 @@ Fragment getFragment() { //frag.color = frag.color * 0.9 + 0.2*vec4(samplePos, 0, 1); // Border overlay - frag.color = frag.color + patchBorderOverlay(fs_uv, vec3(0.5, 0.5, 0.5), 0.02); + //frag.color = frag.color + patchBorderOverlay(fs_uv, vec3(0.5, 0.5, 0.5), 0.02); frag.depth = fs_position.w; From 6f953ecdfb6b1f108f79c680e021e4557a12feb8 Mon Sep 17 00:00:00 2001 From: Erik Broberg Date: Mon, 16 May 2016 13:12:52 -0400 Subject: [PATCH 2/6] Flush tile request queue every X frame. Currently set to every 60 frame --- .../globebrowsing/globes/chunkedlodglobe.cpp | 2 ++ modules/globebrowsing/globes/chunkindex.h | 2 +- .../globebrowsing/globes/renderableglobe.cpp | 4 +-- .../other/concurrentjobmanager.h | 6 ++++ modules/globebrowsing/other/tileprovider.cpp | 34 +++++++++++++++---- modules/globebrowsing/other/tileprovider.h | 32 +++++++++++++++-- modules/globebrowsing/rendering/culling.cpp | 5 ++- modules/globebrowsing/rendering/culling.h | 5 ++- 8 files changed, 71 insertions(+), 19 deletions(-) diff --git a/modules/globebrowsing/globes/chunkedlodglobe.cpp b/modules/globebrowsing/globes/chunkedlodglobe.cpp index ca8e6be1f1..86128e7a04 100644 --- a/modules/globebrowsing/globes/chunkedlodglobe.cpp +++ b/modules/globebrowsing/globes/chunkedlodglobe.cpp @@ -97,6 +97,7 @@ namespace openspace { minDistToCamera = INFINITY; ChunkNode::renderedPatches = 0; + _leftRoot->render(data); _rightRoot->render(data); @@ -112,6 +113,7 @@ namespace openspace { void ChunkedLodGlobe::update(const UpdateData& data) { _patchRenderer->update(); + } const Ellipsoid& ChunkedLodGlobe::ellipsoid() const diff --git a/modules/globebrowsing/globes/chunkindex.h b/modules/globebrowsing/globes/chunkindex.h index 80226aa1e1..0f70e6f690 100644 --- a/modules/globebrowsing/globes/chunkindex.h +++ b/modules/globebrowsing/globes/chunkindex.h @@ -48,7 +48,7 @@ using HashKey = unsigned long; struct ChunkIndex { - + int x, y, level; diff --git a/modules/globebrowsing/globes/renderableglobe.cpp b/modules/globebrowsing/globes/renderableglobe.cpp index baac1ecf10..04734bafbf 100644 --- a/modules/globebrowsing/globes/renderableglobe.cpp +++ b/modules/globebrowsing/globes/renderableglobe.cpp @@ -96,7 +96,7 @@ namespace openspace { colorTextureDictionary.getValue("FilePath", path); std::shared_ptr colorTextureProvider = std::shared_ptr(new TileProvider( - path, 5000, 1024)); + path, 5000, 1024, 60)); _tileProviderManager->addColorTexture(name, colorTextureProvider); } @@ -113,7 +113,7 @@ namespace openspace { heightMapDictionary.getValue("FilePath", path); std::shared_ptr heightMapProvider = std::shared_ptr(new TileProvider( - path, 5000, 256)); + path, 5000, 256, 60)); _tileProviderManager->addHeightMap(name, heightMapProvider); } diff --git a/modules/globebrowsing/other/concurrentjobmanager.h b/modules/globebrowsing/other/concurrentjobmanager.h index 1a994a3a7f..33cd150b5a 100644 --- a/modules/globebrowsing/other/concurrentjobmanager.h +++ b/modules/globebrowsing/other/concurrentjobmanager.h @@ -83,6 +83,12 @@ namespace openspace { } } + void clearEnqueuedJobs() { + while (_incomingJobs.size()) { + _incomingJobs.pop(); + } + } + std::shared_ptr> popFinishedJob() { ghoul_assert(_finishedJobs.size() > 0, "There is no finished job to pop!"); return _finishedJobs.pop(); diff --git a/modules/globebrowsing/other/tileprovider.cpp b/modules/globebrowsing/other/tileprovider.cpp index a8ef467eb4..58d98915d5 100644 --- a/modules/globebrowsing/other/tileprovider.cpp +++ b/modules/globebrowsing/other/tileprovider.cpp @@ -48,9 +48,12 @@ namespace openspace { TileProvider::TileProvider( const std::string& filePath, int tileCacheSize, - int minimumPixelSize) + int minimumPixelSize, + int framesUntilRequestFlush) : _filePath(filePath) , _tileCache(tileCacheSize) // setting cache size + , _framesSinceLastRequestFlush(0) + , _framesUntilRequestFlush(framesUntilRequestFlush) { // Set a temporary texture std::string fileName = "textures/earth_bluemarble.jpg"; @@ -101,11 +104,20 @@ namespace openspace { } TileProvider::~TileProvider(){ + clearRequestQueue(); delete _gdalDataSet; } void TileProvider::prerender() { + initTexturesFromLoadedData(); + + if (_framesSinceLastRequestFlush++ > _framesUntilRequestFlush) { + clearRequestQueue(); + } + } + + void TileProvider::initTexturesFromLoadedData() { while (_tileLoadManager.numFinishedJobs() > 0) { auto finishedJob = _tileLoadManager.popFinishedJob(); std::shared_ptr uninitedTex = @@ -116,6 +128,12 @@ namespace openspace { } } + void TileProvider::clearRequestQueue() { + _tileLoadManager.clearEnqueuedJobs(); + _queuedTileRequests.clear(); + _framesSinceLastRequestFlush = 0; + } + Tile TileProvider::getMostHiResTile(ChunkIndex chunkIndex) { std::shared_ptr tex = nullptr; @@ -187,14 +205,16 @@ namespace openspace { return _tileCache.get(hashkey); } else { - // enque load job - std::shared_ptr job = std::shared_ptr( - new TextureTileLoadJob(this, chunkIndex)); + bool tileHasBeenQueued = _queuedTileRequests.find(hashkey) != _queuedTileRequests.end(); + if (!tileHasBeenQueued) { + // enque load job + std::shared_ptr job = std::shared_ptr( + new TextureTileLoadJob(this, chunkIndex)); - _tileLoadManager.enqueueJob(job); + _tileLoadManager.enqueueJob(job); - // map key to nullptr while tile is loaded - _tileCache.put(hashkey, nullptr); + _queuedTileRequests.insert(hashkey); + } return nullptr; } } diff --git a/modules/globebrowsing/other/tileprovider.h b/modules/globebrowsing/other/tileprovider.h index 4117b5898d..6a5f46dcf8 100644 --- a/modules/globebrowsing/other/tileprovider.h +++ b/modules/globebrowsing/other/tileprovider.h @@ -28,6 +28,7 @@ #include "gdal_priv.h" #include +#include #include #include // absPath @@ -69,7 +70,8 @@ namespace openspace { */ class TileProvider { public: - TileProvider(const std::string& fileName, int tileCacheSize, int minimumPixelSize); + TileProvider(const std::string& fileName, int tileCacheSize, int minimumPixelSize, + int framesUntilRequestFlush); ~TileProvider(); Tile getMostHiResTile(ChunkIndex chunkIndex); @@ -84,6 +86,13 @@ namespace openspace { friend class TextureTileLoadJob; + + + ////////////////////////////////////////////////////////////////////////////////// + // Helper functions // + ////////////////////////////////////////////////////////////////////////////////// + + /** Fetches all the needeed texture data from the GDAL dataset. */ @@ -96,9 +105,26 @@ namespace openspace { std::shared_ptr initializeTexture( std::shared_ptr uninitedTexture); - LRUCache> _tileCache; - const std::string _filePath; + void clearRequestQueue(); + + void initTexturesFromLoadedData(); + + + + + ////////////////////////////////////////////////////////////////////////////////// + // Member variables // + ////////////////////////////////////////////////////////////////////////////////// + + + LRUCache> _tileCache; + std::set _queuedTileRequests; + + int _framesSinceLastRequestFlush; + int _framesUntilRequestFlush; + + const std::string _filePath; static bool hasInitializedGDAL; GDALDataset* _gdalDataSet; diff --git a/modules/globebrowsing/rendering/culling.cpp b/modules/globebrowsing/rendering/culling.cpp index 9e50a7accb..a8dd94c96d 100644 --- a/modules/globebrowsing/rendering/culling.cpp +++ b/modules/globebrowsing/rendering/culling.cpp @@ -24,7 +24,6 @@ #include -#include #include @@ -48,7 +47,7 @@ namespace openspace { } - const AABB3 viewFrustum = AABB3(vec3(-1, -1, 0), vec3(1, 1, 1e35)); + const AABB3 FrustumCuller::viewFrustum(vec3(-1, -1, 0), vec3(1, 1, 1e35)); bool FrustumCuller::isVisible( @@ -147,7 +146,7 @@ namespace openspace { } - return bounds.intersects(viewFrustum); + return bounds.intersects(FrustumCuller::viewFrustum); /* vec2 center = bounds.center(); diff --git a/modules/globebrowsing/rendering/culling.h b/modules/globebrowsing/rendering/culling.h index b47661908a..f7d6e92f33 100644 --- a/modules/globebrowsing/rendering/culling.h +++ b/modules/globebrowsing/rendering/culling.h @@ -34,6 +34,8 @@ #include #include +#include + namespace openspace { @@ -52,9 +54,6 @@ namespace openspace { class FrustumCuller { public: - - - FrustumCuller(); ~FrustumCuller(); From 33d1d1a31d1fe9376945fa4d138434327a314441 Mon Sep 17 00:00:00 2001 From: Erik Broberg Date: Mon, 16 May 2016 14:33:28 -0400 Subject: [PATCH 3/6] Enable rendering smallest (i.e. closest) chunk nodes first. Toggleable from GUI --- .../globebrowsing/globes/chunkedlodglobe.cpp | 24 +++++-- .../globebrowsing/globes/chunkedlodglobe.h | 3 + modules/globebrowsing/globes/chunknode.cpp | 65 ++++++++++++------- modules/globebrowsing/globes/chunknode.h | 17 +++-- .../globebrowsing/globes/renderableglobe.cpp | 6 +- .../globebrowsing/globes/renderableglobe.h | 1 + 6 files changed, 77 insertions(+), 39 deletions(-) diff --git a/modules/globebrowsing/globes/chunkedlodglobe.cpp b/modules/globebrowsing/globes/chunkedlodglobe.cpp index 86128e7a04..cb63048376 100644 --- a/modules/globebrowsing/globes/chunkedlodglobe.cpp +++ b/modules/globebrowsing/globes/chunkedlodglobe.cpp @@ -95,20 +95,30 @@ namespace openspace { void ChunkedLodGlobe::render(const RenderData& data){ minDistToCamera = INFINITY; - ChunkNode::renderedPatches = 0; + ChunkNode::renderedChunks = 0; - - _leftRoot->render(data); - _rightRoot->render(data); + renderChunkTree(_leftRoot.get(), data); + renderChunkTree(_rightRoot.get(), data); //LDEBUG("min distnace to camera: " << minDistToCamera); Vec3 cameraPos = data.camera.position().dvec3(); //LDEBUG("cam pos x: " << cameraPos.x << " y: " << cameraPos.y << " z: " << cameraPos.z); - //LDEBUG("ChunkNode count: " << ChunkNode::instanceCount); - //LDEBUG("RenderedPatches count: " << ChunkNode::renderedPatches); - //LDEBUG(ChunkNode::renderedPatches << " / " << ChunkNode::instanceCount << " chunks rendered"); + //LDEBUG("ChunkNode count: " << ChunkNode::chunkNodeCount); + //LDEBUG("RenderedPatches count: " << ChunkNode::renderedChunks); + //LDEBUG(ChunkNode::renderedChunks << " / " << ChunkNode::chunkNodeCount << " chunks rendered"); + } + + void ChunkedLodGlobe::renderChunkTree(ChunkNode* node, const RenderData& data) const { + node->updateChunkTree(data); + if (renderSmallChunksFirst) { + node->renderReversedBreadthFirst(data); + } + else { + node->renderDepthFirst(data); + } + } void ChunkedLodGlobe::update(const UpdateData& data) { diff --git a/modules/globebrowsing/globes/chunkedlodglobe.h b/modules/globebrowsing/globes/chunkedlodglobe.h index f0c0611a65..ab1dc68295 100644 --- a/modules/globebrowsing/globes/chunkedlodglobe.h +++ b/modules/globebrowsing/globes/chunkedlodglobe.h @@ -90,10 +90,13 @@ namespace openspace { bool mergeInvisible; float lodScaleFactor; bool initChunkVisible; + bool renderSmallChunksFirst = true; private: + void renderChunkTree(ChunkNode* node, const RenderData& data) const; + // Covers all negative longitudes std::unique_ptr _leftRoot; diff --git a/modules/globebrowsing/globes/chunknode.cpp b/modules/globebrowsing/globes/chunknode.cpp index 9937f2e753..cba8d660dd 100644 --- a/modules/globebrowsing/globes/chunknode.cpp +++ b/modules/globebrowsing/globes/chunknode.cpp @@ -22,13 +22,14 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include #include +#include #include #include @@ -39,8 +40,8 @@ namespace { namespace openspace { -int ChunkNode::instanceCount = 0; -int ChunkNode::renderedPatches = 0; +int ChunkNode::chunkNodeCount = 0; +int ChunkNode::renderedChunks = 0; ChunkNode::ChunkNode(const Chunk& chunk, ChunkNode* parent) : _chunk(chunk) @@ -50,11 +51,11 @@ ChunkNode::ChunkNode(const Chunk& chunk, ChunkNode* parent) _children[1] = nullptr; _children[2] = nullptr; _children[3] = nullptr; - instanceCount++; + chunkNodeCount++; } ChunkNode::~ChunkNode() { - instanceCount--; + chunkNodeCount--; } bool ChunkNode::isRoot() const { @@ -66,16 +67,8 @@ bool ChunkNode::isLeaf() const { } -void ChunkNode::render(const RenderData& data) { - ghoul_assert(isRoot(), "this method should only be invoked on root"); - //LDEBUG("-------------"); - internalUpdateChunkTree(data); - internalRender(data); -} - - // Returns true or false wether this node can be merge or not -bool ChunkNode::internalUpdateChunkTree(const RenderData& data) { +bool ChunkNode::updateChunkTree(const RenderData& data) { //Geodetic2 center = _chunk.surfacePatch.center(); //LDEBUG("x: " << patch.x << " y: " << patch.y << " level: " << patch.level << " lat: " << center.lat << " lon: " << center.lon); @@ -90,7 +83,7 @@ bool ChunkNode::internalUpdateChunkTree(const RenderData& data) { char requestedMergeMask = 0; for (int i = 0; i < 4; ++i) { - if (_children[i]->internalUpdateChunkTree(data)) { + if (_children[i]->updateChunkTree(data)) { requestedMergeMask |= (1 << i); } } @@ -99,26 +92,54 @@ bool ChunkNode::internalUpdateChunkTree(const RenderData& data) { if (requestedMergeMask == 0xf && _chunk.update(data)) { merge(); - // re-run internalUpdateChunkTree on this, now that this is a leaf node + // re-run updateChunkTree on this, now that this is a leaf node // OBS, this may currently cause a split() again ... - return internalUpdateChunkTree(data); + return updateChunkTree(data); } return false; } } -void ChunkNode::internalRender(const RenderData& data) { +void ChunkNode::renderReversedBreadthFirst(const RenderData& data) { + std::stack S; + std::queue Q; + Q.push(this); + while (Q.size() > 0) { + ChunkNode* node = Q.front(); + Q.pop(); + if (node->isLeaf()) { + if (node->_chunk.isVisible()) { + S.push(node); + } + } + else { + for (int i = 0; i < 4; ++i) { + Q.push(node->_children[i].get()); + } + } + } + while (S.size() > 0) { + S.top()->renderThisChunk(data); + S.pop(); + } +} + +void ChunkNode::renderThisChunk(const RenderData& data) { + ChunkRenderer& patchRenderer = _chunk.owner()->getPatchRenderer(); + patchRenderer.renderChunk(_chunk, data); + ChunkNode::renderedChunks++; +} + +void ChunkNode::renderDepthFirst(const RenderData& data) { if (isLeaf()) { if (_chunk.isVisible()) { - ChunkRenderer& patchRenderer = _chunk.owner()->getPatchRenderer(); - patchRenderer.renderChunk(_chunk, data); - ChunkNode::renderedPatches++; + renderThisChunk(data); } } else { for (int i = 0; i < 4; ++i) { - _children[i]->internalRender(data); + _children[i]->renderDepthFirst(data); } } } diff --git a/modules/globebrowsing/globes/chunknode.h b/modules/globebrowsing/globes/chunknode.h index f107466180..9af224db6f 100644 --- a/modules/globebrowsing/globes/chunknode.h +++ b/modules/globebrowsing/globes/chunknode.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -64,19 +65,17 @@ public: const ChunkNode& getChild(Quad quad) const; - void render(const RenderData& data); + void renderDepthFirst(const RenderData& data); - static int instanceCount; - static int renderedPatches; + void renderReversedBreadthFirst(const RenderData& data); + void renderThisChunk(const RenderData& data); + bool updateChunkTree(const RenderData& data); + + static int chunkNodeCount; + static int renderedChunks; private: - - void internalRender(const RenderData& data); - bool internalUpdateChunkTree(const RenderData& data); - - - ChunkNode* _parent; std::unique_ptr _children[4]; diff --git a/modules/globebrowsing/globes/renderableglobe.cpp b/modules/globebrowsing/globes/renderableglobe.cpp index 04734bafbf..a69500fd41 100644 --- a/modules/globebrowsing/globes/renderableglobe.cpp +++ b/modules/globebrowsing/globes/renderableglobe.cpp @@ -59,7 +59,7 @@ namespace openspace { , mergeInvisible(properties::BoolProperty("mergeInvisible", "mergeInvisible", true)) , lodScaleFactor(properties::FloatProperty("lodScaleFactor", "lodScaleFactor", 10.0f, 0.0f, 100.0f)) , initChunkVisible(properties::BoolProperty("initChunkVisible", "initChunkVisible", true)) - + , renderSmallChunksFirst(properties::BoolProperty("renderSmallChunksFirst", "renderSmallChunksFirst", true)) { setName("RenderableGlobe"); @@ -70,9 +70,13 @@ namespace openspace { addProperty(mergeInvisible); addProperty(lodScaleFactor); addProperty(initChunkVisible); + addProperty(renderSmallChunksFirst); doFrustumCulling.setValue(true); doHorizonCulling.setValue(true); + renderSmallChunksFirst.setValue(true); + + // Read the radii in to its own dictionary Vec3 radii; diff --git a/modules/globebrowsing/globes/renderableglobe.h b/modules/globebrowsing/globes/renderableglobe.h index 7ee16227e9..0567422abe 100644 --- a/modules/globebrowsing/globes/renderableglobe.h +++ b/modules/globebrowsing/globes/renderableglobe.h @@ -68,6 +68,7 @@ public: properties::BoolProperty mergeInvisible; properties::FloatProperty lodScaleFactor; properties::BoolProperty initChunkVisible; + properties::BoolProperty renderSmallChunksFirst; private: double _time; From 025a221df2c223ff783c5af788b334327b7f1e1d Mon Sep 17 00:00:00 2001 From: Erik Broberg Date: Mon, 16 May 2016 19:10:39 -0400 Subject: [PATCH 4/6] TileProvider enqueues tiles at lower level first --- modules/globebrowsing/globes/chunk.cpp | 4 +- modules/globebrowsing/globes/chunk.h | 1 + modules/globebrowsing/globes/chunknode.cpp | 6 +- modules/globebrowsing/other/tileprovider.cpp | 134 +++++++++--------- modules/globebrowsing/other/tileprovider.h | 6 +- .../globebrowsing/rendering/patchrenderer.cpp | 16 +-- 6 files changed, 86 insertions(+), 81 deletions(-) diff --git a/modules/globebrowsing/globes/chunk.cpp b/modules/globebrowsing/globes/chunk.cpp index 9ee0df61b1..7044d95971 100644 --- a/modules/globebrowsing/globes/chunk.cpp +++ b/modules/globebrowsing/globes/chunk.cpp @@ -130,7 +130,9 @@ namespace openspace { else return DO_NOTHING; } - + void Chunk::render(const RenderData& data) const { + _owner->getPatchRenderer().renderChunk(*this, data); + } } // namespace openspace diff --git a/modules/globebrowsing/globes/chunk.h b/modules/globebrowsing/globes/chunk.h index 5582d90f49..a5f095e1f2 100644 --- a/modules/globebrowsing/globes/chunk.h +++ b/modules/globebrowsing/globes/chunk.h @@ -54,6 +54,7 @@ namespace openspace { /// Updates chunk internally and returns a desired level Status update(const RenderData& data); + void render(const RenderData& data) const; const GeodeticPatch& surfacePatch() const; ChunkedLodGlobe* const owner() const; diff --git a/modules/globebrowsing/globes/chunknode.cpp b/modules/globebrowsing/globes/chunknode.cpp index cba8d660dd..60d70ddebf 100644 --- a/modules/globebrowsing/globes/chunknode.cpp +++ b/modules/globebrowsing/globes/chunknode.cpp @@ -119,15 +119,15 @@ void ChunkNode::renderReversedBreadthFirst(const RenderData& data) { } } } + while (S.size() > 0) { - S.top()->renderThisChunk(data); + S.top()->renderThisChunk(data); S.pop(); } } void ChunkNode::renderThisChunk(const RenderData& data) { - ChunkRenderer& patchRenderer = _chunk.owner()->getPatchRenderer(); - patchRenderer.renderChunk(_chunk, data); + _chunk.render(data); ChunkNode::renderedChunks++; } diff --git a/modules/globebrowsing/other/tileprovider.cpp b/modules/globebrowsing/other/tileprovider.cpp index d4e6a9c691..601c9ee660 100644 --- a/modules/globebrowsing/other/tileprovider.cpp +++ b/modules/globebrowsing/other/tileprovider.cpp @@ -135,90 +135,88 @@ namespace openspace { } - Tile TileProvider::getMostHiResTile(ChunkIndex chunkIndex) { - std::shared_ptr tex = nullptr; - glm::vec2 uvOffset(0, 0); - glm::vec2 uvScale(1, 1); - - // Check if we are trying to get a texture for a very small patch. - // In that case, use the biggest one defined for the dataset. - int maximumAllowedLevel = - _gdalDataSet->GetRasterBand(1)->GetOverviewCount() - 1; - int levelInDataset = chunkIndex.level + _tileLevelDifference; - int timesToStepUp = levelInDataset - maximumAllowedLevel; - for (int i = 0; i < timesToStepUp; i++) - { - uvScale *= 0.5; - uvOffset *= 0.5; - - if (chunkIndex.isEastChild()) { - uvOffset.x += 0.5; - } - - // In OpenGL, positive y direction is up - if (chunkIndex.isNorthChild()) { - uvOffset.y += 0.5; - } + Tile TileProvider::getHighestResolutionTile(ChunkIndex chunkIndex) { + TileUvTransform uvTransform; + uvTransform.uvOffset = glm::vec2(0, 0); + uvTransform.uvScale = glm::vec2(1, 1); + int numOverviews = _gdalDataSet->GetRasterBand(1)->GetOverviewCount(); + int maximumLevel = numOverviews - 1 - _tileLevelDifference; + while(chunkIndex.level > maximumLevel){ + transformFromParent(chunkIndex, uvTransform); chunkIndex = chunkIndex.parent(); } - // We also need to check if the wanted texture is available. If not, go up a level - while (true) { - tex = getOrStartFetchingTile(chunkIndex); - - if (tex != nullptr) { - break; - } - - if (chunkIndex.level <= 1) { - tex = getDefaultTexture(); - break; - } - - // If we have a parent, calculate the UV offset and scale from the chunkIndex - else { - uvScale *= 0.5; - uvOffset *= 0.5; - - if (chunkIndex.isEastChild()) { - uvOffset.x += 0.5; - } - - // In OpenGL, positive y direction is up - if (chunkIndex.isNorthChild()) { - uvOffset.y += 0.5; - } - - chunkIndex = chunkIndex.parent(); - } - } - - return{ tex, {uvOffset, uvScale } }; + return getOrEnqueueHighestResolutionTile(chunkIndex, uvTransform); } + Tile TileProvider::getOrEnqueueHighestResolutionTile(const ChunkIndex& chunkIndex, + TileUvTransform& uvTransform) + { + HashKey key = chunkIndex.hashKey(); + if (_tileCache.exist(key)) { + return { _tileCache.get(key), uvTransform }; + } + else if (chunkIndex.level <= 1) { + return { getDefaultTexture(), uvTransform }; + } + else { + // We don't have the tile for the requested level + // --> check if the parent has a tile we can use + transformFromParent(chunkIndex, uvTransform); + Tile tile = getOrEnqueueHighestResolutionTile(chunkIndex.parent(), uvTransform); + + // As we didn't have this tile, push it to the request queue + // post order enqueueing tiles --> enqueue tiles at low levels first + enqueueTileRequest(chunkIndex); + + return tile; + } + } + + + + void TileProvider::transformFromParent(const ChunkIndex& chunkIndex, TileUvTransform& uv) const { + uv.uvOffset *= 0.5; + uv.uvScale *= 0.5; + + if (chunkIndex.isEastChild()) { + uv.uvOffset.x += 0.5; + } + + // In OpenGL, positive y direction is up + if (chunkIndex.isNorthChild()) { + uv.uvOffset.y += 0.5; + } + } + + std::shared_ptr TileProvider::getOrStartFetchingTile(ChunkIndex chunkIndex) { - HashKey hashkey = chunkIndex.hashKey(); - if (_tileCache.exist(hashkey)) { return _tileCache.get(hashkey); } else { - bool tileHasBeenQueued = _queuedTileRequests.find(hashkey) != _queuedTileRequests.end(); - if (!tileHasBeenQueued) { - // enque load job - std::shared_ptr job = std::shared_ptr( - new TextureTileLoadJob(this, chunkIndex)); - - _tileLoadManager.enqueueJob(job); - - _queuedTileRequests.insert(hashkey); - } + enqueueTileRequest(chunkIndex); return nullptr; } } + bool TileProvider::enqueueTileRequest(const ChunkIndex& chunkIndex) { + HashKey key = chunkIndex.hashKey(); + bool tileHasBeenQueued = _queuedTileRequests.find(key) != _queuedTileRequests.end(); + if (!tileHasBeenQueued) { + // enque load job + std::shared_ptr job = std::shared_ptr( + new TextureTileLoadJob(this, chunkIndex)); + + _tileLoadManager.enqueueJob(job); + + _queuedTileRequests.insert(key); + } + return !tileHasBeenQueued; + } + std::shared_ptr TileProvider::getDefaultTexture() { diff --git a/modules/globebrowsing/other/tileprovider.h b/modules/globebrowsing/other/tileprovider.h index 6a5f46dcf8..422c702db6 100644 --- a/modules/globebrowsing/other/tileprovider.h +++ b/modules/globebrowsing/other/tileprovider.h @@ -74,7 +74,7 @@ namespace openspace { int framesUntilRequestFlush); ~TileProvider(); - Tile getMostHiResTile(ChunkIndex chunkIndex); + Tile getHighestResolutionTile(ChunkIndex chunkIndex); std::shared_ptr getOrStartFetchingTile(ChunkIndex chunkIndex); std::shared_ptr getDefaultTexture(); @@ -91,8 +91,11 @@ namespace openspace { ////////////////////////////////////////////////////////////////////////////////// // Helper functions // ////////////////////////////////////////////////////////////////////////////////// + Tile getOrEnqueueHighestResolutionTile(const ChunkIndex& ci, TileUvTransform& uvTransform); + void transformFromParent(const ChunkIndex& ci, TileUvTransform& uv) const; + /** Fetches all the needeed texture data from the GDAL dataset. */ @@ -105,6 +108,7 @@ namespace openspace { std::shared_ptr initializeTexture( std::shared_ptr uninitedTexture); + bool enqueueTileRequest(const ChunkIndex& ci); void clearRequestQueue(); diff --git a/modules/globebrowsing/rendering/patchrenderer.cpp b/modules/globebrowsing/rendering/patchrenderer.cpp index ac406d91cc..b99d87e7ce 100644 --- a/modules/globebrowsing/rendering/patchrenderer.cpp +++ b/modules/globebrowsing/rendering/patchrenderer.cpp @@ -172,7 +172,7 @@ namespace openspace { { auto tileProvider = it->second; // Get the texture that should be used for rendering - Tile tile = tileProvider->getMostHiResTile(chunk.index()); + Tile tile = tileProvider->getHighestResolutionTile(chunk.index()); TileDepthTransform depthTransform = tileProvider->depthTransform(); // The texture needs a unit to sample from @@ -206,7 +206,7 @@ namespace openspace { { auto tileProvider = it->second; // Get the texture that should be used for rendering - Tile tile = tileProvider->getMostHiResTile(chunk.index()); + Tile tile = tileProvider->getHighestResolutionTile(chunk.index()); // The texture needs a unit to sample from texUnitColor.activate(); @@ -271,7 +271,7 @@ namespace openspace { //auto tileProviderHeight = heightMapProviders.begin()->second; // Get the textures that should be used for rendering - Tile heightTile = tileProviderHeight->getMostHiResTile(chunk.index()); + Tile heightTile = tileProviderHeight->getHighestResolutionTile(chunk.index()); // Bind and use the texture @@ -291,7 +291,7 @@ namespace openspace { // Pick the first color texture auto colorTextureProviders = _tileProviderManager->colorTextureProviders(); auto tileProviderColor = colorTextureProviders.begin()->second; - Tile colorTile = tileProviderColor->getMostHiResTile(chunk.index()); + Tile colorTile = tileProviderColor->getHighestResolutionTile(chunk.index()); // Bind and use the texture @@ -364,7 +364,7 @@ namespace openspace { { auto tileProvider = it->second; // Get the texture that should be used for rendering - Tile tile = tileProvider->getMostHiResTile(chunk.index()); + Tile tile = tileProvider->getHighestResolutionTile(chunk.index()); TileDepthTransform depthTransform = tileProvider->depthTransform(); // The texture needs a unit to sample from @@ -398,7 +398,7 @@ namespace openspace { { auto tileProvider = it->second; // Get the texture that should be used for rendering - Tile tile = tileProvider->getMostHiResTile(chunk.index()); + Tile tile = tileProvider->getHighestResolutionTile(chunk.index()); // The texture needs a unit to sample from texUnitColor.activate(); @@ -502,7 +502,7 @@ namespace openspace { auto tileProviderHeight = heightMapProviders.begin()->second; // Get the textures that should be used for rendering - Tile heightTile = tileProviderHeight->getMostHiResTile(chunk.index()); + Tile heightTile = tileProviderHeight->getHighestResolutionTile(chunk.index()); // Bind and use the texture ghoul::opengl::TextureUnit texUnitHeight; @@ -519,7 +519,7 @@ namespace openspace { // Pick the first color texture auto colorTextureProviders = _tileProviderManager->colorTextureProviders(); auto tileProviderColor = colorTextureProviders.begin()->second; - Tile colorTile = tileProviderColor->getMostHiResTile(chunk.index()); + Tile colorTile = tileProviderColor->getHighestResolutionTile(chunk.index()); // Bind and use the texture From e03258dab0c9fa7b9637012ff6d769b5f49d944c Mon Sep 17 00:00:00 2001 From: Erik Broberg Date: Mon, 16 May 2016 20:27:26 -0400 Subject: [PATCH 5/6] removed out-dated tests --- tests/test_texturetileset.inl | 143 -------------------------------- tests/test_twmstileprovider.inl | 51 ------------ 2 files changed, 194 deletions(-) delete mode 100644 tests/test_texturetileset.inl delete mode 100644 tests/test_twmstileprovider.inl diff --git a/tests/test_texturetileset.inl b/tests/test_texturetileset.inl deleted file mode 100644 index cc5b4dfa65..0000000000 --- a/tests/test_texturetileset.inl +++ /dev/null @@ -1,143 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2016 * - * * - * 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 "gtest/gtest.h" - -#include - -#define _USE_MATH_DEFINES -#include -#include - -class TextureTileSetTest : public testing::Test {}; - -using namespace openspace; - -TEST_F(TextureTileSetTest, getTileIndexLevel) { - - // Create a tile set with maximum depth 0 - TextureTileSet tileSet(Geodetic2(M_PI, M_PI * 2), Geodetic2(M_PI / 2, - M_PI), 0); - - GeodeticPatch patch(Geodetic2(0, 0), Geodetic2(M_PI / 16, M_PI / 8)); - GeodeticTileIndex tileIndex0 = tileSet.getTileIndex(patch); - - // Maximum level is 0 - ASSERT_EQ(tileIndex0.level, 0); - - - // Create a tile set with maximum depth 10 - TextureTileSet tileSetDepth10(Geodetic2(M_PI, M_PI * 2), Geodetic2(M_PI / 2, - M_PI), 10); - - // A big tile that covers the whole latlon space - GeodeticPatch patchBig(Geodetic2(0, 0), Geodetic2(M_PI / 2, M_PI)); - tileIndex0 = tileSetDepth10.getTileIndex(patchBig); - - // Should return 0 since the tile covers the whole latlon space - ASSERT_EQ(tileIndex0.level, 0); - - - // An edge case tile that covers a fourth of the latlon space - GeodeticPatch patchEdgeCase(Geodetic2(0, 0), Geodetic2(M_PI / 4, M_PI / 2)); - GeodeticTileIndex tileIndex1 = tileSetDepth10.getTileIndex(patchEdgeCase); - - // Now it can go up a level - ASSERT_EQ(tileIndex1.level, 1); - - - // Bigger than the edge case - GeodeticPatch patchEdgeCaseBigger(Geodetic2(0, 0), Geodetic2(M_PI / 4 + 0.001, M_PI / 2 + 0.001)); - tileIndex0 = tileSetDepth10.getTileIndex(patchEdgeCaseBigger); - - // Should return 0 again - ASSERT_EQ(tileIndex0.level, 0); -} - -TEST_F(TextureTileSetTest, getTileIndexXY) { - - // Create a tile set with maximum depth 0 - TextureTileSet tileSet(Geodetic2(M_PI, M_PI * 2), Geodetic2(M_PI / 2, - M_PI), 0); - - GeodeticPatch patch(Geodetic2(0, 0), Geodetic2(M_PI / 16, M_PI / 8)); - GeodeticTileIndex tileIndex0 = tileSet.getTileIndex(patch); - - // Maximum level is 0 so the x y indices should also be 0 - ASSERT_EQ(tileIndex0.x, 0); - ASSERT_EQ(tileIndex0.y, 0); - - - // Create a tile set with maximum depth 10 - TextureTileSet tileSetDepth10(Geodetic2(M_PI, M_PI * 2), Geodetic2(M_PI / 2, - M_PI), 10); - - // A big tile that covers the whole latlon space - GeodeticPatch patchBig(Geodetic2(0, 0), Geodetic2(M_PI / 2, M_PI)); - tileIndex0 = tileSetDepth10.getTileIndex(patchBig); - - // Should return 0 in x and y since the tile covers the whole latlon space - ASSERT_EQ(tileIndex0.x, 0); - ASSERT_EQ(tileIndex0.y, 0); - - - // A tile that covers a fourth of the latlon space - GeodeticPatch patchEdgeCase(Geodetic2(0, 0), Geodetic2(M_PI / 4, M_PI / 2)); - GeodeticTileIndex tileIndex1 = tileSetDepth10.getTileIndex(patchEdgeCase); - - // Now it can go up a level (1) - // Since the position is 0, 0 it has 0, 0, in x, y index - ASSERT_EQ(tileIndex1.x, 0); - ASSERT_EQ(tileIndex1.y, 0); - - - // A smaller edge case tile - GeodeticPatch patchEdgeCase2(Geodetic2(0, 0), Geodetic2(M_PI / 8, M_PI / 4)); - GeodeticTileIndex tileIndex11 = tileSetDepth10.getTileIndex(patchEdgeCase2); - - // Now it can go up two levels (2) - // Since the position is 0, 0 it now has 1, 1, in x, y index - // (north west corner is in that tile) - ASSERT_EQ(tileIndex11.x, 1); - ASSERT_EQ(tileIndex11.y, 1); -} - - -TEST_F(TextureTileSetTest, getUvTransformationPatchToTile) { - // Create a tile set with maximum depth 0 - TextureTileSet tileSet(Geodetic2(M_PI, M_PI * 2), Geodetic2(M_PI / 2, -M_PI), 0); - - // Create a patch that covers the whole latlon space - GeodeticPatch patch(Geodetic2(0, 0), Geodetic2(M_PI / 2, M_PI)); - - // Should be a 1:1 mapping - glm::mat3 patchToTileTransform = - tileSet.getUvTransformationPatchToTile(patch, { 0, 0, 0 }); - - ASSERT_EQ(patchToTileTransform, glm::mat3(1)); - - // Create a smaller patch in the upper west side - patch = GeodeticPatch(Geodetic2(M_PI / 4, - M_PI / 2), Geodetic2(M_PI / 4, M_PI / 2)); - patchToTileTransform = - tileSet.getUvTransformationPatchToTile(patch, { 0,0,0 }); - - glm::vec2 uvPatchSpace = glm::vec2(0, 0); - glm::vec2 uvTileSpace = glm::vec2(patchToTileTransform * glm::vec3(uvPatchSpace, 1)); -} diff --git a/tests/test_twmstileprovider.inl b/tests/test_twmstileprovider.inl deleted file mode 100644 index 7896f5fd4f..0000000000 --- a/tests/test_twmstileprovider.inl +++ /dev/null @@ -1,51 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2016 * - * * - * 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 "gtest/gtest.h" - -#include -#include - -#define _USE_MATH_DEFINES -#include -#include - -class TWMSTileProviderTest : public testing::Test {}; - -using namespace openspace; - -TEST_F(TWMSTileProviderTest, Simple) { - - - TileProvider* tileProvider = new TileProvider(); - - GeodeticTileIndex tileIndex = { 0, 0, 0 }; - tileProvider->getTile(tileIndex); - - using namespace std::chrono_literals; - std::this_thread::sleep_for(2s); - - std::cout << "exiting" << std::endl; - -} From a9d34d4d259915c791c08e82f44a8a6db959cb2e Mon Sep 17 00:00:00 2001 From: Erik Broberg Date: Mon, 16 May 2016 20:28:16 -0400 Subject: [PATCH 6/6] Added thread pool class and a simple unit test --- modules/globebrowsing/CMakeLists.txt | 2 + modules/globebrowsing/other/threadpool.cpp | 120 +++++++++++++++++++++ modules/globebrowsing/other/threadpool.h | 81 ++++++++++++++ tests/main.cpp | 9 +- tests/test_chunknode.inl | 2 +- tests/test_threadpool.inl | 59 ++++++++++ 6 files changed, 268 insertions(+), 5 deletions(-) create mode 100644 modules/globebrowsing/other/threadpool.cpp create mode 100644 modules/globebrowsing/other/threadpool.h create mode 100644 tests/test_threadpool.inl diff --git a/modules/globebrowsing/CMakeLists.txt b/modules/globebrowsing/CMakeLists.txt index e5c31e27ed..3c93f41d47 100644 --- a/modules/globebrowsing/CMakeLists.txt +++ b/modules/globebrowsing/CMakeLists.txt @@ -55,6 +55,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/other/gdaldataconverter.h ${CMAKE_CURRENT_SOURCE_DIR}/other/lrucache.h ${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentjobmanager.h + ${CMAKE_CURRENT_SOURCE_DIR}/other/threadpool.h ${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentqueue.h ${CMAKE_CURRENT_SOURCE_DIR}/other/tileprovidermanager.h ${CMAKE_CURRENT_SOURCE_DIR}/other/layeredtextureshaderprovider.h @@ -91,6 +92,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/other/gdaldataconverter.inl ${CMAKE_CURRENT_SOURCE_DIR}/other/lrucache.inl ${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentjobmanager.inl + ${CMAKE_CURRENT_SOURCE_DIR}/other/threadpool.cpp ${CMAKE_CURRENT_SOURCE_DIR}/other/tileprovidermanager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/other/layeredtextureshaderprovider.cpp diff --git a/modules/globebrowsing/other/threadpool.cpp b/modules/globebrowsing/other/threadpool.cpp new file mode 100644 index 0000000000..8712ed65b6 --- /dev/null +++ b/modules/globebrowsing/other/threadpool.cpp @@ -0,0 +1,120 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + + + +namespace openspace { + + Worker::Worker(ThreadPool& pool) + : pool(pool) + { + + } + + void Worker::operator()() { + std::function task; + while (true) { + + // acquire lock + { + std::unique_lock lock(pool.queue_mutex); + + // look for a work item + while (!pool.stop && pool.tasks.empty()) { + // if there are none wait for notification + pool.condition.wait(lock); + } + + if (pool.stop) { // exit if the pool is stopped + return; + } + + // get the task from the queue + task = pool.tasks.front(); + pool.tasks.pop_front(); + + }// release lock + + // execute the task + task(); + } + + + } + + + + + + ThreadPool::ThreadPool(size_t numThreads) + : stop(false) + { + for (size_t i = 0; i < numThreads; ++i) { + workers.push_back(std::thread(Worker(*this))); + } + } + + // the destructor joins all threads + ThreadPool::~ThreadPool() { + // stop all threads + stop = true; + condition.notify_all(); + + // join them + for (size_t i = 0; i < workers.size(); ++i) { + workers[i].join(); + } + } + + + // add new work item to the pool + void ThreadPool::enqueue(std::function f) { + { // acquire lock + std::unique_lock lock(queue_mutex); + + // add the task + tasks.push_back(f); + } // release lock + + // wake up one thread + std::cout << "Notify one thread" << std::endl; + condition.notify_one(); + } + +} // namespace openspace \ No newline at end of file diff --git a/modules/globebrowsing/other/threadpool.h b/modules/globebrowsing/other/threadpool.h new file mode 100644 index 0000000000..73ae6a2d6d --- /dev/null +++ b/modules/globebrowsing/other/threadpool.h @@ -0,0 +1,81 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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. * + ****************************************************************************************/ + +#ifndef __THREAD_POOL_H__ +#define __THREAD_POOL_H__ + +#include +#include +#include +#include +#include +#include + +#include + +#include + + + +// Implementatin based on http://progsch.net/wordpress/?p=81 + +namespace openspace { + + + class ThreadPool; + + class Worker { + public: + Worker(ThreadPool& pool); + void operator()(); + private: + ThreadPool& pool; + }; + + class ThreadPool { + public: + ThreadPool(size_t numThreads); + ~ThreadPool(); + + void enqueue(std::function f); + + private: + friend class Worker; + + std::vector workers; + + std::deque> tasks; + + std::mutex queue_mutex; + std::condition_variable condition; + + bool stop; + }; + + +} // namespace openspace + + + +#endif // __THREAD_POOL_H__ diff --git a/tests/main.cpp b/tests/main.cpp index c52f078c77..33ac9906c1 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -33,15 +33,16 @@ //#include //#include //#include -#include -#include +//#include +//#include +#include //#include //#include -#include +//#include //#include //#include -#include +//#include //#include //#include diff --git a/tests/test_chunknode.inl b/tests/test_chunknode.inl index 72e30d36d0..35f12a0890 100644 --- a/tests/test_chunknode.inl +++ b/tests/test_chunknode.inl @@ -25,7 +25,7 @@ #include "gtest/gtest.h" #include -#include +#include #include #include diff --git a/tests/test_threadpool.inl b/tests/test_threadpool.inl new file mode 100644 index 0000000000..5d50c15f09 --- /dev/null +++ b/tests/test_threadpool.inl @@ -0,0 +1,59 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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 "gtest/gtest.h" + +#include + + +#define _USE_MATH_DEFINES +#include +#include + + + + +class ThreadPoolTest : public testing::Test {}; + + +using namespace openspace; +using namespace std::chrono_literals; + + + +TEST_F(ThreadPoolTest, Basic) { + ThreadPool pool(5); + + int val = 0; + + for (int i = 0; i < 10; ++i) { + pool.enqueue([&val, i]() { + std::this_thread::sleep_for(std::chrono::milliseconds(100 + 10*i)); + val++; + }); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + EXPECT_EQ(10, val) << "10 tasks taking 100 to 190 ms on 5 threads should take less than 1000 ms"; +}