mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-21 12:29:04 -06:00
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:
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Submodule ext/ghoul updated: b0170cead1...56bd0eb34a
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
31
modules/globebrowsing/cache/lrucache.h
vendored
31
modules/globebrowsing/cache/lrucache.h
vendored
@@ -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
|
||||
|
||||
133
modules/globebrowsing/cache/lrucache.inl
vendored
133
modules/globebrowsing/cache/lrucache.inl
vendored
@@ -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
|
||||
|
||||
100
modules/globebrowsing/cache/memoryawarelrucache.inl
vendored
100
modules/globebrowsing/cache/memoryawarelrucache.inl
vendored
@@ -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
|
||||
284
modules/globebrowsing/cache/memoryawaretilecache.cpp
vendored
284
modules/globebrowsing/cache/memoryawaretilecache.cpp
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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__
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 ..
|
||||
// | / |
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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__
|
||||
141
modules/globebrowsing/other/lruthreadpool.inl
Normal file
141
modules/globebrowsing/other/lruthreadpool.inl
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
140
modules/globebrowsing/other/pixelbuffer.h
Normal file
140
modules/globebrowsing/other/pixelbuffer.h
Normal 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__
|
||||
115
modules/globebrowsing/other/pixelbuffercontainer.h
Normal file
115
modules/globebrowsing/other/pixelbuffercontainer.h
Normal 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__
|
||||
130
modules/globebrowsing/other/pixelbuffercontainer.inl
Normal file
130
modules/globebrowsing/other/pixelbuffercontainer.inl
Normal 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
|
||||
@@ -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__
|
||||
@@ -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__
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) + "].";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -112,6 +112,7 @@ struct LayerSettings {
|
||||
float opacity;
|
||||
float gamma;
|
||||
float multiplier;
|
||||
float valueBlending;
|
||||
};
|
||||
|
||||
struct Layer {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -81,8 +81,8 @@ private:
|
||||
|
||||
void setGdalProxyConfiguration();
|
||||
|
||||
properties::IntProperty _gdalMaximumCacheSize;
|
||||
properties::BoolProperty _logGdalErrors;
|
||||
properties::IntProperty _gdalMaximumCacheSize;
|
||||
|
||||
static GdalWrapper* _singleton;
|
||||
static std::mutex _mutexLock;
|
||||
|
||||
@@ -26,10 +26,6 @@
|
||||
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
|
||||
namespace {
|
||||
const char* _loggerCat = "IODescription";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user