Feature/globebrowsing optimization (#310)

* Simplest possible PBO implementation.

* Add PBO class

* TileLoadJob owns raw tile data

* Working on a soluton to cache textures and reuse them

* PBO and cached textures working for one texture type. Color textures.

* Threadpool for tile requests uses LRU cache as queue

* Remove framesUntilRequestFlush

* Clean up

* Clean up

* Use prioritizing concurrent job manager

* Use TileTextureInitData to initialize RawTileDataReader.

* Class TextureContainer owns the textures to use for tiles.

* Using TileTextureInitData to determine if new caches need to be created.

* Remove WriteDataDescription

* Remove TileDataLayout

* Rendering many different layer types again

* TileProviderByLevel gives layergroup id to tile providers

* Comment away use of PBO

* Erase unfinished requests to make room for new ones

* Enable choice of PBO or not.

* Enable resetting of asynctiledataprovider

* Add the ability to use PBO and also load to CPU

* Update ghoul

* Solve culling issue.

* Texture pointer of Tile is now a raw pointer. Currently break single image tile provider and text tile provider.

* Add gpudata

* Move fetching of shader preprocessing data to LayerManager

* No comparisons to determine shader recompilation.

* Show the tile cache size in the GUI

* Clean up and comment.

* Solve bug where float is interpreted as NaN

* Enable ability to blend between layers again

* Fix single image provider

* Fix windows build error

* Fix OSX compile issue.

* Some clean up

* Showing correct texture data size

* Enable use of text tile providers again. No backgroupd image path however.

* Change cache size from GUI

* Clean up

* Solve osx compilation error.

* Update ghoul

* Make it possible to switch between PBO and not during runtime.

* Enable resetting of tile datasets

* change function module in moduleengine to identify module by name

* MemoryAwareTileCache is no longer a singleton

* Update ownership of properties for globe browsing

* Logging info about resetting tile reader.

* Logging info

* Fix requested changes

* Fix some compile warnings.

* Fix compilation warnings

* Add ability to blend values with blend parameter. Also define settings through lua dict.

* Fix some comments on pull request.

* Change formatting

* Change formatting

* Change formatting

* Fix pull request comments.

* Those are details

* Make Mercury great again.

* Make Earth great again.

* Solve conflict

* Test to sometimes use valueblending and sometimes not

* Not always use value blending

* Update ghoul

* Change from auto to explicit type.

* Update test for LRU Cache

* Include algorithm.
This commit is contained in:
Kalle Bladin
2017-05-30 15:37:05 +02:00
committed by GitHub
parent f6da2b6472
commit f51f293989
128 changed files with 3039 additions and 1930 deletions

View File

@@ -64,8 +64,6 @@ end
function postInitialization()
openspace.printInfo("Setting default values")
openspace.setInteractionMode('GlobeBrowsing')
openspace.setPropertyValue("MilkyWay.renderable.transparency", 0.55)
openspace.setPropertyValue("MilkyWay.renderable.segments", 50)
@@ -74,8 +72,8 @@ function postInitialization()
openspace.setPropertyValue("SunMarker.renderable.enabled", false)
openspace.setPropertyValue("Earth.RenderableGlobe.atmosphere", true)
openspace.setPropertyValue("Earth.RenderableGlobe.Layers.NightLayers.Earth at Night 2012.settings.gamma", 1.8)
openspace.setPropertyValue("Earth.RenderableGlobe.Layers.NightLayers.Earth at Night 2012.settings.multiplier", 10)
openspace.setPropertyValue("Earth.RenderableGlobe.Debug.levelByProjectedAreaElseDistance", false)
openspace.setPropertyValue("Earth.RenderableGlobe.Layers.ColorLayers.blendTileLevels", true)
openspace.resetCameraDirection()
@@ -88,8 +86,8 @@ return {
CommonFolder = "common",
Camera = {
Focus = "Earth",
Position = {138530625167.228241, 42217005217.825005, -46336405755.934372},
Rotation = {0.633883, 0.492158, -0.123913, -0.583625},
Position = {30000000, 0, 0},
Rotation = {0.758797, 0.221490, -0.605693, -0.091135},
},
Modules = {

View File

@@ -56,31 +56,27 @@ return {
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {
{
Name = "ESRI VIIRS Combo",
Type = "ByLevel",
LevelTileProviders = {
{
MaxLevel = 3,
TileProvider = { Type = "Temporal", FilePath = "map_service_configs/GIBS/Temporal_VIIRS_SNPP_CorrectedReflectance_TrueColor.xml", },
TileProvider = {
Type = "Temporal",
Name = "Temporal VIIRS SNPP",
FilePath = "map_service_configs/GIBS/Temporal_VIIRS_SNPP_CorrectedReflectance_TrueColor.xml", },
},
{
MaxLevel = 22,
TileProvider = { FilePath = "map_service_configs/ESRI/ESRI_Imagery_World_2D.wms" },
MaxLevel = 22,
TileProvider = {
Name = "ESRI Imagery World 2D",
FilePath = "map_service_configs/ESRI/ESRI_Imagery_World_2D.wms"
},
},
},
Enabled = true,
},
{
Name = "ESRI Imagery World",
FilePath = "map_service_configs/ESRI/ESRI_Imagery_World_2D.wms"
},
{
Type = "Temporal",
Name = "Temporal VIIRS SNPP",
FilePath = "map_service_configs/GIBS/Temporal_VIIRS_SNPP_CorrectedReflectance_TrueColor.xml",
},
{
Type = "Temporal",
Name = "Temporal_GHRSST_L4_MUR_Sea_Surface_Temperature",
@@ -90,21 +86,11 @@ return {
Type = "Temporal",
Name = "Temporal_AMSR2_GCOM_W1_Sea_Ice_Concentration",
FilePath = "map_service_configs/GIBS/Temporal_AMSR2_GCOM_W1_Sea_Ice_Concentration.xml",
},
-- {
-- Type = "SingleImage",
-- Name = "Debug Tiles",
-- FilePath = "../../debugglobe/textures/test_tile.png",
-- },
},
{
Name = "BMNG",
FilePath = "map_service_configs/Utah/Bmng.wms"
}
-- {
-- Type = "Temporal",
-- Name = "NOAA RT",
-- FilePath = "map_service_configs/other/noaa_rt.xml"
-- }
},
GrayScaleLayers = { },
GrayScaleColorOverlays = { },
@@ -113,6 +99,11 @@ return {
Name = "Earth at Night 2012",
FilePath = "map_service_configs/GIBS/VIIRS_CityLights_2012.xml",
Enabled = true,
Settings = {
Opacity = 1.0,
Gamma = 1.5,
Multiplier = 15.0,
},
},
{
Type = "Temporal",
@@ -154,20 +145,6 @@ return {
Radii = earthEllipsoid,
BackgroundImagePath = "../arrows.png",
},
--[[{
Name = "Test",
Type = "LevelSpecific",
LevelTileProviders = {
{
MaxLevel = 5,
TileProvider = { Type = "TileIndex" },
},
{
MaxLevel = 7,
TileProvider = { Type = "SingleImage", FilePath = "../../debugglobe/textures/test_tile.png",},
},
},
},]]
},
HeightLayers = {
{
@@ -175,7 +152,6 @@ return {
FilePath = "map_service_configs/ESRI/TERRAIN.wms",
Enabled = true,
TilePixelSize = 64,
DoPreProcessing = true,
},
},
},

View File

@@ -1,6 +1,6 @@
<OpenSpaceTemporalGDALDataset>
<OpenSpaceTimeStart>2015-11-24</OpenSpaceTimeStart>
<OpenSpaceTimeEnd></OpenSpaceTimeEnd>
<OpenSpaceTimeEnd>Yesterday</OpenSpaceTimeEnd>
<OpenSpaceTimeResolution>1d</OpenSpaceTimeResolution>
<OpenSpaceTimeIdFormat>YYYY-MM-DD</OpenSpaceTimeIdFormat>
<GDAL_WMS>

View File

@@ -7,7 +7,7 @@
<UpperLeftY>90</UpperLeftY>
<LowerRightX>396.0</LowerRightX>
<LowerRightY>-198</LowerRightY>
<TileLevel>8</TileLevel>
<TileLevel>7</TileLevel>
<TileCountX>2</TileCountX>
<TileCountY>1</TileCountY>
<YOrigin>top</YOrigin>

View File

@@ -38,18 +38,8 @@ return {
Layers = {
ColorLayers = {
{
Name = "Viking combo",
Type = "ByLevel",
LevelTileProviders = {
{
MaxLevel = 3,
TileProvider = { FilePath = "textures/mars.jpg", },
},
{
MaxLevel = 22,
TileProvider = { FilePath = "map_service_configs/MARS_Viking_MDIM21.xml" },
},
},
Name = "Viking",
FilePath = "map_service_configs/MARS_Viking_MDIM21.xml",
Enabled = true,
},
-- {
@@ -157,7 +147,7 @@ return {
Name = "West Candor Chasma",
FilePath = "map_datasets/CTX/West_Candor_Chasma_DEM_longlat_global.vrt",
--Enabled = true,
MinimumPixelSize = 90,
TilePixelSize = 90,
DoPreProcessing = true,
},
{

View File

@@ -1,101 +0,0 @@
return {
-- Barycenter module
{
Name = "MercuryBarycenter",
Parent = "SolarSystemBarycenter",
Transform = {
Translation = {
Type = "SpiceTranslation",
Body = "MERCURY",
Observer = "SUN",
Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp"
},
},
},
-- RenderableGlobe module
{
Name = "Mercury",
Parent = "MercuryBarycenter",
Transform = {
Rotation = {
Type = "SpiceRotation",
SourceFrame = "IAU_MERCURY",
DestinationFrame = "GALACTIC",
},
Scale = {
Type = "StaticScale",
Scale = 1,
},
},
Renderable = {
Type = "RenderableGlobe",
Radii = {2439700, 2439700.0, 2439700.0},
Frame = "IAU_MERCURY",
Body = "MERCURY",
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {
{
Name = "Simple Texture",
FilePath = "textures/mercury.jpg",
Enabled = true,
TilePixelSize = 256,
},
{
Name = "Messenger_Mosaic",
FilePath = "map_service_configs/Utah/MessengerMosaic.wms"
}
--[[
{
Name = "On Mercury Color",
FilePath = "map_service_configs/OnMercuryColor.xml",
Enabled = true,
},
{
Name = "On Mercury Image",
FilePath = "map_service_configs/OnMercuryImage.xml",
},
]]
},
GrayScaleLayers = {
{
Name = "Messenger_MDIS",
FilePath = "map_service_configs/Utah/MessengerMDIS.wms"
}
},
GrayScaleColorOverlays = { },
NightLayers = { },
WaterMasks = { },
ColorOverlays = { },
HeightLayers = {
--[[
{
Name = "On Mercury Height",
FilePath = "map_service_configs/OnMercuryElevationGaskell.xml",
Enabled = true,
},
]]
},
},
},
},
-- Trail module
{
Name = "MercuryTrail",
Parent = "SolarSystemBarycenter",
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "SpiceTranslation",
Body = "MERCURY",
Observer = "SUN",
},
Color = {0.6, 0.5, 0.5 },
Period = 87.968,
Resolution = 100
}
}
}

View File

@@ -30,6 +30,9 @@
#include <openspace/util/openspacemodule.h>
#include <ghoul/misc/assert.h>
#include <algorithm>
namespace ghoul {
namespace systemcapabilities {
@@ -83,6 +86,25 @@ public:
* \return A list of all registered OpenSpaceModule%s
*/
std::vector<OpenSpaceModule*> modules() const;
/**
* Get the module subclass with given template argument. Requires the module subclass
* to have the public static member variable <code>name</code> which must be equal to
* the name of the module used in its constructor.
* \return a pointer to the module of the given subclass
*/
template <class ModuleSubClass>
ModuleSubClass* module() const {
auto it = std::find_if(_modules.begin(), _modules.end(),
[](const std::unique_ptr<OpenSpaceModule>& module) {
return module->name() == ModuleSubClass::name;
});
if (it != _modules.end()) {
return dynamic_cast<ModuleSubClass*>(it->get());
} else {
return nullptr;
}
}
/**
* Returns the combined minimum OpenGL version. The return value is the maximum

View File

@@ -41,10 +41,10 @@ namespace ghoul { class Dictionary; }
namespace openspace {
namespace documentation { struct Documentation; }
class SceneGraphNode;
namespace documentation { struct Documentation; }
// Notifications:
// SceneGraphFinishedLoading
class Scene : public DocumentationGenerator {

View File

@@ -83,10 +83,12 @@ public:
* program.
* OBS! Users must ensure bind has been called before using this method.
*/
void setValue(ghoul::opengl::ProgramObject* program, std::shared_ptr<ghoul::opengl::Texture> texture){
void setValue(ghoul::opengl::ProgramObject* program, ghoul::opengl::Texture* texture){
_texUnit = std::make_unique<ghoul::opengl::TextureUnit>();
_texUnit->activate();
texture->bind();
if (texture) {
texture->bind();
}
program->setUniform(_uniformLocation, *_texUnit);
}

View File

@@ -27,10 +27,8 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/cache/lrucache.h
${CMAKE_CURRENT_SOURCE_DIR}/cache/lrucache.inl
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawarecacheable.h
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawarelrucache.h
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawarelrucache.inl
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawaretilecache.h
${CMAKE_CURRENT_SOURCE_DIR}/cache/texturecontainer.h
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunk.h
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunknode.h
@@ -61,12 +59,19 @@ set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentjobmanager.h
${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentjobmanager.inl
${CMAKE_CURRENT_SOURCE_DIR}/other/prioritizingconcurrentjobmanager.h
${CMAKE_CURRENT_SOURCE_DIR}/other/prioritizingconcurrentjobmanager.inl
${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentqueue.h
${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentqueue.inl
${CMAKE_CURRENT_SOURCE_DIR}/other/distanceswitch.h
${CMAKE_CURRENT_SOURCE_DIR}/other/pixelbuffer.h
${CMAKE_CURRENT_SOURCE_DIR}/other/pixelbuffercontainer.h
${CMAKE_CURRENT_SOURCE_DIR}/other/pixelbuffercontainer.inl
${CMAKE_CURRENT_SOURCE_DIR}/other/statscollector.h
${CMAKE_CURRENT_SOURCE_DIR}/other/statscollector.inl
${CMAKE_CURRENT_SOURCE_DIR}/other/threadpool.h
${CMAKE_CURRENT_SOURCE_DIR}/other/lruthreadpool.h
${CMAKE_CURRENT_SOURCE_DIR}/other/lruthreadpool.inl
${CMAKE_CURRENT_SOURCE_DIR}/rendering/chunkrenderer.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layershadermanager.h
@@ -81,6 +86,7 @@ set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/rendering/gpu/gputileuvtransform.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layer.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layergroup.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layergroupid.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layermanager.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layerrendersettings.h
@@ -90,16 +96,12 @@ set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtile.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/textureformat.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tile.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledatalayout.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledepthtransform.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tilediskcache.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileindex.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tilemetadata.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileselector.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileuvtransform.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/loadjob/diskcachedtileloadjob.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/loadjob/loadjob.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/loadjob/tileloadjob.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileloadjob.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/cachingtileprovider.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/presentationslideprovider.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/singleimageprovider.h
@@ -110,6 +112,7 @@ set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileprovider.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbyindex.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbylevel.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiletextureinitdata.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/rawtiledatareader.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/gdalrawtiledatareader.h
@@ -121,6 +124,7 @@ set(HEADER_FILES
set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawaretilecache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/cache/texturecontainer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunk.cpp
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunknode.cpp
@@ -145,6 +149,7 @@ set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/meshes/trianglesoup.cpp
${CMAKE_CURRENT_SOURCE_DIR}/other/distanceswitch.cpp
${CMAKE_CURRENT_SOURCE_DIR}/other/pixelbuffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/other/statscollector.cpp
${CMAKE_CURRENT_SOURCE_DIR}/other/threadpool.cpp
@@ -169,12 +174,10 @@ set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tilediskcache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileindex.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tilemetadata.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileselector.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/loadjob/diskcachedtileloadjob.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/loadjob/tileloadjob.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileloadjob.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/cachingtileprovider.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/presentationslideprovider.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/singleimageprovider.cpp
@@ -185,6 +188,7 @@ set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileprovider.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbyindex.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbylevel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiletextureinitdata.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/rawtiledatareader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/gdalrawtiledatareader.cpp

View File

@@ -36,26 +36,49 @@ namespace cache {
* Templated class implementing a Least-Recently-Used Cache.
* <code>KeyType</code> needs to be an enumerable type.
*/
template<typename KeyType, typename ValueType>
template<typename KeyType, typename ValueType, typename HasherType>
class LRUCache {
public:
using Item = std::pair<KeyType, ValueType>;
using Items = std::list<Item>;
/**
* \param size is the maximum size of the cache given in number of cached items.
*/
LRUCache(size_t size);
void put(const KeyType& key, const ValueType& value);
std::vector<Item> putAndFetchPopped(const KeyType& key, const ValueType& value);
void clear();
bool exist(const KeyType& key) const;
/**
* If value exists, the value is bumped to the front of the queue.
* \returns true if value of this key exists.
*/
bool touch(const KeyType& key);
bool isEmpty() const;
ValueType get(const KeyType& key);
/**
* Pops the front of the queue.
*/
Item popMRU();
/**
* Pops the back of the queue.
*/
Item popLRU();
size_t size() const;
private:
void putWithoutCleaning(const KeyType& key, const ValueType& value);
void clean();
std::vector<Item> cleanAndFetchPopped();
std::list<std::pair<KeyType, ValueType>> _itemList;
std::unordered_map<KeyType, decltype(_itemList.begin())> _itemMap;
size_t _cacheSize;
Items _itemList;
std::unordered_map<KeyType, typename Items::const_iterator, HasherType> _itemMap;
size_t _maximumCacheSize;
};
} // namespace cache

View File

@@ -28,36 +28,63 @@ namespace openspace {
namespace globebrowsing {
namespace cache {
template<typename KeyType, typename ValueType>
LRUCache<KeyType, ValueType>::LRUCache(size_t size)
: _cacheSize(size)
template<typename KeyType, typename ValueType, typename HasherType>
LRUCache<KeyType, ValueType, HasherType>::LRUCache(size_t size)
: _maximumCacheSize(size)
{}
template<typename KeyType, typename ValueType>
void LRUCache<KeyType, ValueType>::clear() {
_itemList.erase(_itemList.begin(), _itemList.end());
_itemMap.erase(_itemMap.begin(), _itemMap.end());
template<typename KeyType, typename ValueType, typename HasherType>
void LRUCache<KeyType, ValueType, HasherType>::clear() {
_itemList.clear();
_itemMap.clear();
}
template<typename KeyType, typename ValueType>
void LRUCache<KeyType, ValueType>::put(const KeyType& key, const ValueType& value) {
auto it = _itemMap.find(key);
if (it != _itemMap.end()) {
_itemList.erase(it->second);
_itemMap.erase(it);
}
_itemList.push_front(std::make_pair(key, value));
_itemMap.insert(std::make_pair(key, _itemList.begin()));
template<typename KeyType, typename ValueType, typename HasherType>
void LRUCache<KeyType, ValueType, HasherType>::put(const KeyType& key,
const ValueType& value)
{
putWithoutCleaning(key, value);
clean();
}
template<typename KeyType, typename ValueType>
bool LRUCache<KeyType, ValueType>::exist(const KeyType& key) const {
template<typename KeyType, typename ValueType, typename HasherType>
std::vector<std::pair<KeyType, ValueType>>
LRUCache<KeyType, ValueType, HasherType>::putAndFetchPopped(const KeyType& key,
const ValueType& value)
{
putWithoutCleaning(key, value);
return cleanAndFetchPopped();
}
template<typename KeyType, typename ValueType, typename HasherType>
bool LRUCache<KeyType, ValueType, HasherType>::exist(const KeyType& key) const {
return _itemMap.count(key) > 0;
}
template<typename KeyType, typename ValueType>
ValueType LRUCache<KeyType, ValueType>::get(const KeyType& key) {
template<typename KeyType, typename ValueType, typename HasherType>
bool LRUCache<KeyType, ValueType, HasherType>::touch(const KeyType& key) {
auto it = _itemMap.find(key);
if (it != _itemMap.end()) { // Found in cache
ValueType value = it->second->second;
// Remove from current position
_itemList.erase(it->second);
_itemMap.erase(it);
// Bump to front
_itemList.emplace_front(key, value);
_itemMap.emplace(key, _itemList.begin());
return true;
} else {
return false;
}
}
template<typename KeyType, typename ValueType, typename HasherType>
bool LRUCache<KeyType, ValueType, HasherType>::isEmpty() const {
return _itemMap.size() == 0;
}
template<typename KeyType, typename ValueType, typename HasherType>
ValueType LRUCache<KeyType, ValueType, HasherType>::get(const KeyType& key) {
//ghoul_assert(exist(key), "Key " << key << " must exist");
auto it = _itemMap.find(key);
// Move list iterator pointing to value
@@ -65,20 +92,72 @@ ValueType LRUCache<KeyType, ValueType>::get(const KeyType& key) {
return it->second->second;
}
template<typename KeyType, typename ValueType>
size_t LRUCache<KeyType, ValueType>::size() const {
template<typename KeyType, typename ValueType, typename HasherType>
std::pair<KeyType, ValueType> LRUCache<KeyType, ValueType, HasherType>::popMRU() {
ghoul_assert(_itemList.size() > 0,
"Can not pop from LRU cache. Ensure cache is not empty.");
auto first_it = _itemList.begin();
_itemMap.erase(first_it->first);
std::pair<KeyType, ValueType> toReturn = _itemList.front();
_itemList.pop_front();
return toReturn;
}
template<typename KeyType, typename ValueType, typename HasherType>
std::pair<KeyType, ValueType> LRUCache<KeyType, ValueType, HasherType>::popLRU() {
ghoul_assert(_itemList.size() > 0,
"Can not pop from LRU cache. Ensure cache is not empty.");
auto lastIt = _itemList.end();
lastIt--;
_itemMap.erase(lastIt->first);
std::pair<KeyType, ValueType> toReturn = _itemList.back();
_itemList.pop_back();
return toReturn;
}
template<typename KeyType, typename ValueType, typename HasherType>
size_t LRUCache<KeyType, ValueType, HasherType>::size() const {
return _itemMap.size();
}
template<typename KeyType, typename ValueType>
void LRUCache<KeyType, ValueType>::clean() {
while (_itemMap.size() > _cacheSize) {
auto last_it = _itemList.end(); last_it--;
_itemMap.erase(last_it->first);
template<typename KeyType, typename ValueType, typename HasherType>
void LRUCache<KeyType, ValueType, HasherType>::putWithoutCleaning(const KeyType& key,
const ValueType& value)
{
auto it = _itemMap.find(key);
if (it != _itemMap.end()) {
_itemList.erase(it->second);
_itemMap.erase(it);
}
_itemList.emplace_front(key, value);
_itemMap.emplace(key, _itemList.begin());
}
template<typename KeyType, typename ValueType, typename HasherType>
void LRUCache<KeyType, ValueType, HasherType>::clean() {
while (_itemMap.size() > _maximumCacheSize) {
auto lastIt = _itemList.end();
lastIt--;
_itemMap.erase(lastIt->first);
_itemList.pop_back();
}
}
template<typename KeyType, typename ValueType, typename HasherType>
std::vector<std::pair<KeyType, ValueType>>
LRUCache<KeyType, ValueType, HasherType>::cleanAndFetchPopped()
{
std::vector<std::pair<KeyType, ValueType>> toReturn;
while (_itemMap.size() > _maximumCacheSize) {
auto lastIt = _itemList.end();
lastIt--;
_itemMap.erase(lastIt->first);
toReturn.push_back(_itemList.back());
_itemList.pop_back();
}
return toReturn;
}
} // namespace cache
} // namespace globebrowsing
} // namespace openspace

View File

@@ -1,100 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* 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 <ghoul/misc/assert.h>
namespace openspace {
namespace globebrowsing {
namespace cache {
template<typename KeyType, typename ValueType, typename HasherType>
MemoryAwareLRUCache<KeyType, ValueType, HasherType>::MemoryAwareLRUCache(size_t maximumSize)
: _maximumCacheSize(maximumSize)
, _cacheSize(0)
{}
template<typename KeyType, typename ValueType, typename HasherType>
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::clear() {
_itemList.clear();
_itemMap.clear();
_cacheSize = 0;
}
template<typename KeyType, typename ValueType, typename HasherType>
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::put(const KeyType& key, const ValueType& value) {
auto it = _itemMap.find(key);
if (it != _itemMap.end()) {
_cacheSize -= it->second->second.memoryImpact();
_itemList.erase(it->second);
_itemMap.erase(it);
}
_itemList.emplace_front(key, value);
_itemMap.emplace(key, _itemList.begin());
_cacheSize += _itemList.begin()->second.memoryImpact();
clean();
}
template<typename KeyType, typename ValueType, typename HasherType>
bool MemoryAwareLRUCache<KeyType, ValueType, HasherType>::exist(const KeyType& key) const {
return _itemMap.count(key) > 0;
}
template<typename KeyType, typename ValueType, typename HasherType>
ValueType MemoryAwareLRUCache<KeyType, ValueType, HasherType>::get(const KeyType& key) {
//ghoul_assert(exist(key), "Key " << key << " must exist");
auto it = _itemMap.at(key);
// Move list iterator pointing to value
_itemList.splice(_itemList.begin(), _itemList, it);
return it->second;
}
template<typename KeyType, typename ValueType, typename HasherType>
size_t MemoryAwareLRUCache<KeyType, ValueType, HasherType>::size() const {
return _cacheSize;
}
template<typename KeyType, typename ValueType, typename HasherType>
size_t MemoryAwareLRUCache<KeyType, ValueType, HasherType>::maximumSize() const {
return _maximumCacheSize;
}
template<typename KeyType, typename ValueType, typename HasherType>
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::setMaximumSize(size_t maximumSize) {
_maximumCacheSize = maximumSize;
}
template<typename KeyType, typename ValueType, typename HasherType>
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::clean() {
while (_cacheSize > _maximumCacheSize) {
auto last_it = _itemList.end();
last_it--;
_itemMap.erase(last_it->first);
_cacheSize -= last_it->second.memoryImpact();
_itemList.pop_back();
}
}
} // namespace cache
} // namespace globebrowsing
} // namespace openspace

View File

@@ -24,59 +24,287 @@
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
#include <modules/globebrowsing/rendering/layer/layergroupid.h>
#include <modules/globebrowsing/rendering/layer/layermanager.h>
#include <ghoul/ghoul.h>
#include <ghoul/logging/consolelog.h>
#include <ghoul/misc/invariants.h>
#include <ghoul/systemcapabilities/generalcapabilitiescomponent.h>
#include <numeric>
#include <algorithm>
namespace openspace {
namespace globebrowsing {
namespace cache {
MemoryAwareTileCache* MemoryAwareTileCache::_singleton = nullptr;
std::mutex MemoryAwareTileCache::_mutexLock;
void MemoryAwareTileCache::create(size_t cacheSize) {
std::lock_guard<std::mutex> guard(_mutexLock);
_singleton = new MemoryAwareTileCache(cacheSize);
namespace {
const char* _loggerCat = "MemoryAwareTileCache";
}
void MemoryAwareTileCache::destroy() {
std::lock_guard<std::mutex> guard(_mutexLock);
delete _singleton;
MemoryAwareTileCache::MemoryAwareTileCache()
: PropertyOwner("TileCache")
, _numTextureBytesAllocatedOnCPU(0)
, _cpuAllocatedTileData(
"cpuAllocatedTileData", "CPU allocated tile data (MB)",
1024, // Default
128, // Minimum
2048, // Maximum
1) // Step: One MB
, _gpuAllocatedTileData(
"gpuAllocatedTileData", "GPU allocated tile data (MB)",
1024, // Default
128, // Minimum
2048, // Maximum
1) // Step: One MB
, _tileCacheSize(
"tileCacheSize", "Tile cache size",
1024, // Default
128, // Minimum
2048, // Maximum
1) // Step: One MB
, _applyTileCacheSize("applyTileCacheSize", "Apply tile cache size")
, _clearTileCache("clearTileCache", "Clear tile cache")
, _usePbo("usePbo", "Use PBO", false)
{
createDefaultTextureContainers();
// Properties
_clearTileCache.onChange(
[&]{
clear();
});
_applyTileCacheSize.onChange(
[&]{
setSizeEstimated(_tileCacheSize * 1024 * 1024);
});
_cpuAllocatedTileData.setMaxValue(
CpuCap.installedMainMemory() * 0.25);
_gpuAllocatedTileData.setMaxValue(
CpuCap.installedMainMemory() * 0.25);
_tileCacheSize.setMaxValue(
CpuCap.installedMainMemory() * 0.25);
setSizeEstimated(_tileCacheSize * 1024 * 1024);
_cpuAllocatedTileData.setReadOnly(true);
_gpuAllocatedTileData.setReadOnly(true);
addProperty(_clearTileCache);
addProperty(_applyTileCacheSize);
addProperty(_cpuAllocatedTileData);
addProperty(_gpuAllocatedTileData);
addProperty(_tileCacheSize);
addProperty(_usePbo);
}
MemoryAwareTileCache& MemoryAwareTileCache::ref() {
std::lock_guard<std::mutex> guard(_mutexLock);
ghoul_assert(_singleton, "MemoryAwareTileCache not created");
return *_singleton;
}
MemoryAwareTileCache::~MemoryAwareTileCache()
{ }
void MemoryAwareTileCache::clear() {
std::lock_guard<std::mutex> guard(_mutexLock);
_tileCache.clear();
LINFO("Clearing tile cache");
_numTextureBytesAllocatedOnCPU = 0;
for (std::pair<const TileTextureInitData::HashKey,
TextureContainerTileCache>& p : _textureContainerMap)
{
p.second.first->reset();
p.second.second->clear();
}
LINFO("Tile cache cleared");
}
void MemoryAwareTileCache::createDefaultTextureContainers() {
for (int id = 0; id < layergroupid::NUM_LAYER_GROUPS; id++) {
TileTextureInitData initData =
LayerManager::getTileTextureInitData(layergroupid::ID(id));
assureTextureContainerExists(initData);
}
}
void MemoryAwareTileCache::assureTextureContainerExists(
const TileTextureInitData& initData)
{
TileTextureInitData::HashKey initDataKey = initData.hashKey();
if (_textureContainerMap.find(initDataKey) == _textureContainerMap.end()) {
// For now create 500 textures of this type
_textureContainerMap.emplace(initDataKey,
TextureContainerTileCache(
std::make_unique<TextureContainer>(initData, 500),
std::make_unique<TileCache>(std::numeric_limits<std::size_t>::max())
)
);
}
}
void MemoryAwareTileCache::setSizeEstimated(size_t estimatedSize) {
LINFO("Resetting tile cache size");
ghoul_assert(_textureContainerMap.size() > 0, "Texture containers must exist.");
size_t sumTextureTypeSize = std::accumulate(
_textureContainerMap.cbegin(),
_textureContainerMap.cend(), 0,
[](size_t s, const std::pair<const TileTextureInitData::HashKey,
TextureContainerTileCache>& p)
{
return s + p.second.first->tileTextureInitData().totalNumBytes();
}
);
size_t numTexturesPerType = estimatedSize / sumTextureTypeSize;
resetTextureContainerSize(numTexturesPerType);
LINFO("Tile cache size was reset");
}
void MemoryAwareTileCache::resetTextureContainerSize(size_t numTexturesPerTextureType) {
_numTextureBytesAllocatedOnCPU = 0;
for (std::pair<const TileTextureInitData::HashKey,
TextureContainerTileCache>& p : _textureContainerMap)
{
p.second.first->reset(numTexturesPerTextureType);
p.second.second->clear();
}
}
bool MemoryAwareTileCache::exist(ProviderTileKey key) const {
std::lock_guard<std::mutex> guard(_mutexLock);
return _tileCache.exist(key);
TextureContainerMap::const_iterator result =
std::find_if(_textureContainerMap.cbegin(), _textureContainerMap.cend(),
[&](const std::pair<const TileTextureInitData::HashKey,
TextureContainerTileCache>& p){
return p.second.second->exist(key);
});
return result != _textureContainerMap.cend();
}
Tile MemoryAwareTileCache::get(ProviderTileKey key) {
std::lock_guard<std::mutex> guard(_mutexLock);
return _tileCache.get(key);
TextureContainerMap::const_iterator it =
std::find_if(_textureContainerMap.cbegin(), _textureContainerMap.cend(),
[&](const std::pair<const TileTextureInitData::HashKey,
TextureContainerTileCache>& p){
return p.second.second->exist(key);
});
if (it != _textureContainerMap.cend()) {
return it->second.second->get(key);
}
else {
return Tile::TileUnavailable;
}
}
void MemoryAwareTileCache::put(ProviderTileKey key, Tile tile) {
std::lock_guard<std::mutex> guard(_mutexLock);
_tileCache.put(key, tile);
ghoul::opengl::Texture* MemoryAwareTileCache::getTexture(
const TileTextureInitData& initData)
{
ghoul::opengl::Texture* texture;
// if this texture type does not exist among the texture containers
// it needs to be created
TileTextureInitData::HashKey initDataKey = initData.hashKey();
assureTextureContainerExists(initData);
// Now we know that the texture container exists,
// check if there are any unused textures
texture = _textureContainerMap[initDataKey].first->getTextureIfFree();
// Second option. No more textures available. Pop from the LRU cache
if (!texture) {
Tile oldTile = _textureContainerMap[initDataKey].second->popLRU().second;
// Use the old tile's texture
texture = oldTile.texture();
}
return texture;
}
void MemoryAwareTileCache::setMaximumSize(size_t maximumSize) {
std::lock_guard<std::mutex> guard(_mutexLock);
_tileCache.setMaximumSize(maximumSize);
void MemoryAwareTileCache::createTileAndPut(ProviderTileKey key,
std::shared_ptr<RawTile> rawTile)
{
ghoul_precondition(rawTile, "RawTile can not be null");
using ghoul::opengl::Texture;
if (rawTile->error != RawTile::ReadError::None) {
return;
}
else {
const TileTextureInitData& initData = *rawTile->textureInitData;
Texture* texture = getTexture(initData);
// Re-upload texture, either using PBO or by using RAM data
if (rawTile->pbo != 0) {
texture->reUploadTextureFromPBO(rawTile->pbo);
if (initData.shouldAllocateDataOnCPU()) {
if (!texture->dataOwnership()) {
_numTextureBytesAllocatedOnCPU += initData.totalNumBytes();
}
texture->setPixelData(rawTile->imageData,
Texture::TakeOwnership::Yes);
}
}
else {
size_t previousExpectedDataSize = texture->expectedPixelDataSize();
ghoul_assert(texture->dataOwnership(),
"Texture must have ownership of old data to avoid leaks");
texture->setPixelData(rawTile->imageData, Texture::TakeOwnership::Yes);
size_t expectedDataSize = texture->expectedPixelDataSize();
size_t numBytes = rawTile->textureInitData->totalNumBytes();
ghoul_assert(expectedDataSize == numBytes, "Pixel data size is incorrect");
_numTextureBytesAllocatedOnCPU += numBytes - previousExpectedDataSize;
texture->reUploadTexture();
}
texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
Tile tile(texture, rawTile->tileMetaData, Tile::Status::OK);
TileTextureInitData::HashKey initDataKey = initData.hashKey();
_textureContainerMap[initDataKey].second->put(key, tile);
}
return;
}
MemoryAwareTileCache::MemoryAwareTileCache(size_t cacheSize)
: _tileCache(cacheSize) {}
void MemoryAwareTileCache::put(const ProviderTileKey& key,
const TileTextureInitData::HashKey& initDataKey, Tile tile)
{
_textureContainerMap[initDataKey].second->put(key, tile);
return;
}
void MemoryAwareTileCache::update() {
size_t dataSizeCPU = getCPUAllocatedDataSize();
size_t dataSizeGPU = getGPUAllocatedDataSize();
_cpuAllocatedTileData.setValue(dataSizeCPU / 1024 / 1024);
_gpuAllocatedTileData.setValue(dataSizeGPU / 1024 / 1024);
}
size_t MemoryAwareTileCache::getGPUAllocatedDataSize() const {
return std::accumulate(
_textureContainerMap.cbegin(),
_textureContainerMap.cend(), 0,
[](size_t s, const std::pair<const TileTextureInitData::HashKey,
TextureContainerTileCache>& p)
{
const TextureContainer& textureContainer = *p.second.first;
size_t bytesPerTexture =
textureContainer.tileTextureInitData().totalNumBytes();
return s + bytesPerTexture * textureContainer.size();
}
);
}
size_t MemoryAwareTileCache::getCPUAllocatedDataSize() const {
size_t dataSize = std::accumulate(
_textureContainerMap.cbegin(),
_textureContainerMap.cend(), 0,
[](size_t s, const std::pair<const TileTextureInitData::HashKey,
TextureContainerTileCache>& p)
{
const TextureContainer& textureContainer = *p.second.first;
const TileTextureInitData& initData = textureContainer.tileTextureInitData();
if (initData.shouldAllocateDataOnCPU()) {
size_t bytesPerTexture = initData.totalNumBytes();
return s + bytesPerTexture * textureContainer.size();
}
return s;
}
);
return dataSize + _numTextureBytesAllocatedOnCPU;
}
bool MemoryAwareTileCache::shouldUsePbo() const {
return _usePbo;
}
} // namespace cache
} // namespace globebrowsing

View File

@@ -25,12 +25,21 @@
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_TILE_CACHE___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_TILE_CACHE___H__
#include <modules/globebrowsing/cache/lrucache.h>
#include <modules/globebrowsing/cache/texturecontainer.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tileindex.h>
#include <modules/globebrowsing/cache/memoryawarelrucache.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/rawtiledatareader/iodescription.h>
#include <openspace/properties/propertyowner.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalarproperty.h>
#include <openspace/properties/triggerproperty.h>
#include <memory>
#include <mutex>
#include <vector>
#include <unordered_map>
namespace openspace {
namespace globebrowsing {
@@ -63,47 +72,63 @@ struct ProviderTileHasher {
unsigned long long operator()(const ProviderTileKey& t) const {
unsigned long long key = 0;
key |= static_cast<unsigned long long>(t.tileIndex.level);
key |= static_cast<unsigned long long>(t.tileIndex.x << 5);
key |= static_cast<unsigned long long>(t.tileIndex.y << 35);
key |= static_cast<unsigned long long>(t.tileIndex.x) << 5ULL;
key |= static_cast<unsigned long long>(t.tileIndex.y) << 35ULL;
// Now the key is unique for all tiles, however not for all tile providers.
// Add to the key depending on the tile provider to avoid some hash collisions.
// (All hash collisions can not be avoided due to the limit in 64 bit for the
// hash key)
// Idea: make some offset in the place of the bits for the x value. Lesser chance
// of having different x-value than having different tile provider ids.
key += static_cast<unsigned long long>(t.providerID << 25);
key += static_cast<unsigned long long>(t.providerID) << 25ULL;
return key;
}
};
/**
* Singleton class used to cache tiles for all <code>CachingTileProvider</code>s.
*/
class MemoryAwareTileCache {
class MemoryAwareTileCache : public properties::PropertyOwner {
public:
static void create(size_t cacheSize);
static void destroy();
MemoryAwareTileCache();
~MemoryAwareTileCache();
void clear();
void setSizeEstimated(size_t estimatedSize);
bool exist(ProviderTileKey key) const;
Tile get(ProviderTileKey key);
void put(ProviderTileKey key, Tile tile);
void setMaximumSize(size_t maximumSize);
ghoul::opengl::Texture* getTexture(const TileTextureInitData& initData);
void createTileAndPut(ProviderTileKey key, std::shared_ptr<RawTile> rawTile);
void put(const ProviderTileKey& key,
const TileTextureInitData::HashKey& initDataKey, Tile tile);
void update();
static MemoryAwareTileCache& ref();
size_t getGPUAllocatedDataSize() const;
size_t getCPUAllocatedDataSize() const;
bool shouldUsePbo() const;
private:
/**
* \param cacheSize is the cache size given in bytes.
*/
MemoryAwareTileCache(size_t cacheSize);
~MemoryAwareTileCache() = default;
void createDefaultTextureContainers();
void assureTextureContainerExists(const TileTextureInitData& initData);
void resetTextureContainerSize(size_t numTexturesPerTextureType);
static MemoryAwareTileCache* _singleton;
MemoryAwareLRUCache<ProviderTileKey, Tile, ProviderTileHasher> _tileCache;
static std::mutex _mutexLock;
using TileCache = LRUCache<ProviderTileKey, Tile, ProviderTileHasher>;
using TextureContainerTileCache =
std::pair<std::unique_ptr<TextureContainer>, std::unique_ptr<TileCache>>;
using TextureContainerMap = std::unordered_map<TileTextureInitData::HashKey,
TextureContainerTileCache>;
TextureContainerMap _textureContainerMap;
size_t _numTextureBytesAllocatedOnCPU;
// Properties
properties::IntProperty _cpuAllocatedTileData;
properties::IntProperty _gpuAllocatedTileData;
properties::IntProperty _tileCacheSize;
properties::TriggerProperty _applyTileCacheSize;
properties::TriggerProperty _clearTileCache;
/// Whether or not pixel buffer objects should be used when uploading tile data
properties::BoolProperty _usePbo;
};
} // namespace cache

View File

@@ -22,37 +22,70 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___DISKCACHEDTILELOADJOB___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___DISKCACHEDTILELOADJOB___H__
#include <modules/globebrowsing/tile/loadjob/tileloadjob.h>
#include <modules/globebrowsing/cache/texturecontainer.h>
#include <modules/globebrowsing/tile/tiletextureinitdata.h>
namespace openspace {
namespace globebrowsing {
namespace cache {
class TileDiskCache;
TextureContainer::TextureContainer(TileTextureInitData initData, size_t numTextures)
: _initData(initData)
, _freeTexture(0)
, _numTextures(numTextures)
{
reset();
}
struct DiskCachedTileLoadJob : public TileLoadJob {
enum CacheMode {
Disabled,
ReadOnly,
ReadAndWrite,
WriteOnly,
CacheHitsOnly,
};
void TextureContainer::reset() {
_textures.clear();
_freeTexture = 0;
ghoul::opengl::Texture::AllocateData allocate =
_initData.shouldAllocateDataOnCPU() ?
ghoul::opengl::Texture::AllocateData::Yes :
ghoul::opengl::Texture::AllocateData::No;
for (size_t i = 0; i < _numTextures; ++i)
{
auto tex = std::make_unique<ghoul::opengl::Texture>(
_initData.dimensionsWithPadding(),
_initData.ghoulTextureFormat(),
_initData.glTextureFormat(),
_initData.glType(),
ghoul::opengl::Texture::FilterMode::Linear,
ghoul::opengl::Texture::WrappingMode::ClampToEdge,
allocate
);
DiskCachedTileLoadJob(std::shared_ptr<RawTileDataReader> rawTileDataReader,
const TileIndex& tileIndex, std::shared_ptr<TileDiskCache> tdc,
CacheMode cacheMode = CacheMode::ReadOnly);
tex->setDataOwnership(ghoul::opengl::Texture::TakeOwnership::Yes);
tex->uploadTexture();
tex->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
_textures.push_back(std::move(tex));
}
}
void execute() override;
void TextureContainer::reset(size_t numTextures) {
_numTextures = numTextures;
reset();
}
protected:
std::shared_ptr<TileDiskCache> _tileDiskCache;
CacheMode _mode;
ghoul::opengl::Texture* TextureContainer::getTextureIfFree() {
ghoul::opengl::Texture* texture = nullptr;
if (_freeTexture < _textures.size()) {
texture = _textures[_freeTexture].get();
_freeTexture++;
}
return texture;
}
const openspace::globebrowsing::TileTextureInitData& TextureContainer::tileTextureInitData() const {
return _initData;
};
} // namespace globebrowsing
} // namespace openspace
size_t TextureContainer::size() const {
return _textures.size();
};
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___DISKCACHEDTILELOADJOB___H__
}
}
}

View File

@@ -22,37 +22,59 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_CACHEABLE___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_CACHEABLE___H__
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TEXTURE_CONTAINER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___TEXTURE_CONTAINER___H__
#include <modules/globebrowsing/tile/tiletextureinitdata.h>
#include <memory>
#include <vector>
namespace openspace {
namespace globebrowsing {
namespace cache {
/**
* Base class to be extended by classes that need to be cached and make use of the
* memoryImpact interface. A class extending <code>MemoryAwareCacheable</code> needs to
* know its memory impact at initialization and hence provide the memory impact in its
* constructors. The memory impact can not change during the lifetime of an object that is
* a <code>MemoryAwareCacheable</code>.
* Owner of texture data used for tiles. Instead of dynamically allocating textures one
* by one, they are created once and reused.
*/
class MemoryAwareCacheable {
class TextureContainer
{
public:
/**
* \param memoryImpact is the memory impact of the object. Can for example be given
* in kilobytes.
* \param initData is the description of the texture type.
* \param numTextures is the number of textures to allocate.
*/
MemoryAwareCacheable(size_t memoryImpact) : _memoryImpact(memoryImpact) {};
~MemoryAwareCacheable() {};
TextureContainer(TileTextureInitData initData, size_t numTextures);
size_t memoryImpact() { return _memoryImpact; };
~TextureContainer() = default;
void reset();
void reset(size_t numTextures);
protected:
size_t _memoryImpact;
/**
* \returns a pointer to a texture if there is one texture never used before.
* If there are no textures left, nullptr is returned. TextureContainer still owns
* the texture so no delete should be called on the raw pointer.
*/
ghoul::opengl::Texture* getTextureIfFree();
const TileTextureInitData& tileTextureInitData() const;
/**
* \returns the number of textures in this TextureContainer
*/
size_t size() const;
private:
std::vector<std::unique_ptr<ghoul::opengl::Texture>> _textures;
size_t _freeTexture;
const TileTextureInitData _initData;
size_t _numTextures;
};
} // namespace cache
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_CACHEABLE___H__
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TEXTURE_CONTAINER___H__

View File

@@ -40,9 +40,9 @@ const float Chunk::DEFAULT_HEIGHT = 0.0f;
Chunk::Chunk(const RenderableGlobe& owner, const TileIndex& tileIndex, bool initVisible)
: _owner(owner)
, _surfacePatch(tileIndex)
, _tileIndex(tileIndex)
, _isVisible(initVisible)
, _isVisible(initVisible)
, _surfacePatch(tileIndex)
{}
const GeodeticPatch& Chunk::surfacePatch() const {
@@ -64,7 +64,7 @@ bool Chunk::isVisible() const {
Chunk::Status Chunk::update(const RenderData& data) {
const auto& savedCamera = _owner.savedCamera();
const Camera& camRef = savedCamera != nullptr ? *savedCamera : data.camera;
RenderData myRenderData = { camRef, data.position, data.doPerformanceMeasurement };
RenderData myRenderData = { camRef, data.position, data.doPerformanceMeasurement, data.doPerformanceMeasurement, data.renderBinMask, data.modelTransform };
_isVisible = true;
if (_owner.chunkedLodGlobe()->testIfCullable(*this, myRenderData)) {
@@ -102,9 +102,9 @@ Chunk::BoundingHeights Chunk::getBoundingHeights() const {
// a single raster image. If it is not we will just use the first raster
// (that is channel 0).
const size_t HeightChannel = 0;
const LayerGroup& heightmaps = layerManager->layerGroup(LayerManager::HeightLayers);
const LayerGroup& heightmaps = layerManager->layerGroup(layergroupid::HeightLayers);
std::vector<ChunkTileSettingsPair> chunkTileSettingPairs =
tileselector::getTilesAndSettingsSortedByHighestResolution(
tileselector::getTilesAndSettingsUnsorted(
heightmaps, _tileIndex);
bool lastHadMissingData = true;

View File

@@ -40,7 +40,7 @@ int AvailableTileData::getDesiredLevel(const Chunk& chunk, const RenderData& dat
// auto layers = layerManager->layerGroup(LayerManager::HeightLayers).activeLayers();
int currLevel = chunk.tileIndex().level;
for (size_t i = 0; i < LayerManager::NUM_LAYER_GROUPS; ++i) {
for (size_t i = 0; i < layergroupid::NUM_LAYER_GROUPS; ++i) {
for (const auto& layer : layerManager->layerGroup(i).activeLayers()) {
Tile::Status status = layer->tileProvider()->getTileStatus(chunk.tileIndex());
if (status == Tile::Status::OK) {

View File

@@ -38,6 +38,7 @@ namespace chunklevelevaluator {
*/
class AvailableTileData : public Evaluator {
public:
virtual ~AvailableTileData() override = default;
int getDesiredLevel(const Chunk& chunk, const RenderData& data) const override;
};

View File

@@ -42,6 +42,7 @@ namespace chunklevelevaluator {
*/
class Evaluator {
public:
virtual ~Evaluator() = default;
static const int UnknownDesiredLevel = -1;
virtual int getDesiredLevel(const Chunk& chunk, const RenderData& data) const = 0;

View File

@@ -34,7 +34,7 @@ namespace globebrowsing {
ChunkNode::ChunkNode(const Chunk& chunk, ChunkNode* parent)
: _parent(parent)
, _children({ nullptr, nullptr, nullptr, nullptr })
, _children({ {nullptr, nullptr, nullptr, nullptr} })
, _chunk(chunk)
{}
@@ -147,7 +147,7 @@ const ChunkNode& ChunkNode::find(const Geodetic2& location) const {
++index;
++index;
}
node = &(node->getChild((Quad)index));
node = &(node->getChild(static_cast<Quad>(index)));
}
return *node;
}

View File

@@ -37,6 +37,7 @@ namespace culling {
class ChunkCuller {
public:
virtual ~ChunkCuller() = default;
/**
* Determine if the Chunk is cullable. That is return true if removing the
* Chunk 'culling it' will not have any result on the final rendering. Culling

View File

@@ -43,6 +43,7 @@ namespace culling {
*/
class FrustumCuller : public ChunkCuller {
public:
virtual ~FrustumCuller() override = default;
/**
* \param viewFrustum is the view space in normalized device coordinates space.
* Hence it is an axis aligned bounding box and not a real frustum.

View File

@@ -41,6 +41,7 @@ namespace culling {
*/
class HorizonCuller : public ChunkCuller {
public:
virtual ~HorizonCuller() override = default;
bool isCullable(const Chunk& chunk, const RenderData& renderData) override;
private:

View File

@@ -49,8 +49,8 @@ GeodeticPatch::GeodeticPatch(const GeodeticPatch& patch)
{}
GeodeticPatch::GeodeticPatch(const TileIndex& tileIndex) {
double deltaLat = (2 * glm::pi<double>()) / ((double)(1 << tileIndex.level));
double deltaLon = (2 * glm::pi<double>()) / ((double)(1 << tileIndex.level));
double deltaLat = (2 * glm::pi<double>()) / (static_cast<double>(1 << tileIndex.level));
double deltaLon = (2 * glm::pi<double>()) / (static_cast<double>(1 << tileIndex.level));
Geodetic2 nwCorner(glm::pi<double>() / 2 - deltaLat * tileIndex.y, -glm::pi<double>() + deltaLon * tileIndex.x);
_halfSize = Geodetic2(deltaLat / 2, deltaLon / 2);
_center = Geodetic2(nwCorner.lat - _halfSize.lat, nwCorner.lon + _halfSize.lon);

View File

@@ -50,39 +50,20 @@
namespace openspace {
const std::string GlobeBrowsingModule::name = "GlobeBrowsing";
GlobeBrowsingModule::GlobeBrowsingModule()
: OpenSpaceModule("GlobeBrowsing")
, _openSpaceMaximumTileCacheSize(
"maximumTileCacheSize", "Maximum tile cache size",
512, // Default: 512 MB
0, // Minimum: No caching
1024, // Maximum: 1024 MB
1) // Step: One MB
, _clearTileCache("clearTileCache", "Clear tile cache") {}
: OpenSpaceModule(name)
{ }
void GlobeBrowsingModule::internalInitialize() {
using namespace globebrowsing;
OsEng.registerModuleCallback(OpenSpaceEngine::CallbackOption::Initialize, [&] {
// Set maximum cache size to 25% of total RAM
_openSpaceMaximumTileCacheSize.setMaxValue(CpuCap.installedMainMemory() * 0.25);
// Convert from MB to KB
cache::MemoryAwareTileCache::create(_openSpaceMaximumTileCacheSize * 1024);
_openSpaceMaximumTileCacheSize.onChange(
[&]{
// Convert from MB to KB
cache::MemoryAwareTileCache::ref().setMaximumSize(
_openSpaceMaximumTileCacheSize * 1024);
});
_clearTileCache.onChange(
[&]{
cache::MemoryAwareTileCache::ref().clear();
});
addProperty(_openSpaceMaximumTileCacheSize);
addProperty(_clearTileCache);
_tileCache = std::make_unique<globebrowsing::cache::MemoryAwareTileCache>();
addPropertySubOwner(*_tileCache);
#ifdef GLOBEBROWSING_USE_GDAL
// Convert from MB to Bytes
GdalWrapper::create(
@@ -92,9 +73,12 @@ void GlobeBrowsingModule::internalInitialize() {
#endif // GLOBEBROWSING_USE_GDAL
});
OsEng.registerModuleCallback(OpenSpaceEngine::CallbackOption::Render, [&]{
_tileCache->update();
});
OsEng.registerModuleCallback(OpenSpaceEngine::CallbackOption::Deinitialize, [&]{
cache::MemoryAwareTileCache::ref().clear();
cache::MemoryAwareTileCache::ref().destroy();
#ifdef GLOBEBROWSING_USE_GDAL
GdalWrapper::ref().destroy();
#endif // GLOBEBROWSING_USE_GDAL
@@ -123,4 +107,8 @@ void GlobeBrowsingModule::internalInitialize() {
FactoryManager::ref().addFactory(std::move(fTileProvider));
}
globebrowsing::cache::MemoryAwareTileCache* GlobeBrowsingModule::tileCache() {
return _tileCache.get();
}
} // namespace openspace

View File

@@ -26,22 +26,28 @@
#define __OPENSPACE_MODULE_GLOBEBROWSING___GLOBEBROWSING_MODULE___H__
#include <openspace/util/openspacemodule.h>
#include <openspace/properties/scalarproperty.h>
#include <openspace/properties/triggerproperty.h>
#include <memory>
namespace openspace {
namespace globebrowsing {
namespace cache {
class MemoryAwareTileCache;
}
}
class GlobeBrowsingModule : public OpenSpaceModule {
public:
GlobeBrowsingModule();
globebrowsing::cache::MemoryAwareTileCache* tileCache();
static const std::string name;
protected:
void internalInitialize() override;
private:
properties::IntProperty _openSpaceMaximumTileCacheSize;
properties::TriggerProperty _clearTileCache;
std::unique_ptr<globebrowsing::cache::MemoryAwareTileCache> _tileCache;
};
} // namespace openspace

View File

@@ -58,13 +58,14 @@ const GeodeticPatch ChunkedLodGlobe::COVERAGE = GeodeticPatch(0, 0, 90, 180);
ChunkedLodGlobe::ChunkedLodGlobe(const RenderableGlobe& owner, size_t segmentsPerPatch,
std::shared_ptr<LayerManager> layerManager)
: _owner(owner)
: minSplitDepth(2)
, maxSplitDepth(22)
, stats(StatsCollector(absPath("test_stats"), 1, StatsCollector::Enabled::No))
, _owner(owner)
, _leftRoot(std::make_unique<ChunkNode>(Chunk(owner, LEFT_HEMISPHERE_INDEX)))
, _rightRoot(std::make_unique<ChunkNode>(Chunk(owner, RIGHT_HEMISPHERE_INDEX)))
, minSplitDepth(2)
, maxSplitDepth(22)
, _layerManager(layerManager)
, stats(StatsCollector(absPath("test_stats"), 1, StatsCollector::Enabled::No))
, _shadersNeedRecompilation(true)
{
auto geometry = std::make_shared<SkirtedGrid>(
static_cast<unsigned int>(segmentsPerPatch),
@@ -172,23 +173,28 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
);
// Get the tile providers for the height maps
const auto& heightMapLayers = _layerManager->layerGroup(LayerManager::HeightLayers).activeLayers();
const std::vector<std::shared_ptr<Layer>>& heightMapLayers =
_layerManager->layerGroup(layergroupid::HeightLayers).activeLayers();
for (const auto& layer : heightMapLayers) {
for (const std::shared_ptr<Layer>& layer : heightMapLayers) {
tileprovider::TileProvider* tileProvider = layer->tileProvider();
// Transform the uv coordinates to the current tile texture
ChunkTile chunkTile = tileProvider->getChunkTile(tileIndex);
const auto& tile = chunkTile.tile;
const auto& uvTransform = chunkTile.uvTransform;
const auto& depthTransform = tileProvider->depthTransform();
const Tile& tile = chunkTile.tile;
const TileUvTransform& uvTransform = chunkTile.uvTransform;
const TileDepthTransform& depthTransform = tileProvider->depthTransform();
if (tile.status() != Tile::Status::OK) {
return 0;
}
ghoul::opengl::Texture* tileTexture = tile.texture();
if (!tileTexture)
return 0;
glm::vec2 transformedUv = Tile::TileUvToTextureSamplePosition(
uvTransform,
patchUV,
glm::uvec2(tile.texture()->dimensions())
glm::uvec2(tileTexture->dimensions())
);
// Sample and do linear interpolation
@@ -196,7 +202,7 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
// Suggestion: a function in ghoul::opengl::Texture that takes uv coordinates
// in range [0,1] and uses the set interpolation method and clamping.
glm::uvec3 dimensions = tile.texture()->dimensions();
glm::uvec3 dimensions = tileTexture->dimensions();
glm::vec2 samplePos = transformedUv * glm::vec2(dimensions);
glm::uvec2 samplePos00 = samplePos;
@@ -220,10 +226,10 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
glm::uvec2(dimensions) - glm::uvec2(1)
);
float sample00 = tile.texture()->texelAsFloat(samplePos00).x;
float sample10 = tile.texture()->texelAsFloat(samplePos10).x;
float sample01 = tile.texture()->texelAsFloat(samplePos01).x;
float sample11 = tile.texture()->texelAsFloat(samplePos11).x;
float sample00 = tileTexture->texelAsFloat(samplePos00).x;
float sample10 = tileTexture->texelAsFloat(samplePos10).x;
float sample01 = tileTexture->texelAsFloat(samplePos01).x;
float sample11 = tileTexture->texelAsFloat(samplePos11).x;
// In case the texture has NaN or no data values don't use this height map.
bool anySampleIsNaN =
@@ -258,8 +264,16 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
return height;
}
void ChunkedLodGlobe::notifyShaderRecompilation() {
_shadersNeedRecompilation = true;
}
void ChunkedLodGlobe::render(const RenderData& data) {
stats.startNewRecord();
if (_shadersNeedRecompilation) {
_renderer->recompileShaders(_owner);
_shadersNeedRecompilation = false;
}
auto duration = std::chrono::system_clock::now().time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
@@ -270,7 +284,7 @@ void ChunkedLodGlobe::render(const RenderData& data) {
// Calculate the MVP matrix
glm::dmat4 viewTransform = glm::dmat4(data.camera.combinedViewMatrix());
glm::dmat4 vp = glm::dmat4(data.camera.projectionMatrix()) * viewTransform;
glm::dmat4 vp = glm::dmat4(data.camera.sgctInternal.projectionMatrix()) * viewTransform;
glm::dmat4 mvp = vp * _owner.modelTransform();
// Render function

View File

@@ -107,6 +107,8 @@ public:
*/
float getHeight(glm::dvec3 position) const;
void notifyShaderRecompilation();
const int minSplitDepth;
const int maxSplitDepth;
@@ -121,6 +123,8 @@ private:
static const TileIndex LEFT_HEMISPHERE_INDEX;
static const TileIndex RIGHT_HEMISPHERE_INDEX;
const RenderableGlobe& _owner;
// Covers all negative longitudes
std::unique_ptr<ChunkNode> _leftRoot;
@@ -138,7 +142,7 @@ private:
std::shared_ptr<LayerManager> _layerManager;
const RenderableGlobe& _owner;
bool _shadersNeedRecompilation;
};
} // namespace globebrowsing

View File

@@ -106,7 +106,7 @@ void PointGlobe::render(const RenderData& data) {
_programObject->setUniform("globeRadius", avgRadius);
_programObject->setUniform("directionToSunViewSpace", directionToSunViewSpace);
_programObject->setUniform("modelViewTransform", glm::mat4(modelViewTransform));
_programObject->setUniform("projectionTransform", data.camera.projectionMatrix());
_programObject->setUniform("projectionTransform", data.camera.sgctInternal.projectionMatrix());
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

View File

@@ -135,7 +135,18 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
_debugPropertyOwner.addProperty(_debugProperties.collectStats);
_debugPropertyOwner.addProperty(_debugProperties.limitLevelByAvailableData);
_debugPropertyOwner.addProperty(_debugProperties.modelSpaceRenderingCutoffLevel);
auto notifyShaderRecompilation = [&](){
_chunkedLodGlobe->notifyShaderRecompilation();
};
_generalProperties.atmosphereEnabled.onChange(notifyShaderRecompilation);
_generalProperties.performShading.onChange(notifyShaderRecompilation);
_debugProperties.showChunkEdges.onChange(notifyShaderRecompilation);
_debugProperties.showHeightResolution.onChange(notifyShaderRecompilation);
_debugProperties.showHeightIntensities.onChange(notifyShaderRecompilation);
_layerManager->onChange(notifyShaderRecompilation);
addPropertySubOwner(_debugPropertyOwner);
addPropertySubOwner(_layerManager.get());
}

View File

@@ -33,7 +33,7 @@ BasicGrid::BasicGrid(unsigned int xSegments, unsigned int ySegments,
TriangleSoup::Positions usePositions,
TriangleSoup::TextureCoordinates useTextureCoordinates,
TriangleSoup::Normals useNormals)
: Grid(xSegments, ySegments, usePositions, useTextureCoordinates, useNormals)
: Grid(xSegments, ySegments)
{
_geometry = std::make_unique<TriangleSoup>(
createElements(xSegments, ySegments),
@@ -84,8 +84,8 @@ std::vector<GLuint> BasicGrid::createElements(int xSegments, int ySegments) {
std::vector<GLuint> elements;
elements.reserve(numElements(xSegments, ySegments));
for (unsigned int y = 0; y < ySegments; y++) {
for (unsigned int x = 0; x < xSegments; x++) {
for (int y = 0; y < ySegments; y++) {
for (int x = 0; x < xSegments; x++) {
// x v01---v11 x ..
// | / |
@@ -141,8 +141,8 @@ std::vector<glm::vec2> BasicGrid::createTextureCoordinates(int xSegments, int yS
std::vector<glm::vec2> textureCoordinates;
textureCoordinates.reserve(numVertices(xSegments, ySegments));
for (unsigned int y = 0; y < ySegments + 1; y++) {
for (unsigned int x = 0; x < xSegments + 1; x++) {
for (int y = 0; y < ySegments + 1; y++) {
for (int x = 0; x < xSegments + 1; x++) {
textureCoordinates.push_back(glm::vec2(
static_cast<float>(x) / static_cast<float>(xSegments),
static_cast<float>(y) / static_cast<float>(ySegments)
@@ -157,8 +157,8 @@ std::vector<glm::vec3> BasicGrid::createNormals(int xSegments, int ySegments) {
std::vector<glm::vec3> normals;
normals.reserve(numVertices(xSegments, ySegments));
for (unsigned int y = 0; y < ySegments + 1; y++) {
for (unsigned int x = 0; x < xSegments + 1; x++) {
for (int y = 0; y < ySegments + 1; y++) {
for (int x = 0; x < xSegments + 1; x++) {
normals.push_back(glm::vec3(0, 0, 1));
}
}

View File

@@ -51,8 +51,8 @@ public:
TriangleSoup::TextureCoordinates useTextureCoordinates,
TriangleSoup::Normals useNormals);
virtual int xSegments() const;
virtual int ySegments() const;
virtual int xSegments() const override;
virtual int ySegments() const override;
private:
std::vector<GLuint> createElements(int xRes, int yRes) override;

View File

@@ -27,8 +27,7 @@
namespace openspace {
namespace globebrowsing {
Grid::Grid(int xSegments, int ySegments, TriangleSoup::Positions usePositions,
TriangleSoup::TextureCoordinates useTextures, TriangleSoup::Normals useNormals)
Grid::Grid(int xSegments, int ySegments)
: _xSegments(xSegments)
, _ySegments(ySegments)
{}

View File

@@ -43,11 +43,7 @@ namespace globebrowsing {
*/
class Grid {
public:
Grid(int xSegments, int ySegments,
TriangleSoup::Positions usePositions = TriangleSoup::Positions::No,
TriangleSoup::TextureCoordinates useTextures =
TriangleSoup::TextureCoordinates::No,
TriangleSoup::Normals useNormals = TriangleSoup::Normals::No);
Grid(int xSegments, int ySegments);
virtual ~Grid() = default;

View File

@@ -33,7 +33,7 @@ SkirtedGrid::SkirtedGrid(unsigned int xSegments, unsigned int ySegments,
TriangleSoup::Positions usePositions,
TriangleSoup::TextureCoordinates useTextureCoordinates,
TriangleSoup::Normals useNormals)
: Grid(xSegments, ySegments, usePositions, useTextureCoordinates, useNormals)
: Grid(xSegments, ySegments)
{
_geometry = std::make_unique<TriangleSoup>(
createElements(xSegments, ySegments),
@@ -81,8 +81,8 @@ std::vector<GLuint> SkirtedGrid::createElements(int xSegments, int ySegments) {
std::vector<GLuint> elements;
elements.reserve(numElements(xSegments + 2, ySegments + 2));
for (unsigned int y = 0; y < ySegments + 2; y++) {
for (unsigned int x = 0; x < xSegments + 2; x++) {
for (int y = 0; y < ySegments + 2; y++) {
for (int x = 0; x < xSegments + 2; x++) {
// x v01---v11 x ..
// | / |

View File

@@ -57,8 +57,8 @@ public:
TriangleSoup::Normals useNormals);
~SkirtedGrid() = default;
virtual int xSegments() const;
virtual int ySegments() const;
virtual int xSegments() const override;
virtual int ySegments() const override;
private:
std::vector<GLuint> createElements(int xRes, int yRes) override;

View File

@@ -35,12 +35,12 @@ namespace globebrowsing {
TriangleSoup::TriangleSoup(std::vector<unsigned int> elements, Positions usePositions,
TextureCoordinates useTextures, Normals useNormals)
: _vaoID(0)
, _vertexBufferID(0)
, _elementBufferID(0)
, _useVertexPositions(usePositions)
: _useVertexPositions(usePositions)
, _useTextureCoordinates(useTextures)
, _useVertexNormals(useNormals)
, _vaoID(0)
, _vertexBufferID(0)
, _elementBufferID(0)
{
setElements(elements);
}

View File

@@ -39,13 +39,13 @@ struct Job {
virtual ~Job();
virtual void execute() = 0;
virtual std::shared_ptr<P> product() const = 0;
virtual std::shared_ptr<P> product() = 0;
};
/*
* Templated Concurrent Job Manager
* This class is used execute specific jobs on one (1) parallell thread
*/
* Templated Concurrent Job Manager
* This class is used execute specific jobs on one (1) parallell thread
*/
template<typename P>
class ConcurrentJobManager {
public:
@@ -59,8 +59,6 @@ public:
size_t numFinishedJobs() const;
void reset();
private:
ConcurrentQueue<std::shared_ptr<Job<P>>> _finishedJobs;
std::shared_ptr<ThreadPool> threadPool;

View File

@@ -34,16 +34,12 @@ template<typename P>
Job<P>::~Job() {}
template<typename P>
ConcurrentJobManager<P>::ConcurrentJobManager(std::shared_ptr<ThreadPool> pool) : threadPool(pool) {
}
ConcurrentJobManager<P>::ConcurrentJobManager(std::shared_ptr<ThreadPool> pool)
: threadPool(pool)
{ }
template<typename P>
void ConcurrentJobManager<P>::enqueueJob(std::shared_ptr<Job<P>> job) {
//threadPool->queue([this, job]() {
// job->execute();
// _finishedJobs.push(job);
//});
threadPool->enqueue([this, job]() {
job->execute();
_finishedJobs.push(job);
@@ -52,7 +48,6 @@ void ConcurrentJobManager<P>::enqueueJob(std::shared_ptr<Job<P>> job) {
template<typename P>
void ConcurrentJobManager<P>::clearEnqueuedJobs() {
//threadPool->clearRemainingTasks();
threadPool->clearTasks();
}
@@ -67,12 +62,5 @@ size_t ConcurrentJobManager<P>::numFinishedJobs() const {
return _finishedJobs.size();
}
template<typename P>
void ConcurrentJobManager<P>::reset() {
//threadPool->clearRemainingTasks();
threadPool->clearTasks();
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -33,14 +33,14 @@ DistanceSwitch::~DistanceSwitch() {}
bool DistanceSwitch::initialize() {
_objectScale = 1.0;
for (int i = 0; i < _renderables.size(); ++i) {
for (unsigned int i = 0; i < _renderables.size(); ++i) {
_renderables[i]->initialize();
}
return true;
}
bool DistanceSwitch::deinitialize() {
for (int i = 0; i < _renderables.size(); ++i) {
for (unsigned int i = 0; i < _renderables.size(); ++i) {
_renderables[i]->deinitialize();
}
return true;
@@ -52,14 +52,14 @@ void DistanceSwitch::render(const RenderData& data) {
return;
}
double distanceToCamera = (data.camera.positionVec3() - data.position.dvec3()).length();
double distanceToCamera = distance(data.camera.positionVec3(), data.modelTransform.translation);
if (distanceToCamera > _maxDistances.back() * _objectScale) {
return;
}
// linear search through nodes to find which Renderable to render
for (int i = 0; i < _renderables.size(); ++i) {
for (unsigned int i = 0; i < _renderables.size(); ++i) {
if (distanceToCamera < _maxDistances[i] * _objectScale) {
_renderables[i]->render(data);
return;
@@ -69,7 +69,7 @@ void DistanceSwitch::render(const RenderData& data) {
void DistanceSwitch::update(const UpdateData& data) {
_objectScale = data.modelTransform.scale;
for (int i = 0; i < _renderables.size(); ++i) {
for (unsigned int i = 0; i < _renderables.size(); ++i) {
_renderables[i]->update(data);
}
}

View File

@@ -22,62 +22,78 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_LRU_CACHE___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_LRU_CACHE___H__
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___LRU_THREAD_POOL___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___LRU_THREAD_POOL___H__
#include <list>
#include <unordered_map>
#include <modules/globebrowsing/cache/lrucache.h>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
// Implementatin based on http://progsch.net/wordpress/?p=81
namespace openspace {
namespace globebrowsing {
namespace cache {
namespace globebrowsing {
/**
* Least recently used cache that knows about its memory impact. This class is templated
* and the second template argument <code>ValueType</code> needs to have a function
* <code>void memoryImpact()</code> that returns the size of the object given in whatever
* unit is used for size in the creation of the <code>MemoryAwareLRUCache</code>.
* It can for example be given in kilobytes.
* <code>KeyType</code> needs to be a size comparable type.
*/
template<typename KeyType, typename ValueType, typename HasherType>
class MemoryAwareLRUCache {
public:
/**
* \param maximumSize is the maximum size of the <code>MemoryAwareLRUCache</code>
* Once the maximum size is reached, the cache will start removing objects that were
* least recently used. The maximum size can for example be given in kilobytes. It
* must be the same size unit as used by the cached object class
* <code>ValueType</code>.
*/
MemoryAwareLRUCache(size_t maximumSize);
void put(const KeyType& key, const ValueType& value);
void clear();
bool exist(const KeyType& key) const;
ValueType get(const KeyType& key);
size_t size() const;
size_t maximumSize() const;
void setMaximumSize(size_t maximumSize);
template<typename KeyType>
class LRUThreadPool;
template<typename KeyType>
class LRUThreadPoolWorker {
public:
LRUThreadPoolWorker(LRUThreadPool<KeyType>& pool);
void operator()();
private:
void clean();
using Item = std::pair<KeyType, ValueType>;
using Items = std::list<Item>;
Items _itemList;
std::unordered_map<KeyType, decltype(_itemList.begin()), HasherType> _itemMap;
size_t _cacheSize;
size_t _maximumCacheSize;
LRUThreadPool<KeyType>& _pool;
};
/**
* The <code>LRUThreadPool</code> will only enqueue a certain number of tasks. The most
* recently enqueued task is the one that will be executed first. This class is templated
* on a key type which used as an identifier to determine wheter or not a task with the
* given key has been enqueued or not. This means that a task can be enqueued several
* times. The user must ensure that an enqueued task with a given key should be
* equal in outcome to a second enqueued task with the same key. This is because a second
* enqueued task with the same key will simply be bumped and prioritised before other
* enqueued tasks. The given task will be ignored.
*/
template<typename KeyType>
class LRUThreadPool {
public:
LRUThreadPool(size_t numThreads, size_t queueSize);
~LRUThreadPool();
void enqueue(std::function<void()> f, KeyType key);
bool touch(KeyType key);
std::vector<KeyType> getQueuedTasksKeys();
std::vector<KeyType> getUnqueuedTasksKeys();
void clearEnqueuedTasks();
private:
struct DefaultHasher {
unsigned long long operator()(const KeyType& key) const {
return static_cast<unsigned long long>(key);
}
};
friend class LRUThreadPoolWorker<KeyType>;
std::vector<std::thread> _workers;
cache::LRUCache<KeyType, std::function<void()>, DefaultHasher> _queuedTasks;
std::vector<KeyType> _unqueuedTasks;
std::mutex _queueMutex;
std::condition_variable _condition;
bool _stop;
};
} // namespace cache
} // namespace globebrowsing
} // namespace openspace
#include <modules/globebrowsing/cache/memoryawarelrucache.inl>
#include "lruthreadpool.inl"
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_LRU_CACHE___H__
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___LRU_THREAD_POOL___H__

View File

@@ -0,0 +1,141 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* 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. *
****************************************************************************************/
namespace openspace {
namespace globebrowsing {
template<typename KeyType>
LRUThreadPoolWorker<KeyType>::LRUThreadPoolWorker(LRUThreadPool<KeyType>& pool)
: _pool(pool)
{}
template<typename KeyType>
void LRUThreadPoolWorker<KeyType>::operator()() {
std::function<void()> task;
while (true) {
// acquire lock
{
std::unique_lock<std::mutex> lock(_pool._queueMutex);
// look for a work item
while (!_pool._stop && _pool._queuedTasks.isEmpty()) {
// 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._queuedTasks.popMRU().second;
}// release lock
// execute the task
task();
}
}
template<typename KeyType>
LRUThreadPool<KeyType>::LRUThreadPool(size_t numThreads, size_t queueSize)
: _queuedTasks(queueSize)
, _stop(false)
{
for (size_t i = 0; i < numThreads; ++i) {
_workers.push_back(std::thread(LRUThreadPoolWorker<KeyType>(*this)));
}
}
// the destructor joins all threads
template<typename KeyType>
LRUThreadPool<KeyType>::~LRUThreadPool() {
// 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
template<typename KeyType>
void LRUThreadPool<KeyType>::enqueue(std::function<void()> f, KeyType key) {
{ // acquire lock
std::unique_lock<std::mutex> lock(_queueMutex);
// add the task
//_queuedTasks.put(key, f);
std::vector<std::pair<KeyType, std::function<void()>>> unfinishedTasks =
_queuedTasks.putAndFetchPopped(key, f);
for (auto unfinishedTask : unfinishedTasks) {
_unqueuedTasks.push_back(unfinishedTask.first);
}
} // release lock
// wake up one thread
_condition.notify_one();
}
template<typename KeyType>
bool LRUThreadPool<KeyType>::touch(KeyType key) {
std::unique_lock<std::mutex> lock(_queueMutex);
return _queuedTasks.touch(key);
}
template<typename KeyType>
std::vector<KeyType> LRUThreadPool<KeyType>::getUnqueuedTasksKeys() {
std::vector<KeyType> toReturn = _unqueuedTasks;
{
std::unique_lock<std::mutex> lock(_queueMutex);
_unqueuedTasks.clear();
}
return toReturn;
}
template<typename KeyType>
std::vector<KeyType> LRUThreadPool<KeyType>::getQueuedTasksKeys() {
std::vector<KeyType> queuedTasks;
{
std::unique_lock<std::mutex> lock(_queueMutex);
while (!_queuedTasks.isEmpty()) {
queuedTasks.push_back(_queuedTasks.popMRU().first);
}
}
return queuedTasks;
}
template<typename KeyType>
void LRUThreadPool<KeyType>::clearEnqueuedTasks() {
{ // acquire lock
std::unique_lock<std::mutex> lock(_queueMutex);
_queuedTasks.clear();
} // release lock
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -22,35 +22,63 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEDATALAYOUT___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEDATALAYOUT___H__
#include <modules/globebrowsing/other/pixelbuffer.h>
#include <ghoul/logging/logmanager.h>
#include <modules/globebrowsing/tile/textureformat.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
class GDALDataset;
namespace openspace {
namespace globebrowsing {
struct TileDataLayout {
GLuint glType;
size_t bytesPerDatum;
size_t numRasters;
/// Number of rasters available in the GDAL dataset.
/// Does not necessarily have to be equal to numRasters.
/// In case an extra alpha channel needs to be added that
/// does not exist in the GDAL dataset for example
size_t numRastersAvailable;
size_t bytesPerPixel;
TextureFormat textureFormat;
namespace {
const char* _loggerCat = "PixelBuffer";
};
} // namespace globebrowsing
} // namespace openspace
using namespace openspace::globebrowsing;
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEDATALAYOUT___H__
PixelBuffer::PixelBuffer(size_t numBytes, Usage usage)
: _numBytes(numBytes)
, _usage(usage)
, _isMapped(false)
{
glGenBuffers(1, &_id);
bind();
glBufferData(GL_PIXEL_UNPACK_BUFFER, _numBytes, 0, static_cast<GLenum>(_usage));
unbind();
}
PixelBuffer::~PixelBuffer() {
glDeleteBuffers(1, &_id);
}
void* PixelBuffer::mapBuffer(Access access) {
void* dataPtr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, static_cast<GLenum>(access));
_isMapped = dataPtr ? true : false;
return dataPtr;
}
void* PixelBuffer::mapBufferRange(GLintptr offset, GLsizeiptr length, GLbitfield access) {
void* dataPtr = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, offset, length, access);
_isMapped = dataPtr ? true : false;
return dataPtr;
}
bool PixelBuffer::unMapBuffer() {
bool success = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
if (!success) {
LERROR("Unable to unmap pixel buffer, data may be corrupt!");
}
_isMapped = false;
return success;
}
void PixelBuffer::bind() {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _id);
}
void PixelBuffer::unbind() {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
bool PixelBuffer::isMapped() const {
return _isMapped;
}
PixelBuffer::operator GLuint() const {
return _id;
}

View File

@@ -0,0 +1,140 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* 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 __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER___H__
#include <ghoul/opengl/ghoul_gl.h>
namespace openspace {
namespace globebrowsing {
/**
* Handles an OpenGL pixel buffer which contains data allocated on the GPU. A simple
* class that wraps the standard functionality of OpenGL pixel buffer objects. Once
* the PixelBuffer is created, data is allocated on the GPU. When mapping data to a
* address pointer, the user needs to ensure the data is unmapped before the data can
* be used on the GPU / CPU depending on Usage.
*/
class PixelBuffer
{
public:
/**
* All kinds of usage for pixel buffer objects as defined by the OpenGL standard.
* See: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBufferData.xhtml
*/
enum class Usage {
StreamDraw = GL_STREAM_DRAW,
StreamRead = GL_STREAM_READ,
StreamCopy = GL_STREAM_COPY,
StaticDraw = GL_STATIC_DRAW,
StaticRead = GL_STATIC_READ,
StaticCopy = GL_STATIC_COPY,
DynamicDraw = GL_DYNAMIC_DRAW,
DynamicRead = GL_DYNAMIC_READ,
DynamicCopy = GL_DYNAMIC_COPY
};
/**
* Access hints for OpenGL buffer mapping
* See: https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glMapBuffer.xml
*/
enum class Access {
ReadOnly = GL_READ_ONLY,
WriteOnly = GL_WRITE_ONLY,
ReadWrite = GL_READ_WRITE
};
/**
* Allocates <code>numBytes</code> bytes on the GPU and creates an ID for the pixel
* buffer object.
* \param numBytes is the number of bytes to be allocated on GPU memory
* \param usage is the <code>Usage</code> for the pixel buffer
*/
PixelBuffer(size_t numBytes, Usage usage);
/**
* calls glDeleteBuffers().
*/
~PixelBuffer();
/**
* Maps an address pointer to GPU direct memory access. The user must make sure the
* buffer is bound before calling this function.
* \param access is the access to which can be any of <code>GL_READ_ONLY</code>,
* <code>GL_WRITE_ONLY</code>, or <code>GL_READ_WRITE</code>
* \returns the DMA address to the mapped buffer. Returns nullptr if the mapping
* failed
*/
void* mapBuffer(Access access);
/**
* Maps an address pointer to GPU direct memory access. Gives access to a range of
* the buffer. The user must make sure the buffer is bound before calling this
* function.
* \param offet is the number of bytes to the first address to get in the buffer
* \param length is the number of bytes to access in the buffer
* \param access is a bitfield describing the access as described in:
* https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glMapBufferRange.xhtml
* \returns the DMA address to the mapped buffer. Returns nullptr if the mapping
* failed
*/
void* mapBufferRange(GLintptr offset, GLsizeiptr length, GLbitfield access);
/**
* Maps the default buffer and makes the data available on the GPU
*/
bool unMapBuffer();
/**
* Calls glBindBuffer()
*/
void bind();
/**
* Calls glBindBuffer() with argument 0 to unmap any pixel buffer
*/
void unbind();
/**
* \returns true of the buffer is mapped, otherwise false
*/
bool isMapped() const;
/**
* \returns the OpenGL id of the pixel buffer object
*/
operator GLuint() const;
private:
GLuint _id;
const size_t _numBytes;
const Usage _usage;
bool _isMapped;
};
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER___H__

View File

@@ -0,0 +1,115 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* 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 __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER_CONTAINER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER_CONTAINER___H__
#include <modules/globebrowsing/other/pixelbuffer.h>
#include <map>
namespace openspace {
namespace globebrowsing {
/**
* Templated class which owns one or many <code>PixelBuffer</code>s. The
* <code>KeyType</code> is used to map a pixel buffer but only if it is not already
* mapped.
*/
template <class KeyType>
class PixelBufferContainer
{
public:
/**
* Creates numPixelBuffers pixel buffer objects, each with numBytesPerBuffer bytes
* allocated on the GPU.
* \param numBytesPerBuffer is the number of bytes per pixel buffer. All pixel
* buffers within a <code>PixelBufferContainer</code> have the same number of bytes
* \param usage is the <code>Usage</code> as described by <code>PixelBuffer</code>
* \param numPixelBuffers is the number of pixel buffers to create for this container.
* If numPixelBuffers is omitted, no pixel buffers are created.
*/
PixelBufferContainer(size_t numBytesPerBuffer,
globebrowsing::PixelBuffer::Usage usage, size_t numPixelBuffers = 0);
~PixelBufferContainer() = default;
/**
* Finds a Pixel buffer and maps it if it is available.
* \param key is the identifier for the pixel buffer which can be used later when
* unmapping the mapped pixel buffer.
* \param access is the access as described by <code>PixelBuffer</code>
* \returns an address pointer to DMA if the mapping succeeded. Otherwise a nullptr
* is returned. The mapping can fail if the buffer identified with <code>key</code>
* is already mapped or if something else failed.
*/
void* mapBuffer(KeyType key, PixelBuffer::Access access);
/**
* Finds a Pixel buffer and maps a range of it if it is available.
* \param key is the identifier for the pixel buffer which can be used later when
* unmapping the mapped pixel buffer.
* \param offet is the number of bytes to the first address to get in the buffer
* \param length is the number of bytes to access in the buffer
* \param access is the access as described by <code>PixelBuffer</code>
* \returns an address pointer to DMA if the mapping succeeded. Otherwise a nullptr
* is returned. The mapping can fail if the buffer identified with <code>key</code>
* is already mapped or if something else failed.
*/
void* mapBufferRange(KeyType key, GLintptr offset, GLsizeiptr length,
GLbitfield access);
/**
* Unmaps all buffers in the PixelBufferContainer.
* \returns true if the unmapping succeeded, otherwise false.
*/
bool resetMappedBuffers();
/**
* Unmaps a buffer that has previously been mapped. This buffer is identified using
* <code>key</code>.
* \param key is the identifier of the mapped buffer.
* \returns true if the unmapping succeeded, otherwise false.
*/
bool unMapBuffer(KeyType key);
/**
* \returns the <code>GLuint</code> id of a pixel buffer identified by <code>key</code>
* if it currently is mapped.
*/
GLuint idOfMappedBuffer(KeyType key);
private:
const globebrowsing::PixelBuffer::Usage _usage;
std::vector<std::unique_ptr<PixelBuffer>> _pixelBuffers;
// Maps from KeyType to index of mapped buffers
std::map<KeyType, int> _indexMap;
};
} // namespace globebrowsing
} // namespace openspace
#include "pixelbuffercontainer.inl"
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER_CONTAINER___H__

View File

@@ -0,0 +1,130 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* 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. *
****************************************************************************************/
namespace openspace {
namespace globebrowsing {
template <class KeyType>
PixelBufferContainer<KeyType>::PixelBufferContainer(size_t numBytesPerBuffer,
PixelBuffer::Usage usage, size_t numPixelBuffers)
: _usage(usage)
{
for (size_t i = 0; i < numPixelBuffers; ++i) {
_pixelBuffers.push_back(std::make_unique<PixelBuffer>(numBytesPerBuffer, _usage));
}
}
template <class KeyType>
void* PixelBufferContainer<KeyType>::mapBuffer(KeyType key, PixelBuffer::Access access) {
typename std::map<KeyType, int>::const_iterator iter = _indexMap.find(key);
const bool notFoundAmongMappedBuffers = (iter == _indexMap.end());
if (!notFoundAmongMappedBuffers) { // This PBO is already mapped
ghoul_assert(_pixelBuffers[iter->second], "Incorrect index map");
return nullptr;
}
// Find a pixel buffer that is unmapped
for (size_t i = 0; i < _pixelBuffers.size(); ++i) {
if (!_pixelBuffers[i]->isMapped()) {
_pixelBuffers[i]->bind();
void* dataPtr = _pixelBuffers[i]->mapBuffer(access);
_pixelBuffers[i]->unbind();
if (dataPtr) { // Success in mapping
// Add this index to the map of mapped pixel buffers
_indexMap.emplace(key, i);
return dataPtr;
}
}
}
return nullptr;
}
template <class KeyType>
void* PixelBufferContainer<KeyType>::mapBufferRange(KeyType key, GLintptr offset,
GLsizeiptr length, GLbitfield access)
{
typename std::map<KeyType, int>::const_iterator iter = _indexMap.find(key);
bool notFoundAmongMappedBuffers = iter == _indexMap.end();
if (!notFoundAmongMappedBuffers) { // This PBO is already mapped
ghoul_assert(_pixelBuffers[iter->second], "Incorrect index map");
return nullptr;
}
// Find a pixel buffer that is unmapped
for (int i = 0; i < _pixelBuffers.size(); ++i) {
bool bufferIsMapped = _pixelBuffers[i]->isMapped();
if (!bufferIsMapped) {
_pixelBuffers[i]->bind();
void* dataPtr = _pixelBuffers[i]->mapBufferRange(offset, length, access);
_pixelBuffers[i]->unbind();
if (dataPtr) { // Success in mapping
_indexMap.emplace(key, i);
return dataPtr;
}
}
}
return nullptr;
}
template <class KeyType>
bool PixelBufferContainer<KeyType>::resetMappedBuffers() {
bool success = true;
for (auto iter = _indexMap.begin(); iter != _indexMap.end(); iter++) {
int index = iter->second; // Index where the mapped buffer is stored
_pixelBuffers[index]->bind();
success &= _pixelBuffers[index]->unMapBuffer();
_pixelBuffers[index]->unbind();
_indexMap.erase(iter); // This key should no longer be among the mapped buffers
}
return success;
}
template <class KeyType>
bool PixelBufferContainer<KeyType>::unMapBuffer(KeyType key) {
bool success = false;
typename std::map<KeyType, int>::const_iterator iter = _indexMap.find(key);
if (iter != _indexMap.end()) { // Found a mapped pixel buffer
int index = iter->second; // Index where the mapped buffer is stored
_pixelBuffers[index]->bind();
success = _pixelBuffers[index]->unMapBuffer();
_pixelBuffers[index]->unbind();
_indexMap.erase(iter); // This key should no longer be among the mapped buffers
}
return success;
}
template <class KeyType>
GLuint PixelBufferContainer<KeyType>::idOfMappedBuffer(KeyType key) {
typename std::map<KeyType, int>::const_iterator iter = _indexMap.find(key);
if (iter != _indexMap.end()) { // Found a mapped pixel buffer
int index = iter->second; // Index where the mapped buffer is stored
return *_pixelBuffers[index];
}
return 0;
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -0,0 +1,95 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* 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 __OPENSPACE_MODULE_GLOBEBROWSING___PRIORITIZING_CONCURRENT_JOB_MANAGER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___PRIORITIZING_CONCURRENT_JOB_MANAGER___H__
#include <modules/globebrowsing/other/concurrentqueue.h>
#include <modules/globebrowsing/other/lruthreadpool.h>
#include <modules/globebrowsing/other/concurrentjobmanager.h>
namespace openspace {
namespace globebrowsing {
/**
* Concurrent job manager which prioritizes which jobs to work on depending on which
* ones were enqueued latest. The class is templated both on the job type and the key
* type which is used to identify jobs. In case a job need to be explicitly ended
* It can be identified using its key.
*/
template<typename P, typename KeyType>
class PrioritizingConcurrentJobManager {
public:
PrioritizingConcurrentJobManager(std::shared_ptr<LRUThreadPool<KeyType>> pool);
/**
* Enqueues a job which is identified using a given key
*/
void enqueueJob(std::shared_ptr<Job<P>> job, KeyType key);
/**
* The keys returned by this function have been popped from the queue and corresponds
* to jobs that will not be executed and therefore marked as unfinished. Calling this
* function will also clear the list of unfinished jobs so if the jobs need to be
* explicitly ended, the user need to make sure to do so after calling this function.
*/
std::vector<KeyType> getKeysToUnfinishedJobs();
std::vector<KeyType> getKeysToEnqueuedJobs();
/**
* Bumps the job identified with <code>key</code> to the beginning of the queue.
* In case the job was not already enqueued the function simply returns false and
* no state is changed.
* \param key is the identifier of the job to bump.
* \returns true if the job was found, else returns false.
*/
bool touch(KeyType key);
/**
* Clear all enqueued jobs. Can not end jobs that workers are currently handling.
* Therefore it is not safe to assume that there will be no finished jobs after
* calling this function.
*/
void clearEnqueuedJobs();
/**
* \returns one finished job.
*/
std::shared_ptr<Job<P>> popFinishedJob();
size_t numFinishedJobs() const;
private:
ConcurrentQueue<std::shared_ptr<Job<P>>> _finishedJobs;
/// An LRU thread pool is used since the jobs can be bumped and hence prioritized.
std::shared_ptr<LRUThreadPool<KeyType>> _threadPool;
};
} // namespace globebrowsing
} // namespace openspace
#include "prioritizingconcurrentjobmanager.inl"
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___PRIORITIZING_CONCURRENT_JOB_MANAGER___H__

View File

@@ -22,41 +22,59 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DISK_CACHE___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DISK_CACHE___H__
#include <ghoul/filesystem/directory.h>
#include <ghoul/filesystem/file.h>
#include <memory>
#include <ghoul/misc/assert.h>
namespace openspace {
namespace globebrowsing {
struct TileIndex;
struct RawTile;
template<typename P, typename KeyType>
PrioritizingConcurrentJobManager<P, KeyType>::PrioritizingConcurrentJobManager(
std::shared_ptr<LRUThreadPool<KeyType>> pool)
: _threadPool(pool)
{ }
class TileDiskCache {
public:
TileDiskCache(const std::string& name);
std::shared_ptr<RawTile> get(const TileIndex& tileIndex);
bool has(const TileIndex& tileIndex) const;
bool put(const TileIndex& tileIndex, std::shared_ptr<RawTile> rawTile);
static const std::string CACHE_ROOT;
private:
const std::string _name;
ghoul::filesystem::Directory _cacheDir;
std::string getFilePath(const TileIndex& tileIndex) const;
ghoul::filesystem::File getMetaDataFile(const TileIndex& tileIndex) const;
ghoul::filesystem::File getDataFile(const TileIndex& tileIndex) const;
};
template<typename P, typename KeyType>
void PrioritizingConcurrentJobManager<P, KeyType>::enqueueJob(std::shared_ptr<Job<P>> job,
KeyType key)
{
_threadPool->enqueue([this, job]() {
job->execute();
_finishedJobs.push(job);
}, key);
}
template<typename P, typename KeyType>
std::vector<KeyType>
PrioritizingConcurrentJobManager<P, KeyType>::getKeysToUnfinishedJobs() {
return _threadPool->getUnqueuedTasksKeys();
}
template<typename P, typename KeyType>
std::vector<KeyType>
PrioritizingConcurrentJobManager<P, KeyType>::getKeysToEnqueuedJobs() {
return _threadPool->getQueuedTasksKeys();
}
template<typename P, typename KeyType>
bool PrioritizingConcurrentJobManager<P, KeyType>::touch(KeyType key) {
return _threadPool->touch(key);
}
template<typename P, typename KeyType>
void PrioritizingConcurrentJobManager<P, KeyType>::clearEnqueuedJobs() {
_threadPool->clearEnqueuedTasks();
}
template<typename P, typename KeyType>
std::shared_ptr<Job<P>> PrioritizingConcurrentJobManager<P, KeyType>::popFinishedJob() {
ghoul_assert(_finishedJobs.size() > 0, "There is no finished job to pop!");
return _finishedJobs.pop();
}
template<typename P, typename KeyType>
size_t PrioritizingConcurrentJobManager<P, KeyType>::numFinishedJobs() const {
return _finishedJobs.size();
}
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DISK_CACHE___H__

View File

@@ -33,14 +33,14 @@ namespace globebrowsing {
StatsCollector::StatsCollector(const std::string& filename, int dumpEveryXRecord,
Enabled enabled, const std::string & delimiter)
: _filename(filename)
: i(TemplatedStatsCollector<long long>(_enabled, delimiter))
, d(TemplatedStatsCollector<double>(_enabled, delimiter))
, _filename(filename)
, _delimiter(delimiter)
, _dumpEveryXRecord(dumpEveryXRecord)
, _recordsSinceLastDump(0)
, _enabled(enabled)
, _delimiter(delimiter)
, _hasWrittenHeader(false)
, i(TemplatedStatsCollector<long long>(_enabled, delimiter))
, d(TemplatedStatsCollector<double>(_enabled, delimiter))
{}
StatsCollector::~StatsCollector() {

View File

@@ -29,8 +29,8 @@ template <typename T>
TemplatedStatsCollector<T>::TemplatedStatsCollector(bool& enabled,
const std::string& delimiter)
: _enabled(enabled)
, _delimiter(delimiter)
, _writePos(0)
, _delimiter(delimiter)
{}
template <typename T>

View File

@@ -33,21 +33,13 @@
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <openspace/util/updatestructures.h>
namespace {
const char* keyFrame = "Frame";
const char* keyGeometry = "Geometry";
const char* keyShading = "PerformShading";
const char* keyBody = "Body";
}
namespace openspace {
namespace globebrowsing {
ChunkRenderer::ChunkRenderer(std::shared_ptr<Grid> grid,
std::shared_ptr<LayerManager> layerManager)
: _layerManager(layerManager)
, _grid(grid)
: _grid(grid)
,_layerManager(layerManager)
{
_globalLayerShaderManager = std::make_shared<LayerShaderManager>(
"GlobalChunkedLodPatch",
@@ -61,7 +53,6 @@ ChunkRenderer::ChunkRenderer(std::shared_ptr<Grid> grid,
_globalGpuLayerManager = std::make_shared<GPULayerManager>();
_localGpuLayerManager = std::make_shared<GPULayerManager>();
}
void ChunkRenderer::renderChunk(const Chunk& chunk, const RenderData& data) {
@@ -79,6 +70,13 @@ void ChunkRenderer::update() {
// unused atm. Could be used for caching or precalculating
}
void ChunkRenderer::recompileShaders(const RenderableGlobe& globe) {
LayerShaderManager::LayerShaderPreprocessingData preprocessingData =
LayerShaderManager::LayerShaderPreprocessingData::get(globe);
_globalLayerShaderManager->recompileShaderProgram(preprocessingData);
_localLayerShaderManager->recompileShaderProgram(preprocessingData);
}
ghoul::opengl::ProgramObject* ChunkRenderer::getActivatedProgramWithTileData(
std::shared_ptr<LayerShaderManager> layeredShaderManager,
std::shared_ptr<GPULayerManager> gpuLayerManager,
@@ -86,45 +84,8 @@ ghoul::opengl::ProgramObject* ChunkRenderer::getActivatedProgramWithTileData(
{
const TileIndex& tileIndex = chunk.tileIndex();
LayerShaderManager::LayerShaderPreprocessingData layeredTexturePreprocessingData;
for (size_t i = 0; i < LayerManager::NUM_LAYER_GROUPS; i++) {
LayerShaderManager::LayerShaderPreprocessingData::LayerGroupPreprocessingData layeredTextureInfo;
auto layerGroup = _layerManager->layerGroup(i);
layeredTextureInfo.lastLayerIdx = layerGroup.activeLayers().size() - 1;
layeredTextureInfo.layerBlendingEnabled = layerGroup.layerBlendingEnabled();
layeredTexturePreprocessingData.layeredTextureInfo[i] = layeredTextureInfo;
}
const auto& generalProps = chunk.owner().generalProperties();
const auto& debugProps = chunk.owner().debugProperties();
auto& pairs = layeredTexturePreprocessingData.keyValuePairs;
pairs.emplace_back("useAtmosphere", std::to_string(generalProps.atmosphereEnabled));
pairs.emplace_back("performShading", std::to_string(generalProps.performShading));
pairs.emplace_back("showChunkEdges", std::to_string(debugProps.showChunkEdges));
pairs.emplace_back("showHeightResolution",
std::to_string(debugProps.showHeightResolution));
pairs.emplace_back("showHeightIntensities",
std::to_string(debugProps.showHeightIntensities));
pairs.emplace_back("defaultHeight", std::to_string(Chunk::DEFAULT_HEIGHT));
pairs.emplace_back("tilePaddingStart",
"ivec2(" +
std::to_string(RawTileDataReader::padding.start.x) + "," +
std::to_string(RawTileDataReader::padding.start.y) + ")"
);
pairs.emplace_back("tilePaddingSizeDiff",
"ivec2(" +
std::to_string(RawTileDataReader::padding.numPixels.x) + "," +
std::to_string(RawTileDataReader::padding.numPixels.y) + ")"
);
// Now the shader program can be accessed
ghoul::opengl::ProgramObject* programObject =
layeredShaderManager->programObject(
layeredTexturePreprocessingData);
ghoul::opengl::ProgramObject* programObject = layeredShaderManager->programObject();
if (layeredShaderManager->updatedOnLastCall()) {
gpuLayerManager->bind(programObject, *_layerManager);
@@ -183,7 +144,7 @@ void ChunkRenderer::renderChunkGlobally(const Chunk& chunk, const RenderData& da
glm::dmat4 modelTransform = chunk.owner().modelTransform();
glm::dmat4 viewTransform = data.camera.combinedViewMatrix();
glm::mat4 modelViewTransform = glm::mat4(viewTransform * modelTransform);
glm::mat4 modelViewProjectionTransform = data.camera.projectionMatrix() *
glm::mat4 modelViewProjectionTransform = data.camera.sgctInternal.projectionMatrix() *
modelViewTransform;
// Upload the uniform variables
@@ -194,9 +155,9 @@ void ChunkRenderer::renderChunkGlobally(const Chunk& chunk, const RenderData& da
programObject->setUniform("radiiSquared", glm::vec3(ellipsoid.radiiSquared()));
if (_layerManager->layerGroup(
LayerManager::NightLayers).activeLayers().size() > 0 ||
layergroupid::NightLayers).activeLayers().size() > 0 ||
_layerManager->layerGroup(
LayerManager::WaterMasks).activeLayers().size() > 0 ||
layergroupid::WaterMasks).activeLayers().size() > 0 ||
chunk.owner().generalProperties().atmosphereEnabled ||
chunk.owner().generalProperties().performShading) {
// This code temporary until real light sources can be implemented.
@@ -254,7 +215,7 @@ void ChunkRenderer::renderChunkLocally(const Chunk& chunk, const RenderData& dat
std::vector<std::string> cornerNames = { "p01", "p11", "p00", "p10" };
std::vector<glm::dvec3> cornersCameraSpace(4);
for (int i = 0; i < 4; ++i) {
Quad q = (Quad)i;
Quad q = static_cast<Quad>(i);
Geodetic2 corner = chunk.surfacePatch().getCorner(q);
glm::dvec3 cornerModelSpace = ellipsoid.cartesianSurfacePosition(corner);
glm::dvec3 cornerCameraSpace =
@@ -272,12 +233,12 @@ void ChunkRenderer::renderChunkLocally(const Chunk& chunk, const RenderData& dat
cornersCameraSpace[Quad::SOUTH_WEST]));
programObject->setUniform("patchNormalCameraSpace", patchNormalCameraSpace);
programObject->setUniform("projectionTransform", data.camera.projectionMatrix());
programObject->setUniform("projectionTransform", data.camera.sgctInternal.projectionMatrix());
if (_layerManager->layerGroup(
LayerManager::NightLayers).activeLayers().size() > 0 ||
layergroupid::NightLayers).activeLayers().size() > 0 ||
_layerManager->layerGroup(
LayerManager::WaterMasks).activeLayers().size() > 0 ||
layergroupid::WaterMasks).activeLayers().size() > 0 ||
chunk.owner().generalProperties().atmosphereEnabled ||
chunk.owner().generalProperties().performShading)
{

View File

@@ -42,6 +42,7 @@ class Grid;
class GPULayerManager;
class LayerManager;
class LayerShaderManager;
class RenderableGlobe;
class ChunkRenderer {
public:
@@ -55,6 +56,8 @@ public:
void renderChunk(const Chunk& chunk, const RenderData& data);
void update();
void recompileShaders(const RenderableGlobe& globe);
private:
/**
* Chunks can be rendered either globally or locally. Global rendering is performed

View File

@@ -48,13 +48,15 @@ struct TileIndex;
class GPUHeightLayer : public GPULayer {
public:
virtual ~GPUHeightLayer() override = default;
/**
* Sets the value of <code>Layer</code> to its corresponding
* GPU struct. OBS! Users must ensure bind has been
* called before setting using this method.
*/
virtual void setValue(ghoul::opengl::ProgramObject* programObject, const Layer& layer,
const TileIndex& tileIndex, int pileSize);
const TileIndex& tileIndex, int pileSize) override;
/**
* Binds this object with GLSL variables with identifiers starting
@@ -62,7 +64,7 @@ public:
* After this method has been called, users may invoke setValue.
*/
virtual void bind(ghoul::opengl::ProgramObject* programObject, const Layer& layer,
const std::string& nameBase, int pileSize);
const std::string& nameBase, int pileSize) override;
private:
GPUTileDepthTransform _gpuDepthTransform;

View File

@@ -41,7 +41,7 @@ void GPULayer::bind(ghoul::opengl::ProgramObject* programObject, const Layer& la
const std::string& nameBase, int pileSize)
{
gpuChunkTilePile.bind(programObject, nameBase + "pile.", pileSize);
gpuRenderSettings.bind(programObject, nameBase + "settings.");
gpuRenderSettings.bind(layer.renderSettings(), programObject, nameBase + "settings.");
}
void GPULayer::deactivate() {

View File

@@ -46,6 +46,8 @@ struct TileIndex;
class GPULayer {
public:
virtual ~GPULayer() = default;
/**
* Sets the value of <code>Layer</code> to its corresponding
* GPU struct. OBS! Users must ensure bind has been

View File

@@ -39,7 +39,7 @@ void GPULayerGroup::setValue(ghoul::opengl::ProgramObject* programObject,
activeLayers.size() == _gpuActiveLayers.size(),
"GPU and CPU active layers must have same size!"
);
for (int i = 0; i < activeLayers.size(); ++i) {
for (unsigned int i = 0; i < activeLayers.size(); ++i) {
_gpuActiveLayers[i]->setValue(
programObject,
*activeLayers[i],
@@ -58,7 +58,7 @@ void GPULayerGroup::bind(ghoul::opengl::ProgramObject* programObject,
int pileSize = layerGroup.pileSize();
for (size_t i = 0; i < _gpuActiveLayers.size(); ++i) {
// should maybe a proper GPULayer factory
_gpuActiveLayers[i] = (category == LayerManager::HeightLayers) ?
_gpuActiveLayers[i] = (category == layergroupid::HeightLayers) ?
std::make_unique<GPUHeightLayer>() :
std::make_unique<GPULayer>();
std::string nameExtension = "[" + std::to_string(i) + "].";

View File

@@ -41,7 +41,7 @@ namespace globebrowsing {
struct ChunkTile;
class Layer;
class LayerRenderSettings;
struct LayerRenderSettings;
struct TileDepthTransform;
struct TileUvTransform;
@@ -54,6 +54,8 @@ struct TileIndex;
class GPULayerGroup {
public:
virtual ~GPULayerGroup() = default;
/**
* Sets the value of <code>LayerGroup</code> to its corresponding
* GPU struct. OBS! Users must ensure bind has been

View File

@@ -51,7 +51,7 @@ void GPULayerManager::bind(ghoul::opengl::ProgramObject* programObject,
}
for (size_t i = 0; i < layerGroups.size(); ++i) {
std::string nameBase = LayerManager::LAYER_GROUP_NAMES[i];
const std::string& nameBase = layergroupid::LAYER_GROUP_NAMES[i];
_gpuLayerGroups[i]->bind(programObject, *layerGroups[i], nameBase, i);
}
}

View File

@@ -44,6 +44,8 @@ class LayerManager;
*/
class GPULayerManager {
public:
virtual ~GPULayerManager() = default;
/**
* Sets the value of <code>LayerGroup</code> to its corresponding
* GPU struct. OBS! Users must ensure bind has been

View File

@@ -35,14 +35,22 @@ void GPULayerRenderSettings::setValue(ghoul::opengl::ProgramObject* programObjec
gpuOpacity.setValue(programObject, layerSettings.opacity.value());
gpuGamma.setValue(programObject, layerSettings.gamma.value());
gpuMultiplier.setValue(programObject, layerSettings.multiplier.value());
if (layerSettings.useValueBlending) {
gpuValueBlending.setValue(programObject, layerSettings.valueBlending.value());
}
}
void GPULayerRenderSettings::bind(ghoul::opengl::ProgramObject* programObject,
void GPULayerRenderSettings::bind(const LayerRenderSettings& layerSettings, ghoul::opengl::ProgramObject* programObject,
const std::string& nameBase)
{
gpuOpacity.bind(programObject, nameBase + "opacity");
gpuGamma.bind(programObject, nameBase + "gamma");
gpuMultiplier.bind(programObject, nameBase + "multiplier");
if (layerSettings.useValueBlending) {
gpuValueBlending.bind(programObject, nameBase + "valueBlending");
}
}
} // namespace globebrowsing

View File

@@ -36,7 +36,7 @@ class ProgramObject;
namespace openspace {
namespace globebrowsing {
class LayerRenderSettings;
struct LayerRenderSettings;
/**
* Manages a GPU representation of a <code>LayerRenderSettings</code>
@@ -56,12 +56,15 @@ public:
* with nameBase within the provided shader program.
* After this method has been called, users may invoke setValue.
*/
void bind(ghoul::opengl::ProgramObject* programObject, const std::string& nameBase);
void bind(const LayerRenderSettings& layerSettings, ghoul::opengl::ProgramObject* programObject, const std::string& nameBase);
private:
GPUData<float> gpuOpacity;
GPUData<float> gpuGamma;
GPUData<float> gpuMultiplier;
// Optional
GPUData<float> gpuValueBlending;
};
} // namespace globebrowsing

View File

@@ -29,12 +29,25 @@
namespace openspace {
namespace globebrowsing {
Layer::Layer(const ghoul::Dictionary& layerDict)
: properties::PropertyOwner(layerDict.value<std::string>("Name"))
, _enabled(properties::BoolProperty("enabled", "enabled", false))
namespace {
const char* keyName = "Name";
const char* keyEnabled = "Enabled";
const char* keyLayerGroupID = "LayerGroupID";
const char* keySettings = "Settings";
}
Layer::Layer(layergroupid::ID id, const ghoul::Dictionary& layerDict)
: properties::PropertyOwner(layerDict.value<std::string>(keyName))
, _enabled(properties::BoolProperty("enabled", "Enabled", false))
, _reset("reset", "Reset")
{
// We add the id to the dictionary since it needs to be known by
// the tile provider
ghoul::Dictionary newLayerDict = layerDict;
newLayerDict.setValue(keyLayerGroupID, id);
_tileProvider = std::shared_ptr<tileprovider::TileProvider>(
tileprovider::TileProvider::createFromDictionary(layerDict));
tileprovider::TileProvider::createFromDictionary(newLayerDict));
// Something else went wrong and no exception was thrown
if (_tileProvider == nullptr) {
@@ -42,9 +55,24 @@ Layer::Layer(const ghoul::Dictionary& layerDict)
}
bool enabled = false; // defaults to false if unspecified
layerDict.getValue("Enabled", enabled);
layerDict.getValue(keyEnabled, enabled);
_enabled.setValue(enabled);
ghoul::Dictionary settingsDict;
if (layerDict.getValue(keySettings, settingsDict)) {
_renderSettings.setValuesFromDictionary(settingsDict);
}
if (id == layergroupid::ID::GrayScaleColorOverlays) {
_renderSettings.addProperty(_renderSettings.valueBlending);
_renderSettings.useValueBlending = true;
}
_reset.onChange([&](){
_tileProvider->reset();
});
addProperty(_enabled);
addProperty(_reset);
addPropertySubOwner(_renderSettings);
addPropertySubOwner(*_tileProvider);
@@ -54,5 +82,9 @@ ChunkTilePile Layer::getChunkTilePile(const TileIndex& tileIndex, int pileSize)
return _tileProvider->getChunkTilePile(tileIndex, pileSize);
}
void Layer::onChange(std::function<void(void)> callback) {
_enabled.onChange(callback);
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -27,10 +27,12 @@
#include <openspace/properties/propertyowner.h>
#include <modules/globebrowsing/rendering/layer/layerrendersettings.h>
#include <modules/globebrowsing/tile/chunktile.h>
#include <modules/globebrowsing/rendering/layer/layergroupid.h>
#include <modules/globebrowsing/rendering/layer/layerrendersettings.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/triggerproperty.h>
namespace openspace {
namespace globebrowsing {
@@ -46,7 +48,7 @@ namespace tileprovider {
*/
class Layer : public properties::PropertyOwner {
public:
Layer(const ghoul::Dictionary& layerDict);
Layer(layergroupid::ID id, const ghoul::Dictionary& layerDict);
ChunkTilePile getChunkTilePile(const TileIndex& tileIndex, int pileSize) const;
@@ -54,8 +56,11 @@ public:
tileprovider::TileProvider* tileProvider() const { return _tileProvider.get(); }
const LayerRenderSettings& renderSettings() const { return _renderSettings; }
void onChange(std::function<void(void)> callback);
private:
properties::BoolProperty _enabled;
properties::TriggerProperty _reset;
std::shared_ptr<tileprovider::TileProvider> _tileProvider;
LayerRenderSettings _renderSettings;
};

View File

@@ -36,21 +36,20 @@ LayerGroup::LayerGroup(std::string name)
addProperty(_levelBlendingEnabled);
}
LayerGroup::LayerGroup(std::string name, const ghoul::Dictionary& dict)
: LayerGroup(std::move(name))
LayerGroup::LayerGroup(layergroupid::ID id, const ghoul::Dictionary& dict)
: LayerGroup(layergroupid::LAYER_GROUP_NAMES[id])
{
for (size_t i = 0; i < dict.size(); i++) {
std::string dictKey = std::to_string(i + 1);
ghoul::Dictionary layerDict = dict.value<ghoul::Dictionary>(dictKey);
try {
_layers.push_back(std::make_shared<Layer>(layerDict));
_layers.push_back(std::make_shared<Layer>(id, layerDict));
}
catch (const ghoul::RuntimeError& e) {
LERRORC(e.component, e.message);
continue;
}
//_layers.push_back(std::make_shared<Layer>(layerDict));
}
for (const auto& layer : _layers) {
@@ -81,5 +80,13 @@ int LayerGroup::pileSize() const{
return _levelBlendingEnabled.value() ? 3 : 1;
}
void LayerGroup::onChange(std::function<void(void)> callback) {
_onChangeCallback = callback;
_levelBlendingEnabled.onChange(callback);
for (const std::shared_ptr<Layer>& layer : _layers) {
layer->onChange(callback);
}
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -28,11 +28,17 @@
#include <openspace/properties/propertyowner.h>
#include <modules/globebrowsing/rendering/layer/layer.h>
#include <modules/globebrowsing/rendering/layer/layergroupid.h>
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h>
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
#include <openspace/properties/scalar/boolproperty.h>
namespace openspace {
namespace globebrowsing {
namespace tileprovider {
class TileProvider;
}
@@ -42,7 +48,7 @@ namespace tileprovider {
*/
struct LayerGroup : public properties::PropertyOwner {
LayerGroup(std::string name);
LayerGroup(std::string name, const ghoul::Dictionary& dict);
LayerGroup(layergroupid::ID id, const ghoul::Dictionary& dict);
/// Updates all layers tile providers within this group
void update();
@@ -58,11 +64,14 @@ struct LayerGroup : public properties::PropertyOwner {
bool layerBlendingEnabled() const { return _levelBlendingEnabled.value(); }
void onChange(std::function<void(void)> callback);
private:
std::vector<std::shared_ptr<Layer>> _layers;
std::vector<std::shared_ptr<Layer>> _activeLayers;
properties::BoolProperty _levelBlendingEnabled;
std::function<void(void)> _onChangeCallback;
};
} // namespace globebrowsing

View File

@@ -22,24 +22,37 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___LOADJOB___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___LOADJOB___H__
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___LAYERGROUPID___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___LAYERGROUPID___H__
#include <modules/globebrowsing/other/concurrentjobmanager.h>
#include <memory>
namespace openspace {
namespace globebrowsing {
namespace layergroupid {
struct RawTile;
struct LoadJob : public Job<RawTile> {
virtual void execute() = 0;
virtual std::shared_ptr<RawTile> product() const = 0;
static const int NUM_LAYER_GROUPS = 7;
static const char* LAYER_GROUP_NAMES[NUM_LAYER_GROUPS] = {
"HeightLayers",
"ColorLayers",
"ColorOverlays",
"GrayScaleLayers",
"GrayScaleColorOverlays",
"NightLayers",
"WaterMasks"
};
enum ID {
HeightLayers,
ColorLayers,
ColorOverlays,
GrayScaleLayers,
GrayScaleColorOverlays,
NightLayers,
WaterMasks
};
} // namespace layergroupid
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___LOADJOB___H__
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___LAYERGROUPID___H__

View File

@@ -26,39 +26,31 @@
#include <modules/globebrowsing/rendering/layer/layergroup.h>
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <modules/globebrowsing/globes/chunkedlodglobe.h>
namespace openspace {
namespace globebrowsing {
const char* LayerManager::LAYER_GROUP_NAMES[NUM_LAYER_GROUPS] = {
"HeightLayers",
"ColorLayers",
"ColorOverlays",
"GrayScaleLayers",
"GrayScaleColorOverlays",
"NightLayers",
"WaterMasks"
};
LayerManager::LayerManager(const ghoul::Dictionary& layerGroupsDict)
LayerManager::LayerManager(const ghoul::Dictionary& layerGroupsDict)
: properties::PropertyOwner("Layers")
{
if (NUM_LAYER_GROUPS != layerGroupsDict.size()) {
if (layergroupid::NUM_LAYER_GROUPS != layerGroupsDict.size()) {
throw ghoul::RuntimeError(
"Number of Layer Groups must be equal to " + NUM_LAYER_GROUPS);
"Number of Layer Groups must be equal to " + layergroupid::NUM_LAYER_GROUPS);
}
// Create all the categories of tile providers
for (size_t i = 0; i < layerGroupsDict.size(); i++) {
std::string groupName = LayerManager::LAYER_GROUP_NAMES[i];
const std::string& groupName = layergroupid::LAYER_GROUP_NAMES[i];
ghoul::Dictionary layerGroupDict =
layerGroupsDict.value<ghoul::Dictionary>(groupName);
_layerGroups.push_back(
std::make_shared<LayerGroup>(groupName, layerGroupDict));
std::make_shared<LayerGroup>(
static_cast<layergroupid::ID>(i), layerGroupDict));
}
for (const auto& layerGroup : _layerGroups) {
for (const std::shared_ptr<LayerGroup>& layerGroup : _layerGroups) {
addPropertySubOwner(layerGroup.get());
}
}
@@ -67,7 +59,7 @@ const LayerGroup& LayerManager::layerGroup(size_t groupId) {
return *_layerGroups[groupId];
}
const LayerGroup& LayerManager::layerGroup(LayerGroupId groupId) {
const LayerGroup& LayerManager::layerGroup(layergroupid::ID groupId) {
return *_layerGroups[groupId];
}
@@ -85,14 +77,14 @@ const std::vector<std::shared_ptr<LayerGroup>>& LayerManager::layerGroups() cons
}
void LayerManager::update() {
for (auto& layerGroup : _layerGroups) {
for (std::shared_ptr<LayerGroup>& layerGroup : _layerGroups) {
layerGroup->update();
}
}
void LayerManager::reset(bool includeDisabled) {
for (auto& layerGroup : _layerGroups) {
for (auto layer : layerGroup->layers()) {
for (std::shared_ptr<LayerGroup>& layerGroup : _layerGroups) {
for (std::shared_ptr<Layer> layer : layerGroup->layers()) {
if (layer->enabled() || includeDisabled) {
layer->tileProvider()->reset();
}
@@ -100,5 +92,65 @@ void LayerManager::reset(bool includeDisabled) {
}
}
TileTextureInitData LayerManager::getTileTextureInitData(layergroupid::ID id,
size_t preferredTileSize)
{
switch (id) {
case layergroupid::ID::HeightLayers: {
size_t tileSize = preferredTileSize ? preferredTileSize : 64;
return TileTextureInitData(tileSize, tileSize, GL_FLOAT,
ghoul::opengl::Texture::Format::Red,
TileTextureInitData::ShouldAllocateDataOnCPU::Yes);
}
case layergroupid::ID::ColorLayers: {
size_t tileSize = preferredTileSize ? preferredTileSize : 512;
return TileTextureInitData(tileSize, tileSize, GL_UNSIGNED_BYTE,
ghoul::opengl::Texture::Format::RGBA);
}
case layergroupid::ID::ColorOverlays: {
size_t tileSize = preferredTileSize ? preferredTileSize : 512;
return TileTextureInitData(tileSize, tileSize, GL_UNSIGNED_BYTE,
ghoul::opengl::Texture::Format::RGBA);
}
case layergroupid::ID::GrayScaleLayers: {
size_t tileSize = preferredTileSize ? preferredTileSize : 512;
return TileTextureInitData(tileSize, tileSize, GL_UNSIGNED_BYTE,
ghoul::opengl::Texture::Format::RG);
}
case layergroupid::ID::GrayScaleColorOverlays: {
size_t tileSize = preferredTileSize ? preferredTileSize : 512;
return TileTextureInitData(tileSize, tileSize, GL_UNSIGNED_BYTE,
ghoul::opengl::Texture::Format::RG);
}
case layergroupid::ID::NightLayers: {
size_t tileSize = preferredTileSize ? preferredTileSize : 512;
return TileTextureInitData(tileSize, tileSize, GL_UNSIGNED_BYTE,
ghoul::opengl::Texture::Format::RGBA);
}
case layergroupid::ID::WaterMasks: {
size_t tileSize = preferredTileSize ? preferredTileSize : 512;
return TileTextureInitData(tileSize, tileSize, GL_UNSIGNED_BYTE,
ghoul::opengl::Texture::Format::RGBA);
}
default: {
ghoul_assert(false, "Unknown layer group ID");
}
}
}
bool LayerManager::shouldPerformPreProcessingOnLayergroup(layergroupid::ID id) {
// Only preprocess height layers by default
switch (id) {
case layergroupid::ID::HeightLayers: return true;
default: return false;
}
}
void LayerManager::onChange(std::function<void(void)> callback) {
for (std::shared_ptr<LayerGroup>& layerGroup : _layerGroups) {
layerGroup->onChange(callback);
}
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -27,7 +27,11 @@
#include <openspace/properties/propertyowner.h>
#include <modules/globebrowsing/rendering/layer/layergroupid.h>
#include <modules/globebrowsing/tile/chunktile.h>
#include <modules/globebrowsing/tile/tiletextureinitdata.h>
#include <functional>
namespace openspace {
namespace globebrowsing {
@@ -39,22 +43,10 @@ struct LayerGroup;
*/
class LayerManager : public properties::PropertyOwner {
public:
static const size_t NUM_LAYER_GROUPS = 7;
static const char* LAYER_GROUP_NAMES[NUM_LAYER_GROUPS];
enum LayerGroupId {
HeightLayers,
ColorLayers,
ColorOverlays,
GrayScaleLayers,
GrayScaleColorOverlays,
NightLayers,
WaterMasks
};
LayerManager(const ghoul::Dictionary& textureCategoriesDictionary);
const LayerGroup& layerGroup(size_t groupId);
const LayerGroup& layerGroup(LayerGroupId);
const LayerGroup& layerGroup(layergroupid::ID);
bool hasAnyBlendingLayersEnabled() const;
@@ -63,6 +55,12 @@ public:
void update();
void reset(bool includingDisabled = false);
static TileTextureInitData getTileTextureInitData(layergroupid::ID id,
size_t preferredTileSize = 0);
static bool shouldPerformPreProcessingOnLayergroup(layergroupid::ID id);
void onChange(std::function<void(void)> callback);
private:
std::vector<std::shared_ptr<LayerGroup>> _layerGroups;
};

View File

@@ -27,17 +27,51 @@
namespace openspace {
namespace globebrowsing {
namespace {
const char* keyOpacity = "Opacity";
const char* keyGamma = "Gamma";
const char* keyMultiplier = "Multiplier";
const char* keyValueBlending = "ValueBlending";
}
LayerRenderSettings::LayerRenderSettings()
: properties::PropertyOwner("Settings")
, opacity(properties::FloatProperty("opacity", "opacity", 1.f, 0.f, 1.f))
, gamma(properties::FloatProperty("gamma", "gamma", 1, 0, 5))
, multiplier(properties::FloatProperty("multiplier", "multiplier", 1.f, 0.f, 20.f))
, opacity(properties::FloatProperty("Opacity", "opacity", 1.f, 0.f, 1.f))
, gamma(properties::FloatProperty("Gamma", "gamma", 1, 0, 5))
, multiplier(properties::FloatProperty("Multiplier", "multiplier", 1.f, 0.f, 20.f))
, valueBlending(properties::FloatProperty("Value blending", "valueBlending",
1.f, 0.f, 1.f))
, useValueBlending(false)
{
// Implicitly added properties (other ones are not for all layer types)
addProperty(opacity);
addProperty(gamma);
addProperty(multiplier);
}
void LayerRenderSettings::setValuesFromDictionary(
const ghoul::Dictionary& renderSettingsDict)
{
float dictOpacity;
float dictGamma;
float dictMultiplier;
float dictValueBlending;
if(renderSettingsDict.getValue(keyOpacity, dictOpacity)) {
opacity.setValue(dictOpacity);
}
if(renderSettingsDict.getValue(keyGamma, dictGamma)) {
gamma.setValue(dictGamma);
}
if(renderSettingsDict.getValue(keyMultiplier, dictMultiplier)) {
multiplier.setValue(dictMultiplier);
}
if(renderSettingsDict.getValue(keyValueBlending, dictValueBlending)) {
valueBlending.setValue(dictValueBlending);
useValueBlending = true;
}
}
float LayerRenderSettings::performLayerSettings(float currentValue) const {
float newValue = currentValue;

View File

@@ -34,10 +34,17 @@ namespace globebrowsing {
struct LayerRenderSettings : public properties::PropertyOwner {
LayerRenderSettings();
properties::FloatProperty opacity;
properties::FloatProperty gamma;
properties::FloatProperty multiplier;
// Optional properties
properties::FloatProperty valueBlending;
bool useValueBlending = false;
void setValuesFromDictionary(const ghoul::Dictionary& renderSettingsDict);
/// This function matches the function with the same name in the
/// shader code
float performLayerSettings(float currentValue) const;

View File

@@ -23,6 +23,12 @@
****************************************************************************************/
#include <modules/globebrowsing/rendering/layershadermanager.h>
#include <modules/globebrowsing/globes/renderableglobe.h>
#include <modules/globebrowsing/globes/chunkedlodglobe.h>
#include <modules/globebrowsing/chunk/chunk.h>
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <modules/globebrowsing/rendering/layer/layermanager.h>
#include <modules/globebrowsing/rendering/layer/layergroup.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/renderengine.h>
@@ -56,6 +62,50 @@ bool LayerShaderManager::LayerShaderPreprocessingData::operator==(
}
}
LayerShaderManager::LayerShaderPreprocessingData
LayerShaderManager::LayerShaderPreprocessingData::get(
const RenderableGlobe& globe)
{
LayerShaderManager::LayerShaderPreprocessingData preprocessingData;
std::shared_ptr<LayerManager> layerManager = globe.chunkedLodGlobe()->layerManager();
for (size_t i = 0; i < layergroupid::NUM_LAYER_GROUPS; i++) {
LayerShaderManager::LayerShaderPreprocessingData::LayerGroupPreprocessingData
layeredTextureInfo;
auto layerGroup = layerManager->layerGroup(i);
layeredTextureInfo.lastLayerIdx = layerGroup.activeLayers().size() - 1;
layeredTextureInfo.layerBlendingEnabled = layerGroup.layerBlendingEnabled();
preprocessingData.layeredTextureInfo[i] = layeredTextureInfo;
}
const auto& generalProps = globe.generalProperties();
const auto& debugProps = globe.debugProperties();
auto& pairs = preprocessingData.keyValuePairs;
pairs.emplace_back("useAtmosphere", std::to_string(generalProps.atmosphereEnabled));
pairs.emplace_back("performShading", std::to_string(generalProps.performShading));
pairs.emplace_back("showChunkEdges", std::to_string(debugProps.showChunkEdges));
pairs.emplace_back("showHeightResolution",
std::to_string(debugProps.showHeightResolution));
pairs.emplace_back("showHeightIntensities",
std::to_string(debugProps.showHeightIntensities));
pairs.emplace_back("defaultHeight", std::to_string(Chunk::DEFAULT_HEIGHT));
pairs.emplace_back("tilePaddingStart",
"ivec2(" +
std::to_string(RawTileDataReader::padding.start.x) + "," +
std::to_string(RawTileDataReader::padding.start.y) + ")"
);
pairs.emplace_back("tilePaddingSizeDiff",
"ivec2(" +
std::to_string(RawTileDataReader::padding.numPixels.x) + "," +
std::to_string(RawTileDataReader::padding.numPixels.y) + ")"
);
return preprocessingData;
}
LayerShaderManager::LayerShaderManager(const std::string& shaderName,
const std::string& vsPath,
const std::string& fsPath)
@@ -73,14 +123,8 @@ LayerShaderManager::~LayerShaderManager() {
}
}
ghoul::opengl::ProgramObject* LayerShaderManager::programObject(
LayerShaderPreprocessingData preprocessingData)
{
_updatedOnLastCall = false;
if (!(preprocessingData == _preprocessingData) || _programObject == nullptr) {
recompileShaderProgram(preprocessingData);
_updatedOnLastCall = true;
}
ghoul::opengl::ProgramObject* LayerShaderManager::programObject() const {
ghoul_assert(_programObject, "Program does not exist. Needs to be compiled!");
return _programObject.get();
}
@@ -96,7 +140,7 @@ void LayerShaderManager::recompileShaderProgram(
for (size_t i = 0; i < textureTypes.size(); i++) {
// lastLayerIndex must be at least 0 for the shader to compile,
// the layer type is inactivated by setting use to false
std::string groupName = LayerManager::LAYER_GROUP_NAMES[i];
std::string groupName = layergroupid::LAYER_GROUP_NAMES[i];
shaderDictionary.setValue(
"lastLayerIndex" + groupName,
glm::max(textureTypes[i].lastLayerIdx, 0)
@@ -130,6 +174,7 @@ void LayerShaderManager::recompileShaderProgram(
ghoul_assert(_programObject != nullptr, "Failed to initialize programObject!");
using IgnoreError = ghoul::opengl::ProgramObject::ProgramObject::IgnoreError;
_programObject->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes);
_updatedOnLastCall = true;
}
bool LayerShaderManager::updatedOnLastCall() {

View File

@@ -39,6 +39,8 @@ class ProgramObject;
namespace openspace {
namespace globebrowsing {
class RenderableGlobe;
/**
* This class has ownership of an updated shader program for rendering tiles.
*/
@@ -64,10 +66,12 @@ public:
bool operator==(const LayerGroupPreprocessingData& other) const;
};
std::array<LayerGroupPreprocessingData, LayerManager::NUM_LAYER_GROUPS>
std::array<LayerGroupPreprocessingData, layergroupid::NUM_LAYER_GROUPS>
layeredTextureInfo;
std::vector<std::pair<std::string, std::string> > keyValuePairs;
bool operator==(const LayerShaderPreprocessingData& other) const;
static LayerShaderPreprocessingData get(const RenderableGlobe&);
};
LayerShaderManager(
@@ -78,20 +82,15 @@ public:
/**
* Returns a pointer to a <code>ProgramObject</code> for rendering tiles.
* \param <code>preprocessingData</code> determines wherer or not the shader
* program needs to be re-compiled. If <code>preprocessingData</code> is different
* from the last time this function was called the shader program will be
* recompiled before returned.
*/
ghoul::opengl::ProgramObject* programObject(
LayerShaderPreprocessingData preprocessingData);
ghoul::opengl::ProgramObject* programObject() const;
bool updatedOnLastCall();
private:
void recompileShaderProgram(LayerShaderPreprocessingData preprocessingData);
private:
std::unique_ptr<ghoul::opengl::ProgramObject> _programObject;
LayerShaderPreprocessingData _preprocessingData;

View File

@@ -146,11 +146,12 @@ float calculateHeight(
}
vec4 calculateColor(
const vec4 currentColor,
const vec2 uv,
LevelWeights levelWeights,
const Layer ColorLayers[NUMLAYERS_COLORTEXTURE]) {
vec4 color = vec4(0);
vec4 color = currentColor;
// The shader compiler will remove unused code when variables are multiplied by
// a constant 0
@@ -309,6 +310,8 @@ vec4 calculateGrayScaleOverlay(
const Layer GrayScaleColorOverlays[NUMLAYERS_GRAYSCALE_OVERLAY]) {
vec4 colorGrayScale = currentColor;
// HSV blending
vec3 hsvCurrent = rgb2hsv(currentColor.rgb);
// The shader compiler will remove unused code when variables are multiplied by
// a constant 0
@@ -324,12 +327,12 @@ vec4 calculateGrayScaleOverlay(
colorSample = performLayerSettings(colorSample, GrayScaleColorOverlays[#{i}].settings);
colorGrayScale = blendOver(colorGrayScale, colorSample);
float valueBlending = GrayScaleColorOverlays[#{i}].settings.valueBlending;
hsvCurrent.z = colorGrayScale.r * valueBlending + hsvCurrent.b * (1 - valueBlending);
}
#endfor
// HSV blending
vec3 hsvCurrent = rgb2hsv(currentColor.rgb);
vec3 hsvNew = vec3(hsvCurrent.x, hsvCurrent.y, colorGrayScale.r);
vec3 hsvNew = vec3(hsvCurrent.x, hsvCurrent.y, hsvCurrent.z);
vec3 rgbNew = hsv2rgb(hsvNew);
/*
// HSL blending

View File

@@ -112,6 +112,7 @@ struct LayerSettings {
float opacity;
float gamma;
float multiplier;
float valueBlending;
};
struct Layer {

View File

@@ -117,11 +117,12 @@ float getUntransformedTileVertexHeight(vec2 uv, LevelWeights levelWeights){
*/
vec4 getTileFragColor(){
vec4 color = vec4(0.1,0.1,0.1,1);
vec4 color = vec4(0.3,0.3,0.3,1);
#if USE_COLORTEXTURE
color = calculateColor(
color,
fs_uv,
levelWeights,
ColorLayers);

View File

@@ -24,31 +24,62 @@
#include <modules/globebrowsing/tile/asynctiledataprovider.h>
#include <modules/globebrowsing/tile/loadjob/tileloadjob.h>
#include <modules/globebrowsing/other/lruthreadpool.h>
#include <modules/globebrowsing/tile/tileloadjob.h>
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <modules/globebrowsing/tile/tilediskcache.h>
#include <modules/globebrowsing/tile/tiletextureinitdata.h>
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/moduleengine.h>
#include <ghoul/logging/logmanager.h>
namespace openspace {
namespace globebrowsing {
AsyncTileDataProvider::AsyncTileDataProvider(
const std::shared_ptr<RawTileDataReader> rawTileDataReader,
std::shared_ptr<ThreadPool> pool)
: _rawTileDataReader(rawTileDataReader)
, _concurrentJobManager(pool)
{}
namespace {
const char* _loggerCat = "AsyncTileDataProvider";
}
AsyncTileDataProvider::AsyncTileDataProvider(const std::string& name,
const std::shared_ptr<RawTileDataReader> rawTileDataReader)
: _name(name)
, _rawTileDataReader(rawTileDataReader)
, _concurrentJobManager(
std::make_shared<LRUThreadPool<TileIndex::TileHashKey>>(1, 10))
, _pboContainer(nullptr)
, _resetMode(ResetMode::ShouldResetAllButRawTileDataReader)
{
_globeBrowsingModule = OsEng.moduleEngine().module<GlobeBrowsingModule>();
performReset(ResetRawTileDataReader::No);
}
std::shared_ptr<RawTileDataReader> AsyncTileDataProvider::getRawTileDataReader() const {
return _rawTileDataReader;
}
bool AsyncTileDataProvider::enqueueTileIO(const TileIndex& tileIndex) {
if (satisfiesEnqueueCriteria(tileIndex)) {
auto job = std::make_shared<TileLoadJob>(_rawTileDataReader, tileIndex);
//auto job = std::make_shared<DiskCachedTileLoadJob>(_tileDataset, tileIndex, tileDiskCache, "ReadAndWrite");
_concurrentJobManager.enqueueJob(job);
_enqueuedTileRequests[tileIndex.hashKey()] = tileIndex;
if (_resetMode == ResetMode::ShouldNotReset && satisfiesEnqueueCriteria(tileIndex)) {
if (_pboContainer) {
char* dataPtr = static_cast<char*>(_pboContainer->mapBuffer(
tileIndex.hashKey(), PixelBuffer::Access::WriteOnly));
if (dataPtr) {
auto job = std::make_shared<TileLoadJob>(_rawTileDataReader, tileIndex,
dataPtr);
_concurrentJobManager.enqueueJob(job, tileIndex.hashKey());
_enqueuedTileRequests.insert(tileIndex.hashKey());
}
else {
return false;
}
}
else {
auto job = std::make_shared<TileLoadJob>(_rawTileDataReader, tileIndex);
_concurrentJobManager.enqueueJob(job, tileIndex.hashKey());
_enqueuedTileRequests.insert(tileIndex.hashKey());
}
return true;
}
return false;
@@ -56,43 +87,159 @@ bool AsyncTileDataProvider::enqueueTileIO(const TileIndex& tileIndex) {
std::vector<std::shared_ptr<RawTile>> AsyncTileDataProvider::getRawTiles() {
std::vector<std::shared_ptr<RawTile>> readyResults;
while (_concurrentJobManager.numFinishedJobs() > 0) {
readyResults.push_back(_concurrentJobManager.popFinishedJob()->product());
std::shared_ptr<RawTile> finishedJob = popFinishedRawTile();
while (finishedJob) {
readyResults.push_back(finishedJob);
finishedJob = popFinishedRawTile();
}
return readyResults;
}
std::shared_ptr<RawTile> AsyncTileDataProvider::popFinishedRawTile() {
if (_concurrentJobManager.numFinishedJobs() > 0)
return _concurrentJobManager.popFinishedJob()->product();
if (_concurrentJobManager.numFinishedJobs() > 0) {
// Now the tile load job looses ownerwhip of the data pointer
std::shared_ptr<RawTile> product =
_concurrentJobManager.popFinishedJob()->product();
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 (_pboContainer) {
product->pbo = _pboContainer->idOfMappedBuffer(key);
// Now we are finished with the mapping of this pbo
_pboContainer->unMapBuffer(key);
}
else {
product->pbo = 0;
if (product->error != RawTile::ReadError::None) {
delete [] product->imageData;
return nullptr;
}
}
return product;
}
else
return nullptr;
}
bool AsyncTileDataProvider::satisfiesEnqueueCriteria(const TileIndex& tileIndex) const {
// only allow tile to be enqueued if it's not already enqueued
//return _futureTileIOResults.find(tileIndex.hashKey()) == _futureTileIOResults.end();
return _enqueuedTileRequests.find(tileIndex.hashKey()) == _enqueuedTileRequests.end();
bool AsyncTileDataProvider::satisfiesEnqueueCriteria(const TileIndex& tileIndex) {
// Only satisfies if it is not already enqueued. Also bumps the request to the top.
bool alreadyEnqueued = _concurrentJobManager.touch(tileIndex.hashKey());
// Concurrent job manager can start jobs which will pop them from enqueued, however
// they are still in _enqueuedTileRequests until finished
bool notFoundAmongEnqueued =
_enqueuedTileRequests.find(tileIndex.hashKey()) == _enqueuedTileRequests.end();
return !alreadyEnqueued && notFoundAmongEnqueued;
}
void AsyncTileDataProvider::endUnfinishedJobs() {
std::vector<TileIndex::TileHashKey> unfinishedJobs =
_concurrentJobManager.getKeysToUnfinishedJobs();
for (TileIndex::TileHashKey unfinishedJob : unfinishedJobs) {
// Unmap unfinished jobs
if (_pboContainer) {
_pboContainer->unMapBuffer(unfinishedJob);
}
// When erasing the job before
_enqueuedTileRequests.erase(unfinishedJob);
}
}
void AsyncTileDataProvider::endEnqueuedJobs() {
std::vector<TileIndex::TileHashKey> enqueuedJobs =
_concurrentJobManager.getKeysToEnqueuedJobs();
for (const TileIndex::TileHashKey& enqueuedJob : enqueuedJobs) {
// Unmap unfinished jobs
if (_pboContainer) {
_pboContainer->unMapBuffer(enqueuedJob);
}
// When erasing the job before
_enqueuedTileRequests.erase(enqueuedJob);
}
}
void AsyncTileDataProvider::updatePboUsage() {
bool usingPbo = _pboContainer != nullptr;
bool shouldUsePbo = _globeBrowsingModule->tileCache()->shouldUsePbo();
// If changed, we need to reset the async tile data provider.
// No need to reset the raw tile data reader when changing PBO usage.
if (usingPbo != shouldUsePbo &&
_resetMode != ResetMode::ShouldResetAllButRawTileDataReader) {
_resetMode = ResetMode::ShouldResetAllButRawTileDataReader;
LINFO(std::string("PBO usage updated, prepairing for resetting of tile reader ") +
"'" + _name + "'");
}
}
void AsyncTileDataProvider::update() {
updatePboUsage();
endUnfinishedJobs();
// May reset
switch (_resetMode) {
case ResetMode::ShouldResetAll: {
// Clean all finished jobs
getRawTiles();
// Only allow resetting if there are no jobs currently running
if (_enqueuedTileRequests.size() == 0) {
performReset(ResetRawTileDataReader::Yes);
LINFO(std::string("Tile data reader ") + "'" + _name + "'" +
" reset successfully.");
}
break;
}
case ResetMode::ShouldResetAllButRawTileDataReader: {
// Clean all finished jobs
getRawTiles();
// Only allow resetting if there are no jobs currently running
if (_enqueuedTileRequests.size() == 0) {
performReset(ResetRawTileDataReader::No);
LINFO(std::string("Tile data reader ") + "'" + _name + "'" +
" reset successfully.");
}
break;
}
case ResetMode::ShouldNotReset:
break;
default:
break;
}
}
void AsyncTileDataProvider::reset() {
//_futureTileIOResults.clear();
//_threadPool->stop(ghoul::ThreadPool::RunRemainingTasks::No);
//_threadPool->start();
_enqueuedTileRequests.clear();
_concurrentJobManager.reset();
while (_concurrentJobManager.numFinishedJobs() > 0) {
_concurrentJobManager.popFinishedJob();
}
_rawTileDataReader->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(std::string("Prepairing for resetting of tile reader ") +
"'" + _name + "'");
}
void AsyncTileDataProvider::clearRequestQueue() {
//_threadPool->clearRemainingTasks();
//_futureTileIOResults.clear();
_concurrentJobManager.clearEnqueuedJobs();
_enqueuedTileRequests.clear();
void AsyncTileDataProvider::performReset(ResetRawTileDataReader resetRawTileDataReader) {
ghoul_assert(_enqueuedTileRequests.size() == 0, "No enqueued requests left");
// Re-initialize PBO container
if (_globeBrowsingModule->tileCache()->shouldUsePbo()) {
size_t pboNumBytes = _rawTileDataReader->tileTextureInitData().totalNumBytes();
_pboContainer = std::make_unique<PixelBufferContainer<TileIndex::TileHashKey>>(
pboNumBytes, PixelBuffer::Usage::StreamDraw, 10
);
}
else {
_pboContainer = nullptr;
}
// Reset raw tile data reader
if (resetRawTileDataReader == ResetRawTileDataReader::Yes) {
_rawTileDataReader->reset();
}
// Finished resetting
_resetMode = ResetMode::ShouldNotReset;
}
float AsyncTileDataProvider::noDataValueAsFloat() const {

View File

@@ -25,39 +25,102 @@
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___ASYNC_TILE_DATAPROVIDER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___ASYNC_TILE_DATAPROVIDER___H__
#include <modules/globebrowsing/other/concurrentjobmanager.h>
#include <modules/globebrowsing/globebrowsingmodule.h>
#include <modules/globebrowsing/other/prioritizingconcurrentjobmanager.h>
#include <modules/globebrowsing/other/pixelbuffercontainer.h>
#include <modules/globebrowsing/tile/tileindex.h>
#include <ghoul/misc/boolean.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <map>
#include <set>
#include <unordered_map>
namespace openspace {
namespace globebrowsing {
//class GlobeBrowsingModule;
struct RawTile;
class RawTileDataReader;
/**
* The responsibility of this class is to enqueue tile requests and fetching finished
* <code>RawTile</code>s that has been asynchronously loaded.
*/
class AsyncTileDataProvider {
public:
AsyncTileDataProvider(std::shared_ptr<RawTileDataReader> textureDataProvider,
std::shared_ptr<ThreadPool> pool);
/**
* \param textureDataProvider is the reader that will be used for the asynchronos
* tile loading.
*/
AsyncTileDataProvider(const std::string& name,
std::shared_ptr<RawTileDataReader> textureDataProvider);
bool enqueueTileIO(const TileIndex& tileIndex);
/**
* Creates a job which asynchronously loads a raw tile. This job is enqueued.
*/
bool enqueueTileIO(const TileIndex& tileIndex);
/**
* Get all finished jobs.
*/
std::vector<std::shared_ptr<RawTile>> getRawTiles();
/**
* Get one finished job.
*/
std::shared_ptr<RawTile> popFinishedRawTile();
void update();
void reset();
void clearRequestQueue();
std::shared_ptr<RawTileDataReader> getRawTileDataReader() const;
float noDataValueAsFloat() const;
protected:
virtual bool satisfiesEnqueueCriteria(const TileIndex&) const;
using ResetRawTileDataReader = ghoul::Boolean;
enum class ResetMode {
ShouldResetAll,
ShouldResetAllButRawTileDataReader,
ShouldNotReset
};
/**
* \returns true if tile of index <code>tileIndex</code> is not already enqueued.
*/
bool satisfiesEnqueueCriteria(const TileIndex& tileIndex);
/**
* An unfinished job is a load tile job that has been popped from the thread pool due
* to its low priority. Once it has been popped, it is marked as unfinished and needs
* to be explicitly ended.
*/
void endUnfinishedJobs();
void endEnqueuedJobs();
void updatePboUsage();
void performReset(ResetRawTileDataReader resetRawTileDataReader);
private:
const std::string _name;
GlobeBrowsingModule* _globeBrowsingModule;
/// The reader used for asynchronous reading
std::shared_ptr<RawTileDataReader> _rawTileDataReader;
ConcurrentJobManager<RawTile> _concurrentJobManager;
std::unordered_map<TileIndex::TileHashKey, TileIndex> _enqueuedTileRequests;
PrioritizingConcurrentJobManager<RawTile, TileIndex::TileHashKey>
_concurrentJobManager;
/// nullptr if pbo is not used for texture uploading. Otherwise initialized.
std::unique_ptr<PixelBufferContainer<TileIndex::TileHashKey>> _pboContainer;
std::set<TileIndex::TileHashKey> _enqueuedTileRequests;
ResetMode _resetMode;
};
} // namespace globebrowsing

View File

@@ -1,83 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* 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/tile/loadjob/diskcachedtileloadjob.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <modules/globebrowsing/tile/tilediskcache.h>
namespace openspace {
namespace globebrowsing {
DiskCachedTileLoadJob::DiskCachedTileLoadJob(
std::shared_ptr<RawTileDataReader> textureDataProvider,
const TileIndex& tileIndex, std::shared_ptr<TileDiskCache> tdc,
CacheMode m)
: TileLoadJob(textureDataProvider, tileIndex)
, _tileDiskCache(tdc)
, _mode(m)
{}
void DiskCachedTileLoadJob::execute() {
_rawTile = nullptr;
switch (_mode) {
case CacheMode::Disabled:
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
break;
case CacheMode::ReadOnly:
_rawTile = _tileDiskCache->get(_chunkIndex);
if (_rawTile == nullptr) {
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
}
break;
case CacheMode::ReadAndWrite:
_rawTile = _tileDiskCache->get(_chunkIndex);
if (_rawTile == nullptr) {
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
_tileDiskCache->put(_chunkIndex, _rawTile);
}
break;
case CacheMode::WriteOnly:
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
_tileDiskCache->put(_chunkIndex, _rawTile);
break;
case CacheMode::CacheHitsOnly:
_rawTile = _tileDiskCache->get(_chunkIndex);
if (_rawTile == nullptr) {
RawTile res = RawTile::createDefaultRes();
res.tileIndex = _chunkIndex;
_rawTile = std::make_shared<RawTile>(res);
}
break;
}
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -25,65 +25,27 @@
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/tilemetadata.h>
#include <modules/globebrowsing/tile/tiletextureinitdata.h>
namespace openspace {
namespace globebrowsing {
RawTile::RawTile()
: imageData(nullptr)
, dimensions(0, 0, 0)
, tileMetaData(nullptr)
, textureInitData(nullptr)
, tileIndex(0, 0, 0)
, error(ReadError::None)
, nBytesImageData(0)
, pbo(0)
{}
RawTile RawTile::createDefaultRes() {
RawTile RawTile::createDefault(const TileTextureInitData& initData) {
RawTile defaultRes;
int w = 8;
int h = 8;
defaultRes.dimensions = glm::uvec3(w, h, 1);
defaultRes.nBytesImageData = w * h * 1 * 3 * 4; // assume max 3 channels, max 4 bytes per pixel
defaultRes.imageData = new char[defaultRes.nBytesImageData];
std::fill_n((char*)defaultRes.imageData, defaultRes.nBytesImageData, 0);
defaultRes.textureInitData = std::make_shared<TileTextureInitData>(initData);
defaultRes.imageData = new char[initData.totalNumBytes()];
std::fill_n(static_cast<char*>(defaultRes.imageData), initData.totalNumBytes(), 0);
return defaultRes;
}
void RawTile::serializeMetaData(std::ostream& os) {
os << dimensions.x << " " << dimensions.y << " " << dimensions.z << std::endl;
os << tileIndex.x << " " << tileIndex.y << " " << tileIndex.level << std::endl;
os << static_cast<int>(error) << std::endl;
// preprocess data
os << (tileMetaData != nullptr) << std::endl;
if (tileMetaData != nullptr) {
tileMetaData->serialize(os);
}
os << nBytesImageData << std::endl;
}
RawTile RawTile::deserializeMetaData(std::istream& is) {
RawTile res;
is >> res.dimensions.x >> res.dimensions.y >> res.dimensions.z;
is >> res.tileIndex.x >> res.tileIndex.y >> res.tileIndex.level;
int err; is >> err; res.error = static_cast<ReadError>(err);
res.tileMetaData = nullptr;
bool hastileMetaData;
is >> hastileMetaData;
if (hastileMetaData) {
TileMetaData tileMetaData = TileMetaData::deserialize(is);
res.tileMetaData = std::make_shared<TileMetaData>(tileMetaData);
}
is >> res.nBytesImageData;
char binaryDataSeparator;
is >> binaryDataSeparator; // not used
// char* buffer = new char[res.nBytesImageData]();
return res;
}
} // namespace globebrowsing

View File

@@ -37,6 +37,7 @@ namespace openspace {
namespace globebrowsing {
struct TileMetaData;
class TileTextureInitData;
struct RawTile {
@@ -51,18 +52,13 @@ struct RawTile {
RawTile();
char* imageData;
glm::uvec3 dimensions;
std::shared_ptr<TileMetaData> tileMetaData;
std::shared_ptr<TileTextureInitData> textureInitData;
TileIndex tileIndex;
ReadError error;
size_t nBytesImageData;
GLuint glType;
TextureFormat textureFormat;
void serializeMetaData(std::ostream& s);
static RawTile deserializeMetaData(std::istream& s);
GLuint pbo;
static RawTile createDefaultRes();
static RawTile createDefault(const TileTextureInitData& initData);
};
} // namespace globebrowsing

View File

@@ -42,6 +42,8 @@
#include <gdal_priv.h>
#include <algorithm>
namespace {
const std::string _loggerCat = "GdalRawTileDataReader";
}
@@ -55,15 +57,14 @@ std::ostream& operator<<(std::ostream& os, const PixelRegion& pr) {
}
GdalRawTileDataReader::GdalRawTileDataReader(const std::string& filePath,
const Configuration& config,
const std::string& baseDirectory)
: RawTileDataReader(config)
const TileTextureInitData& initData,
const std::string& baseDirectory,
RawTileDataReader::PerformPreprocessing preprocess)
: RawTileDataReader(initData, preprocess)
, _dataset(nullptr)
{
std::string initDir = baseDirectory.empty() ? CPLGetCurrentDir() : baseDirectory;
_initData = { initDir, filePath, config.tilePixelSize, config.dataType };
_initDirectory = baseDirectory.empty() ? CPLGetCurrentDir() : baseDirectory;
_datasetFilePath = filePath;
ensureInitialized();
}
@@ -98,7 +99,7 @@ int GdalRawTileDataReader::maxChunkLevel() {
float GdalRawTileDataReader::noDataValueAsFloat() const {
float noDataValue;
if (_dataset && _dataset->GetRasterBand(1)) {
noDataValue = _dataset->GetRasterBand(1)->GetNoDataValue();;
noDataValue = _dataset->GetRasterBand(1)->GetNoDataValue();
}
else {
noDataValue = std::numeric_limits<float>::min();
@@ -137,7 +138,8 @@ IODescription GdalRawTileDataReader::getIODescription(const TileIndex& tileIndex
// write region starts in origin
io.write.region.start = PixelRegion::PixelCoordinate(0, 0);
io.write.region.numPixels = PixelRegion::PixelCoordinate(_initData.tilePixelSize, _initData.tilePixelSize);
io.write.region.numPixels = PixelRegion::PixelCoordinate(
_initData.dimensionsWithoutPadding().x, _initData.dimensionsWithoutPadding().y);
io.read.overview = 0;
io.read.fullRegion = gdalPixelRegion(
@@ -145,7 +147,8 @@ IODescription GdalRawTileDataReader::getIODescription(const TileIndex& tileIndex
// For correct sampling in dataset, we need to pad the texture tile
PixelRegion scaledPadding = padding;
double scale = io.read.region.numPixels.x / static_cast<double>(io.write.region.numPixels.x);
double scale =
io.read.region.numPixels.x / static_cast<double>(io.write.region.numPixels.x);
scaledPadding.numPixels *= scale;
scaledPadding.start *= scale;
@@ -153,97 +156,91 @@ IODescription GdalRawTileDataReader::getIODescription(const TileIndex& tileIndex
io.write.region.pad(padding);
io.write.region.start = PixelRegion::PixelCoordinate(0, 0);
io.write.bytesPerLine = _initData.bytesPerLine();
io.write.totalNumBytes = _initData.totalNumBytes();
io.write.bytesPerLine = _dataLayout.bytesPerPixel * io.write.region.numPixels.x;
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
// OpenGL does not like if the number of bytes per line is not 4
if (io.write.bytesPerLine % 4 != 0) {
io.write.region.roundUpNumPixelToNearestMultipleOf(4);
io.write.bytesPerLine = _dataLayout.bytesPerPixel * io.write.region.numPixels.x;
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
}
ghoul_assert(io.write.region.numPixels.x == io.write.region.numPixels.y, "");
ghoul_assert(io.write.region.numPixels.x == _initData.dimensionsWithPadding().x, "");
return io;
}
void GdalRawTileDataReader::initialize() {
_dataset = openGdalDataset(_initData.datasetFilePath);
_dataset = openGdalDataset(_datasetFilePath);
//Do any other initialization needed for the GdalRawTileDataReader
_dataLayout = getTileDataLayout(_initData.dataType);
// Assume all raster bands have the same data type
_gdalType = tiledatatype::getGdalDataType(_initData.glType());
_depthTransform = calculateTileDepthTransform();
_cached._tileLevelDifference =
calculateTileLevelDifference(_initData.tilePixelSize);
calculateTileLevelDifference(_initData.dimensionsWithoutPadding().x);
}
char* GdalRawTileDataReader::readImageData(
IODescription& io, RawTile::ReadError& worstError) const {
// allocate memory for the image
char* imageData = new char[io.write.totalNumBytes];
void GdalRawTileDataReader::readImageData(
IODescription& io, RawTile::ReadError& worstError, char* imageDataDest) const {
// Only read the minimum number of rasters
int nRastersToRead = std::min(_dataset->GetRasterCount(),
static_cast<int>(_initData.nRasters()));
// In case there are extra channels not existing in the GDAL dataset
// we set the bytes to 255 (for example an extra alpha channel that)
// needs to be 1.
if (_dataLayout.numRasters > _dataLayout.numRastersAvailable) {
memset(imageData, 255, io.write.totalNumBytes);
}
if (_dataLayout.numRastersAvailable == 3) // RGB -> BGR
{
for (size_t i = 0; i < _dataLayout.numRastersAvailable; i++) {
// The final destination pointer is offsetted by one datum byte size
// for every raster (or data channel, i.e. R in RGB)
char* dataDestination = imageData + (i * _dataLayout.bytesPerDatum);
switch (_initData.ghoulTextureFormat()) {
case ghoul::opengl::Texture::Format::Red:
case ghoul::opengl::Texture::Format::RG:
case ghoul::opengl::Texture::Format::RGB:
case ghoul::opengl::Texture::Format::RGBA: {
// Read the data (each rasterband is a separate channel)
for (int i = 0; i < nRastersToRead; i++) {
// The final destination pointer is offsetted by one datum byte size
// for every raster (or data channel, i.e. R in RGB)
char* dataDestination = imageDataDest + (i * _initData.bytesPerDatum());
RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination);
RawTile::ReadError err = repeatedRasterRead(i + 1, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
break;
}
case ghoul::opengl::Texture::Format::BGR: {
// Read the data (each rasterband is a separate channel)
for (int i = 0; i < 3 && i < nRastersToRead; i++) {
// The final destination pointer is offsetted by one datum byte size
// for every raster (or data channel, i.e. R in RGB)
char* dataDestination = imageDataDest + (i * _initData.bytesPerDatum());
RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
break;
}
case ghoul::opengl::Texture::Format::BGRA: {
for (int i = 0; i < 3 && i < nRastersToRead; i++) {
// The final destination pointer is offsetted by one datum byte size
// for every raster (or data channel, i.e. R in RGB)
char* dataDestination = imageDataDest + (i * _initData.bytesPerDatum());
RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
if (nRastersToRead > 3) { // Alpha channel exists
// Last read is the alpha channel
char* dataDestination = imageDataDest + (3 * _initData.bytesPerDatum());
RawTile::ReadError err = repeatedRasterRead(4, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
break;
}
default: {
ghoul_assert(false, "Texture format not supported for tiles");
break;
}
}
else if (_dataLayout.numRastersAvailable == 4) // RGBA -> BGRA
{
for (size_t i = 0; i < 3; i++) {
// The final destination pointer is offsetted by one datum byte size
// for every raster (or data channel, i.e. R in RGB)
char* dataDestination = imageData + (i * _dataLayout.bytesPerDatum);
RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
// The final destination pointer is offsetted by one datum byte size
// for every raster (or data channel, i.e. R in RGB)
char* dataDestination = imageData + (3 * _dataLayout.bytesPerDatum);
RawTile::ReadError err = repeatedRasterRead(4, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
else
{
// Read the data (each rasterband is a separate channel)
for (size_t i = 0; i < _dataLayout.numRastersAvailable; i++) {
GDALRasterBand* rasterBand = _dataset->GetRasterBand(i + 1);
// The final destination pointer is offsetted by one datum byte size
// for every raster (or data channel, i.e. R in RGB)
char* dataDestination = imageData + (i * _dataLayout.bytesPerDatum);
RawTile::ReadError err = repeatedRasterRead(i + 1, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
}
// GDAL reads pixel lines top to bottom, we want the opposite
return imageData;
}
RawTile::ReadError GdalRawTileDataReader::rasterRead(
@@ -257,7 +254,7 @@ RawTile::ReadError GdalRawTileDataReader::rasterRead(
PixelRegion::PixelCoordinate end = io.write.region.end();
size_t largestIndex =
(end.y - 1) * io.write.bytesPerLine + (end.x - 1) * _dataLayout.bytesPerPixel;
(end.y - 1) * io.write.bytesPerLine + (end.x - 1) * _initData.bytesPerPixel();
ghoul_assert(largestIndex <= io.write.totalNumBytes, "Invalid write region");
char* dataDest = dataDestination;
@@ -270,9 +267,10 @@ RawTile::ReadError GdalRawTileDataReader::rasterRead(
// handle requested write region. Note -= since flipped y axis
dataDest -= io.write.region.start.y * io.write.bytesPerLine;
dataDest += io.write.region.start.x * _dataLayout.bytesPerPixel;
dataDest += io.write.region.start.x * _initData.bytesPerPixel();
CPLErr readError = _dataset->GetRasterBand(rasterBand)->RasterIO(
GDALRasterBand* gdalRasterBand = _dataset->GetRasterBand(rasterBand);
CPLErr readError = gdalRasterBand->RasterIO(
GF_Read,
io.read.region.start.x, // Begin read x
io.read.region.start.y, // Begin read y
@@ -282,7 +280,7 @@ RawTile::ReadError GdalRawTileDataReader::rasterRead(
io.write.region.numPixels.x, // width to write x in destination
io.write.region.numPixels.y, // width to write y in destination
_gdalType, // Type
_dataLayout.bytesPerPixel, // Pixel spacing
_initData.bytesPerPixel(), // Pixel spacing
-io.write.bytesPerLine // Line spacing
);
@@ -300,14 +298,15 @@ RawTile::ReadError GdalRawTileDataReader::rasterRead(
}
GDALDataset* GdalRawTileDataReader::openGdalDataset(const std::string& filePath) {
GDALDataset* dataset = static_cast<GDALDataset*>(GDALOpen(filePath.c_str(), GA_ReadOnly));
GDALDataset* dataset = static_cast<GDALDataset*>(
GDALOpen(filePath.c_str(), GA_ReadOnly));
if (!dataset) {
using namespace ghoul::filesystem;
std::string correctedPath = FileSystem::ref().pathByAppendingComponent(
_initData.initDirectory, filePath
_initDirectory, filePath
);
dataset = (GDALDataset *)GDALOpen(correctedPath.c_str(), GA_ReadOnly);
dataset = static_cast<GDALDataset*>(GDALOpen(correctedPath.c_str(), GA_ReadOnly));
if (!dataset) {
throw ghoul::RuntimeError("Failed to load dataset:\n" + filePath);
}
@@ -377,35 +376,6 @@ PixelRegion GdalRawTileDataReader::gdalPixelRegion(GDALRasterBand* rasterBand) c
return gdalRegion;
}
TileDataLayout GdalRawTileDataReader::getTileDataLayout(GLuint preferredGlType) {
TileDataLayout layout;
// Assume all raster bands have the same data type
_gdalType = preferredGlType != 0 ?
tiledatatype::getGdalDataType(preferredGlType) :
_dataset->GetRasterBand(1)->GetRasterDataType();
layout.glType = tiledatatype::getOpenGLDataType(_gdalType);
layout.numRastersAvailable = _dataset->GetRasterCount();
layout.numRasters = layout.numRastersAvailable;
// This is to avoid corrupted textures that can appear when the number of
// bytes per row is not a multiplie of 4.
// Info here: https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_layout
// This also mean that we need to make sure not to read from non existing
// rasters from the GDAL dataset
if (layout.numRasters == 3) {
layout.numRasters = 4;
}
layout.bytesPerDatum = tiledatatype::numberOfBytes(_gdalType);
layout.bytesPerPixel = layout.bytesPerDatum * layout.numRasters;
layout.textureFormat = tiledatatype::getTextureFormatOptimized(layout.numRasters, _gdalType);
return layout;
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -30,7 +30,6 @@
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
@@ -62,8 +61,13 @@ public:
* \param config, Configuration used for initialization
* \param baseDirectory, the base directory to use in future loading operations
*/
GdalRawTileDataReader(const std::string& filePath, const Configuration& config,
const std::string& baseDirectory = "");
GdalRawTileDataReader(const std::string& filePath,
const TileTextureInitData& initData,
const std::string& baseDirectory = "",
RawTileDataReader::PerformPreprocessing preprocess =
RawTileDataReader::PerformPreprocessing::No
);
virtual ~GdalRawTileDataReader() override;
@@ -90,8 +94,8 @@ protected:
private:
// Private virtual function overloading
virtual void initialize() override;
virtual char* readImageData(
IODescription& io, RawTile::ReadError& worstError) const override;
virtual void readImageData(IODescription& io, RawTile::ReadError& worstError,
char* dataDestination) const override;
virtual RawTile::ReadError rasterRead(
int rasterBand, const IODescription& io, char* dst) const override;
@@ -103,18 +107,13 @@ private:
int gdalOverview(const TileIndex& tileIndex) const;
int gdalVirtualOverview(const TileIndex& tileIndex) const;
PixelRegion gdalPixelRegion(GDALRasterBand* rasterBand) const;
TileDataLayout getTileDataLayout(GLuint prefferedGLType);
// Member variables
struct InitData {
std::string initDirectory;
std::string datasetFilePath;
int tilePixelSize;
GLuint dataType;
} _initData;
std::string _initDirectory;
std::string _datasetFilePath;
GDALDataset* _dataset;
GDALDataType _gdalType; // The type to reinterpret to when reading
GDALDataType _gdalType;
};
} // namespace globebrowsing

View File

@@ -81,8 +81,8 @@ private:
void setGdalProxyConfiguration();
properties::IntProperty _gdalMaximumCacheSize;
properties::BoolProperty _logGdalErrors;
properties::IntProperty _gdalMaximumCacheSize;
static GdalWrapper* _singleton;
static std::mutex _mutexLock;

View File

@@ -26,10 +26,6 @@
#include <modules/globebrowsing/tile/pixelregion.h>
namespace {
const char* _loggerCat = "IODescription";
}
namespace openspace {
namespace globebrowsing {

View File

@@ -25,12 +25,7 @@
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___IO_DESCRIPTION___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___IO_DESCRIPTION___H__
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>

View File

@@ -27,8 +27,8 @@
#include <modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiletextureinitdata.h>
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
@@ -56,16 +56,15 @@
namespace openspace {
namespace globebrowsing {
const glm::ivec2 RawTileDataReader::tilePixelStartOffset = glm::ivec2(-2);
const glm::ivec2 RawTileDataReader::tilePixelSizeDifference = glm::ivec2(4);
const PixelRegion RawTileDataReader::padding = PixelRegion(
tilePixelStartOffset,
tilePixelSizeDifference
TileTextureInitData::tilePixelStartOffset,
TileTextureInitData::tilePixelSizeDifference
);
RawTileDataReader::RawTileDataReader(const Configuration& config)
: _config(config)
RawTileDataReader::RawTileDataReader(const TileTextureInitData& initData,
PerformPreprocessing preprocess)
: _initData(initData)
, _preprocess(preprocess)
, _hasBeenInitialized(false)
{}
@@ -77,48 +76,51 @@ void RawTileDataReader::ensureInitialized() {
}
std::shared_ptr<RawTile> RawTileDataReader::defaultTileData() {
PixelRegion pixelRegion = {
PixelRegion::PixelCoordinate(0, 0),
PixelRegion::PixelRange(16, 16)
};
int bytesPerPixel = 1; // GL_R -> 1 bpp
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
rawTile->tileIndex = { 0, 0, 0 };
rawTile->dimensions = glm::uvec3(pixelRegion.numPixels, 1);
rawTile->nBytesImageData =
rawTile->dimensions.x * rawTile->dimensions.y * bytesPerPixel;
rawTile->imageData = new char[rawTile->nBytesImageData];
rawTile->glType = GL_UNSIGNED_BYTE;
rawTile->textureFormat = { ghoul::opengl::Texture::Format::Red, GL_R };
for (size_t i = 0; i < rawTile->nBytesImageData; ++i) {
rawTile->imageData[i] = 0;
}
rawTile->error = RawTile::ReadError::None;
return rawTile;
return std::make_shared<RawTile>(RawTile::createDefault(_initData));
}
std::shared_ptr<RawTile> RawTileDataReader::readTileData(TileIndex tileIndex) {
std::shared_ptr<RawTile> RawTileDataReader::readTileData(TileIndex tileIndex,
char* dataDestination, char* pboMappedDataDestination)
{
ensureInitialized();
IODescription io = getIODescription(tileIndex);
RawTile::ReadError worstError = RawTile::ReadError::None;
// Build the RawTile from the data we querred
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
rawTile->imageData = readImageData(io, worstError);
if (dataDestination && !pboMappedDataDestination) {
// Write only to cpu data destination
memset(dataDestination, 255, _initData.totalNumBytes());
readImageData(io, worstError, dataDestination);
}
else if (!dataDestination && pboMappedDataDestination) {
// Write only to pbo mapped data destination
memset(pboMappedDataDestination, 255, _initData.totalNumBytes());
readImageData(io, worstError, pboMappedDataDestination);
}
else if (dataDestination && pboMappedDataDestination) {
// Write to both data destinations
memset(dataDestination, 255, _initData.totalNumBytes());
readImageData(io, worstError, dataDestination);
size_t numBytes = _initData.totalNumBytes();
memcpy(pboMappedDataDestination, dataDestination, numBytes);
}
else {
ghoul_assert(false, "Need to specify a data destination");
}
rawTile->imageData = dataDestination;
rawTile->error = worstError;
rawTile->tileIndex = tileIndex;
rawTile->dimensions = glm::uvec3(io.write.region.numPixels, 1);
rawTile->nBytesImageData = io.write.totalNumBytes;
rawTile->glType = _dataLayout.glType;
rawTile->textureFormat = _dataLayout.textureFormat;
if (_config.doPreProcessing) {
rawTile->textureInitData = std::make_shared<TileTextureInitData>(_initData);
if (_preprocess == PerformPreprocessing::Yes) {
rawTile->tileMetaData = getTileMetaData(rawTile, io.write.region);
rawTile->error = std::max(rawTile->error, postProcessErrorCheck(rawTile, io));
rawTile->error = std::max(rawTile->error, postProcessErrorCheck(rawTile));
}
return rawTile;
}
@@ -126,6 +128,14 @@ TileDepthTransform RawTileDataReader::getDepthTransform() const {
return _depthTransform;
}
const TileTextureInitData& RawTileDataReader::tileTextureInitData() const {
return _initData;
}
const PixelRegion::PixelRange RawTileDataReader::fullPixelSize() const {
return glm::uvec2(geodeticToPixel(Geodetic2(90, 180)));
}
std::array<double, 6> RawTileDataReader::getGeoTransform() const {
std::array<double, 6> padfTransform;
@@ -303,34 +313,34 @@ std::shared_ptr<TileMetaData> RawTileDataReader::getTileMetaData(
std::shared_ptr<RawTile> rawTile, const PixelRegion& region)
{
ensureInitialized();
size_t bytesPerLine = _dataLayout.bytesPerPixel * region.numPixels.x;
size_t bytesPerLine = _initData.bytesPerPixel() * region.numPixels.x;
TileMetaData* preprocessData = new TileMetaData();
preprocessData->maxValues.resize(_dataLayout.numRasters);
preprocessData->minValues.resize(_dataLayout.numRasters);
preprocessData->hasMissingData.resize(_dataLayout.numRasters);
preprocessData->maxValues.resize(_initData.nRasters());
preprocessData->minValues.resize(_initData.nRasters());
preprocessData->hasMissingData.resize(_initData.nRasters());
std::vector<float> noDataValues;
noDataValues.resize(_dataLayout.numRasters);
noDataValues.resize(_initData.nRasters());
for (size_t raster = 0; raster < _dataLayout.numRasters; ++raster) {
for (size_t raster = 0; raster < _initData.nRasters(); ++raster) {
preprocessData->maxValues[raster] = -FLT_MAX;
preprocessData->minValues[raster] = FLT_MAX;
preprocessData->hasMissingData[raster] = false;
noDataValues[raster] = noDataValueAsFloat();
}
for (size_t y = 0; y < region.numPixels.y; ++y) {
for (int y = 0; y < region.numPixels.y; ++y) {
size_t yi = (region.numPixels.y - 1 - y) * bytesPerLine;
size_t i = 0;
for (size_t x = 0; x < region.numPixels.x; ++x) {
for (size_t raster = 0; raster < _dataLayout.numRasters; ++raster) {
for (int x = 0; x < region.numPixels.x; ++x) {
for (size_t raster = 0; raster < _initData.nRasters(); ++raster) {
float noDataValue = noDataValueAsFloat();
float val = tiledatatype::interpretFloat(
_dataLayout.glType,
_initData.glType(),
&(rawTile->imageData[yi + i])
);
if (val != noDataValue) {
if (val != noDataValue && val == val) {
preprocessData->maxValues[raster] = std::max(
val,
preprocessData->maxValues[raster]
@@ -343,7 +353,7 @@ std::shared_ptr<TileMetaData> RawTileDataReader::getTileMetaData(
else {
preprocessData->hasMissingData[raster] = true;
}
i += _dataLayout.bytesPerDatum;
i += _initData.bytesPerDatum();
}
}
}
@@ -361,9 +371,9 @@ float RawTileDataReader::depthScale() const {
TileDepthTransform RawTileDataReader::calculateTileDepthTransform() {
bool isFloat =
(_dataLayout.glType == GL_FLOAT || _dataLayout.glType == GL_DOUBLE);
(_initData.glType() == GL_HALF_FLOAT || _initData.glType() == GL_FLOAT || _initData.glType() == GL_DOUBLE);
double maximumValue =
isFloat ? 1.0 : tiledatatype::getMaximumValue(_dataLayout.glType);
isFloat ? 1.0 : tiledatatype::getMaximumValue(_initData.glType());
TileDepthTransform transform;
transform.depthOffset = depthOffset();
@@ -372,15 +382,13 @@ TileDepthTransform RawTileDataReader::calculateTileDepthTransform() {
}
RawTile::ReadError RawTileDataReader::postProcessErrorCheck(
std::shared_ptr<const RawTile> rawTile, const IODescription& io)
std::shared_ptr<const RawTile> rawTile) const
{
ensureInitialized();
double missingDataValue = noDataValueAsFloat();
float missingDataValue = noDataValueAsFloat();
bool hasMissingData = false;
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
for (size_t c = 0; c < _initData.nRasters(); c++) {
hasMissingData |= rawTile->tileMetaData->maxValues[c] == missingDataValue;
}

View File

@@ -26,13 +26,14 @@
#define __OPENSPACE_MODULE_GLOBEBROWSING___RAW_TILE_DATA_READER___H__
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/tiletextureinitdata.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/rawtiledatareader/iodescription.h>
#include <ghoul/misc/boolean.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/texture.h>
@@ -46,21 +47,21 @@ class GeodeticPatch;
class RawTileDataReader {
public:
struct Configuration {
bool doPreProcessing;
int tilePixelSize;
GLuint dataType = 0; // default = no datatype reinterpretation
};
using PerformPreprocessing = ghoul::Boolean;
RawTileDataReader(const Configuration& config);
RawTileDataReader(const TileTextureInitData& initData,
PerformPreprocessing preprocess = PerformPreprocessing::No);
virtual ~RawTileDataReader() = default;
/**
* Reads data from the current dataset and initializes a <code>RawTile</code>
* which gets returned.
*/
std::shared_ptr<RawTile> readTileData(TileIndex tileIndex);
std::shared_ptr<RawTile> readTileData(TileIndex tileIndex,
char* dataDestination, char* pboMappedDataDestination);
TileDepthTransform getDepthTransform() const;
const TileTextureInitData& tileTextureInitData() const;
const PixelRegion::PixelRange fullPixelSize() const;
/**
* \returns the maximum chunk level available in the dataset. Should be a value
@@ -77,15 +78,12 @@ public:
virtual int rasterYSize() const = 0;
virtual float depthOffset() const;
virtual float depthScale() const;
/**
* Returns a single channeled empty <code>RawTile</code> of size 16 * 16 pixels.
*/
std::shared_ptr<RawTile> defaultTileData();
const static glm::ivec2 tilePixelStartOffset;
const static glm::ivec2 tilePixelSizeDifference;
/// Padding around all tiles to read to make sure edge blending works.
const static PixelRegion padding; // same as the two above
@@ -114,8 +112,8 @@ protected:
* \param <code>worstError</code> should be set to the error code returned when
* reading the data.
*/
virtual char* readImageData(
IODescription& io, RawTile::ReadError& worstError) const = 0;
virtual void readImageData(
IODescription& io, RawTile::ReadError& worstError, char* dataDestination) const = 0;
virtual RawTile::ReadError rasterRead(
int rasterBand, const IODescription& io, char* dst) const = 0;
@@ -159,15 +157,14 @@ protected:
std::shared_ptr<TileMetaData> getTileMetaData(
std::shared_ptr<RawTile> result, const PixelRegion& region);
TileDepthTransform calculateTileDepthTransform();
RawTile::ReadError postProcessErrorCheck(
std::shared_ptr<const RawTile> ioResult, const IODescription& io);
RawTile::ReadError postProcessErrorCheck(std::shared_ptr<const RawTile> ioResult) const;
struct Cached {
int _maxLevel = -1;
double _tileLevelDifference;
} _cached;
const Configuration _config;
TileDataLayout _dataLayout;
const TileTextureInitData _initData;
PerformPreprocessing _preprocess;
TileDepthTransform _depthTransform;
private:

View File

@@ -44,9 +44,10 @@ namespace {
namespace openspace {
namespace globebrowsing {
SimpleRawTileDataReader::SimpleRawTileDataReader(
const std::string& filePath, const Configuration& config)
: RawTileDataReader(config)
SimpleRawTileDataReader::SimpleRawTileDataReader(const std::string& filePath,
const TileTextureInitData& initData,
RawTileDataReader::PerformPreprocessing preprocess)
: RawTileDataReader(initData, preprocess)
{
_datasetFilePath = filePath;
ensureInitialized();
@@ -57,7 +58,7 @@ void SimpleRawTileDataReader::reset() {
}
int SimpleRawTileDataReader::maxChunkLevel() {
return _cached._maxLevel;
return 2;
}
float SimpleRawTileDataReader::noDataValueAsFloat() const {
@@ -110,50 +111,29 @@ void SimpleRawTileDataReader::initialize() {
".\nCurrently only supporting power of 2 textures."
);
}
_cached._maxLevel = 2;
_cached._tileLevelDifference = 0;
_dataLayout.glType = _dataTexture->dataType();
_dataLayout.bytesPerDatum = tiledatatype::numberOfBytes(_dataLayout.glType);
_dataLayout.numRasters = tiledatatype::numberOfRasters(_dataTexture->format());
_dataLayout.numRastersAvailable = _dataLayout.numRasters;
_dataLayout.bytesPerPixel = _dataLayout.bytesPerDatum * _dataLayout.numRasters;
_dataLayout.textureFormat = {_dataTexture->format(), _dataTexture->internalFormat()};
_depthTransform = {depthScale(), depthOffset()};
}
char* SimpleRawTileDataReader::readImageData(
IODescription& io, RawTile::ReadError& worstError) const {
// allocate memory for the image
char* imageData = new char[io.write.totalNumBytes];
// In case there are extra channels not existing in the dataset
// we set the bytes to 255 (for example an extra alpha channel that)
// needs to be 1.
if (_dataLayout.numRasters > _dataLayout.numRastersAvailable) {
memset(imageData, 255, io.write.totalNumBytes);
}
void SimpleRawTileDataReader::readImageData(
IODescription& io, RawTile::ReadError& worstError, char* dataDestination) const {
// Modify to match OpenGL texture layout:
IODescription modifiedIO = io;
modifiedIO.read.region.start.y = modifiedIO.read.fullRegion.numPixels.y - modifiedIO.read.region.numPixels.y - modifiedIO.read.region.start.y;
RawTile::ReadError err = repeatedRasterRead(0, modifiedIO, imageData);
RawTile::ReadError err = repeatedRasterRead(0, modifiedIO, dataDestination);
// None = 0, Debug = 1, Warning = 2, Failure = 3, Fatal = 4
worstError = std::max(worstError, err);
return imageData;
}
RawTile::ReadError SimpleRawTileDataReader::rasterRead(
int rasterBand, const IODescription& io, char* dataDestination) const
{
ghoul_assert(io.read.fullRegion.numPixels.x == _dataTexture->dimensions().x,
ghoul_assert(static_cast<unsigned int>(io.read.fullRegion.numPixels.x) == _dataTexture->dimensions().x,
"IODescription does not match data texture.");
ghoul_assert(io.read.fullRegion.numPixels.y == _dataTexture->dimensions().y,
ghoul_assert(static_cast<unsigned int>(io.read.fullRegion.numPixels.y) == _dataTexture->dimensions().y,
"IODescription does not match data texture.");
ghoul_assert(io.read.region.numPixels.x == io.write.region.numPixels.x,
"IODescription does not match data texture.");

View File

@@ -28,7 +28,6 @@
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
@@ -48,7 +47,10 @@ class GeodeticPatch;
class SimpleRawTileDataReader : public RawTileDataReader {
public:
SimpleRawTileDataReader(const std::string& filePath, const Configuration& config);
SimpleRawTileDataReader(const std::string& filePath,
const TileTextureInitData& initData,
RawTileDataReader::PerformPreprocessing preprocess =
RawTileDataReader::PerformPreprocessing::No);
// Public virtual function overloading
virtual void reset() override;
@@ -66,8 +68,8 @@ protected:
private:
// Private virtual function overloading
virtual void initialize() override;
virtual char* readImageData(
IODescription& io, RawTile::ReadError& worstError) const override;
virtual void readImageData(
IODescription& io, RawTile::ReadError& worstError, char* dataDestination) const override;
virtual RawTile::ReadError rasterRead(
int rasterBand, const IODescription& io, char* dst) const override;

View File

@@ -411,8 +411,10 @@ size_t numberOfRasters(ghoul::opengl::Texture::Format format) {
switch (format) {
case ghoul::opengl::Texture::Format::Red: return 1;
case ghoul::opengl::Texture::Format::RG: return 2;
case ghoul::opengl::Texture::Format::RGB: return 3;
case ghoul::opengl::Texture::Format::RGBA: return 4;
case ghoul::opengl::Texture::Format::RGB:; // Intentional fallthrough
case ghoul::opengl::Texture::Format::BGR: return 3;
case ghoul::opengl::Texture::Format::RGBA:; // Intentional fallthrough
case ghoul::opengl::Texture::Format::BGRA: return 4;
default: ghoul_assert(false, "Unknown format");
}
}
@@ -420,10 +422,12 @@ size_t numberOfRasters(ghoul::opengl::Texture::Format format) {
size_t numberOfBytes(GLuint glType) {
switch (glType) {
case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
case GL_BYTE: return sizeof(GLbyte);
case GL_UNSIGNED_SHORT: return sizeof(GLushort);
case GL_SHORT: return sizeof(GLshort);
case GL_UNSIGNED_INT: return sizeof(GLuint);
case GL_INT: return sizeof(GLint);
case GL_HALF_FLOAT: return sizeof(GLhalf);
case GL_FLOAT: return sizeof(GLfloat);
case GL_DOUBLE: return sizeof(GLdouble);
default:
@@ -460,6 +464,8 @@ float interpretFloat(GLuint glType, const char* src) {
return static_cast<float>(*reinterpret_cast<const GLuint*>(src));
case GL_INT:
return static_cast<float>(*reinterpret_cast<const GLint*>(src));
case GL_HALF_FLOAT:
return static_cast<float>(*reinterpret_cast<const GLhalf*>(src));
case GL_FLOAT:
return static_cast<float>(*reinterpret_cast<const GLfloat*>(src));
case GL_DOUBLE:
@@ -469,6 +475,135 @@ float interpretFloat(GLuint glType, const char* src) {
}
}
GLint glTextureFormat(GLuint glType, ghoul::opengl::Texture::Format format) {
switch (format) {
case ghoul::opengl::Texture::Format::Red:
switch (glType) {
case GL_BYTE:
return GL_R8;
case GL_UNSIGNED_BYTE:
return GL_R8;
case GL_INT:
return GL_R32I;
case GL_UNSIGNED_INT:
return GL_R32UI;
case GL_FLOAT:
return GL_R32F;
case GL_HALF_FLOAT:
return GL_R16F;
default:
ghoul_assert(false, "glType data type unknown");
LERROR("glType data type unknown: " << glType);
return 0;
}
break;
case ghoul::opengl::Texture::Format::RG:
switch (glType) {
case GL_BYTE:
return GL_RG8;
case GL_UNSIGNED_BYTE:
return GL_RG8;
case GL_INT:
return GL_RG32I;
case GL_UNSIGNED_INT:
return GL_RG32UI;
case GL_FLOAT:
return GL_RG32F;
case GL_HALF_FLOAT:
return GL_RG16F;
default:
ghoul_assert(false, "glType data type unknown");
LERROR("glType data type unknown: " << glType);
return 0;
}
break;
case ghoul::opengl::Texture::Format::RGB:
switch (glType) {
case GL_BYTE:
return GL_RGB8;
case GL_UNSIGNED_BYTE:
return GL_RGB8;
case GL_INT:
return GL_RGB32I;
case GL_UNSIGNED_INT:
return GL_RGB32UI;
case GL_FLOAT:
return GL_RGB32F;
case GL_HALF_FLOAT:
return GL_RGB16F;
default:
ghoul_assert(false, "glType data type unknown");
LERROR("glType data type unknown: " << glType);
return 0;
}
break;
case ghoul::opengl::Texture::Format::RGBA:
switch (glType) {
case GL_BYTE:
return GL_RGBA8;
case GL_UNSIGNED_BYTE:
return GL_RGBA8;
case GL_INT:
return GL_RGBA32I;
case GL_UNSIGNED_INT:
return GL_RGBA32UI;
case GL_FLOAT:
return GL_RGBA32F;
case GL_HALF_FLOAT:
return GL_RGBA16F;
default:
ghoul_assert(false, "glType data type unknown");
LERROR("glType data type unknown: " << glType);
return 0;
}
break;
case ghoul::opengl::Texture::Format::BGR:
switch (glType) {
case GL_BYTE:
return GL_RGB8;
case GL_UNSIGNED_BYTE:
return GL_RGB8;
case GL_INT:
return GL_RGB32I;
case GL_UNSIGNED_INT:
return GL_RGB32UI;
case GL_FLOAT:
return GL_RGB32F;
case GL_HALF_FLOAT:
return GL_RGB16F;
default:
ghoul_assert(false, "glType data type unknown");
LERROR("glType data type unknown: " << glType);
return 0;
}
break;
case ghoul::opengl::Texture::Format::BGRA:
switch (glType) {
case GL_BYTE:
return GL_RGBA8;
case GL_UNSIGNED_BYTE:
return GL_RGBA8;
case GL_INT:
return GL_RGBA32I;
case GL_UNSIGNED_INT:
return GL_RGBA32UI;
case GL_FLOAT:
return GL_RGBA32F;
case GL_HALF_FLOAT:
return GL_RGBA16F;
default:
ghoul_assert(false, "glType data type unknown");
LERROR("glType data type unknown: " << glType);
return 0;
}
break;
default:
LERROR("Unknown format for OpenGL texture: " << format);
return 0;
break;
}
}
} // namespace tiledatatype
} // namespace globebrowsing
} // namespace openspace

View File

@@ -48,6 +48,7 @@ size_t numberOfBytes(GDALDataType gdalType);
float interpretFloat(GDALDataType gdalType, const char* src);
#endif // GLOBEBROWSING_USE_GDAL
GLint glTextureFormat(GLuint glType, ghoul::opengl::Texture::Format format);
size_t numberOfRasters(ghoul::opengl::Texture::Format format);
size_t numberOfBytes(GLuint glType);
size_t getMaximumValue(GLuint glType);

View File

@@ -36,46 +36,12 @@ namespace globebrowsing {
const Tile Tile::TileUnavailable = Tile(nullptr, nullptr, Tile::Status::Unavailable);
Tile::Tile(std::shared_ptr<ghoul::opengl::Texture> texture,
Tile::Tile(ghoul::opengl::Texture* texture,
std::shared_ptr<TileMetaData> metaData, Status status)
: MemoryAwareCacheable(
(sizeof(Tile) +
(metaData ? sizeof(TileMetaData) : 0) +
(texture ? sizeof(ghoul::opengl::Texture) + texture->expectedPixelDataSize() * 2 : 0))
// Multiply by two since double memory is used when creating mip maps.
/ 1000 // Convert from bytes to kilobytes
)
, _texture(texture)
: _texture(texture)
, _metaData(metaData)
, _status(status)
{}
Tile Tile::createPlainTile(const glm::uvec2& size, const glm::uvec4& color) {
using namespace ghoul::opengl;
// Create pixel data
int numBytes = size.x * size.y * 4 * 1;
char* pixels = new char[numBytes];
size_t numPixels = size.x * size.y;
size_t i = 0;
for (size_t p = 0; p < numPixels; p++){
pixels[i++] = color.r;
pixels[i++] = color.g;
pixels[i++] = color.b;
pixels[i++] = color.a;
}
// Create ghoul texture
auto texture = std::make_shared<Texture>(glm::uvec3(size, 1));
texture->setDataOwnership(Texture::TakeOwnership::Yes);
texture->setPixelData(pixels);
texture->uploadTexture();
texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
// Create tile
Tile tile(texture, nullptr, Tile::Status::OK);
return tile;
}
{ }
glm::vec2 Tile::compensateSourceTextureSampling(glm::vec2 startOffset, glm::vec2 sizeDiff,
glm::uvec2 resolution, glm::vec2 tileUV)
@@ -92,8 +58,8 @@ glm::vec2 Tile::TileUvToTextureSamplePosition(const TileUvTransform& uvTransform
{
glm::vec2 uv = uvTransform.uvOffset + uvTransform.uvScale * tileUV;
uv = compensateSourceTextureSampling(
RawTileDataReader::tilePixelStartOffset,
RawTileDataReader::tilePixelSizeDifference,
TileTextureInitData::tilePixelStartOffset,
TileTextureInitData::tilePixelSizeDifference,
resolution,
uv);
return uv;

View File

@@ -28,8 +28,6 @@
#include <modules/globebrowsing/tile/tileindex.h>
#include <modules/globebrowsing/tile/tileuvtransform.h>
#include <modules/globebrowsing/cache/memoryawarecacheable.h>
#include <memory>
namespace ghoul { namespace opengl {
@@ -45,7 +43,7 @@ struct TileUvTransform;
/**
* Defines a status and may have a Texture and TileMetaData
*/
class Tile : public cache::MemoryAwareCacheable {
class Tile {
public:
/**
* Describe if this Tile is good for usage (OK) or otherwise
@@ -79,25 +77,17 @@ public:
OK
};
Tile(std::shared_ptr<ghoul::opengl::Texture> texture,
Tile(ghoul::opengl::Texture* texture,
std::shared_ptr<TileMetaData> metaData,
Status status);
~Tile() = default;
std::shared_ptr<TileMetaData> metaData() const { return _metaData; };
Status status() const { return _status; };
std::shared_ptr<ghoul::opengl::Texture> texture() const { return _texture; };
ghoul::opengl::Texture* texture() const {
return _texture;
};
/**
* Instantiates a new tile with a single color.
*
* \param size The size of texture to be created
* \param color defined RGBA values in range 0-255.
*
* \returns a Tile with status OK and the a texture
* with the requested size and color
*/
static Tile createPlainTile(const glm::uvec2& size, const glm::uvec4& color);
static glm::vec2 compensateSourceTextureSampling(glm::vec2 startOffset,
glm::vec2 sizeDiff, glm::uvec2 resolution, glm::vec2 tileUV);
static glm::vec2 TileUvToTextureSamplePosition(const TileUvTransform& uvTransform,
@@ -109,7 +99,7 @@ public:
static const Tile TileUnavailable;
private:
std::shared_ptr<ghoul::opengl::Texture> _texture;
ghoul::opengl::Texture* _texture;
std::shared_ptr<TileMetaData> _metaData;
Status _status;
};

View File

@@ -1,150 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* 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/tile/tiledatareader.h>
#include <modules/globebrowsing/geometry/angle.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#include <modules/globebrowsing/geometry/geodeticpatch.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledatatype.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/tilemetadata.h>
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/assert.h>
#include <algorithm>
#include <float.h>
#include <limits>
#include <sstream>
namespace {
const char* _loggerCat = "TileDataReader";
}
namespace openspace {
namespace globebrowsing {
std::ostream& operator<<(std::ostream& os, const PixelRegion& pr) {
return os << pr.start.x << ", " << pr.start.y <<
" with size " << pr.numPixels.x << ", " << pr.numPixels.y;
}
TileDataReader::TileDataReader(const Configuration& config)
: _config(config)
{}
std::shared_ptr<RawTile> TileDataReader::defaultTileData() {
ensureInitialized();
PixelRegion pixelRegion = {
PixelRegion::PixelCoordinate(0, 0),
PixelRegion::PixelRange(16, 16)
};
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
rawTile->tileIndex = { 0, 0, 0 };
rawTile->dimensions = glm::uvec3(pixelRegion.numPixels, 1);
rawTile->nBytesImageData =
rawTile->dimensions.x * rawTile->dimensions.y * _dataLayout.bytesPerPixel;
rawTile->imageData = new char[rawTile->nBytesImageData];
for (size_t i = 0; i < rawTile->nBytesImageData; ++i) {
rawTile->imageData[i] = 0;
}
rawTile->error = RawTile::ReadError::None;
if (_config.doPreProcessing) {
rawTile->tileMetaData = getTileMetaData(rawTile, pixelRegion);
}
return rawTile;
}
std::array<double, 6> TileDataset::getGeoTransform() const {
std::array<double, 6> padfTransform;
// Global coverage of the whole latlon space
GeodeticPatch globalCoverage(Geodetic2(0,0), Geodetic2(M_PI / 2, M_PI));
padfTransform[1] = Angle<double>::fromRadians(
globalCoverage.size().lon).asDegrees() / rasterXSize();
padfTransform[5] = -Angle<double>::fromRadians(
globalCoverage.size().lat).asDegrees() / rasterYSize();
padfTransform[0] = Angle<double>::fromRadians(
globalCoverage.getCorner(Quad::NORTH_WEST).lon).asDegrees();
padfTransform[3] = Angle<double>::fromRadians(
globalCoverage.getCorner(Quad::NORTH_WEST).lat).asDegrees();
padfTransform[2] = 0;
padfTransform[4] = 0;
return padfTransform;
}
PixelRegion::PixelCoordinate TileDataReader::geodeticToPixel(const Geodetic2& geo) const {
std::array<double, 6> padfTransform = getGeoTransform();
double Y = Angle<double>::fromRadians(geo.lat).asDegrees();
double X = Angle<double>::fromRadians(geo.lon).asDegrees();
// convert from pixel and line to geodetic coordinates
// Xp = padfTransform[0] + P*padfTransform[1] + L*padfTransform[2];
// Yp = padfTransform[3] + P*padfTransform[4] + L*padfTransform[5];
// <=>
double* a = &(padfTransform[0]);
double* b = &(padfTransform[3]);
// Xp = a[0] + P*a[1] + L*a[2];
// Yp = b[0] + P*b[1] + L*b[2];
// <=>
double divisor = (a[2] * b[1] - a[1] * b[2]);
ghoul_assert(divisor != 0.0, "Division by zero!");
//ghoul_assert(a[2] != 0.0, "a2 must not be zero!");
double P = (a[0] * b[2] - a[2] * b[0] + a[2] * Y - b[2] * X) / divisor;
double L = (-a[0] * b[1] + a[1] * b[0] - a[1] * Y + b[1] * X) / divisor;
// ref: https://www.wolframalpha.com/input/?i=X+%3D+a0+%2B+a1P+%2B+a2L,+Y+%3D+b0+%2B+b1P+%2B+b2L,+solve+for+P+and+L
double Xp = a[0] + P*a[1] + L*a[2];
double Yp = b[0] + P*b[1] + L*b[2];
ghoul_assert(abs(X - Xp) < 1e-10, "inverse should yield X as before");
ghoul_assert(abs(Y - Yp) < 1e-10, "inverse should yield Y as before");
return PixelRegion::PixelCoordinate(glm::round(P), glm::round(L));
}
Geodetic2 TileDataReader::pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const {
std::array<double, 6> padfTransform = getGeoTransform();
Geodetic2 geodetic;
// Should be using radians and not degrees?
geodetic.lon = padfTransform[0] + p.x * padfTransform[1] + p.y * padfTransform[2];
geodetic.lat = padfTransform[3] + p.x * padfTransform[4] + p.y * padfTransform[5];
return geodetic;
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -1,116 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* 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/tile/tilediskcache.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/tile.h>
#include <ghoul/filesystem/filesystem.h>
#include <fstream>
using namespace ghoul::filesystem;
namespace openspace {
namespace globebrowsing {
const std::string TileDiskCache::CACHE_ROOT = "tilecache";
TileDiskCache::TileDiskCache(const std::string& name)
: _name(name)
{
std::string pathToCacheDir = FileSys.pathByAppendingComponent(CACHE_ROOT, name);
Directory cacheDir(pathToCacheDir, Directory::RawPath::No);
if (!FileSys.directoryExists(cacheDir)) {
FileSys.createDirectory(pathToCacheDir, FileSystem::Recursive::Yes);
}
_cacheDir = cacheDir;
}
bool TileDiskCache::has(const TileIndex& tileIndex) const {
File metaFile = getMetaDataFile(tileIndex);
return FileSys.fileExists(metaFile);
}
std::shared_ptr<RawTile> TileDiskCache::get(const TileIndex& tileIndex) {
File metaDataFile = getMetaDataFile(tileIndex);
File dataFile = getDataFile(tileIndex);
if (FileSys.fileExists(metaDataFile) && FileSys.fileExists(dataFile)) {
// read meta
std::ifstream ifsMeta;
ifsMeta.open(metaDataFile.path(), std::ifstream::in);
RawTile res = RawTile::deserializeMetaData(ifsMeta);
ifsMeta.close();
// read data
std::ifstream ifsData;
ifsData.open(dataFile.path(), std::ifstream::binary);
char * buffer = new char[res.nBytesImageData];
ifsData.read(buffer, res.nBytesImageData);
res.imageData = buffer;
return std::make_shared<RawTile>(res);
}
return nullptr;
}
bool TileDiskCache::put(const TileIndex& tileIndex, std::shared_ptr<RawTile> rawTile) {
File metaDataFile = getMetaDataFile(tileIndex);
if (!FileSys.fileExists(metaDataFile)) {
std::ofstream ofsMeta;
ofsMeta.open(metaDataFile.path());
rawTile->serializeMetaData(ofsMeta);
ofsMeta.close();
std::ofstream ofsData;
File dataFile = getDataFile(tileIndex);
ofsData.open(dataFile.path(), std::ofstream::binary);
char* data = (char*)rawTile->imageData;
ofsData.write(data, rawTile->nBytesImageData);
ofsData.close();
return true;
}
return false;
}
std::string TileDiskCache::getFilePath(const TileIndex& tileIndex) const {
std::stringstream ss;
ss << tileIndex.level;
ss << "_" << tileIndex.x;
ss << "_" << tileIndex.y;
std::string filePath = FileSys.pathByAppendingComponent(_cacheDir.path(), ss.str());
return filePath;
}
File TileDiskCache::getMetaDataFile(const TileIndex& tileIndex) const {
return File(getFilePath(tileIndex) + ".meta");
}
File TileDiskCache::getDataFile(const TileIndex& tileIndex) const {
return File(getFilePath(tileIndex) + ".data");
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -128,7 +128,7 @@ TileIndex::TileHashKey TileIndex::hashKey() const {
TileHashKey key = 0LL;
key |= level;
key |= x << 5;
key |= ((TileHashKey)y) << 35;
key |= static_cast<TileHashKey>(y) << 35;
return key;
}
@@ -149,10 +149,6 @@ std::string TileIndex::toString() const {
return ss.str();
}
bool TileIndex::operator==(const TileIndex& other) const {
return x == other.x && y == other.y && level == other.level;
}
std::ostream& operator<<(std::ostream& os, const TileIndex& ci) {
os << "{ x = " << ci.x << ", y = " << ci.y << ", level = " << ci.level << " }";
return os;

View File

@@ -110,7 +110,9 @@ struct TileIndex {
TileHashKey hashKey() const;
bool operator==(const TileIndex& other) const;
inline bool operator==(const TileIndex& other) const {
return (x == other.x) && (y == other.y) && (level == other.level);
}
};
std::ostream& operator<<(std::ostream& os, const TileIndex& ti);

Some files were not shown because too many files have changed in this diff Show More