1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-29 18:32:36 +00:00

Merge branch 'more_stats' into 'master'

Report more stats in F4

See merge request OpenMW/openmw!3956
This commit is contained in:
psi29a 2024-03-30 08:41:29 +00:00
commit 3da6b4f529
25 changed files with 602 additions and 408 deletions

View File

@ -965,17 +965,17 @@ void OMW::Engine::go()
}
// Setup profiler
osg::ref_ptr<Resource::Profiler> statshandler = new Resource::Profiler(stats.is_open(), mVFS.get());
osg::ref_ptr<Resource::Profiler> statsHandler = new Resource::Profiler(stats.is_open(), *mVFS);
initStatsHandler(*statshandler);
initStatsHandler(*statsHandler);
mViewer->addEventHandler(statshandler);
mViewer->addEventHandler(statsHandler);
osg::ref_ptr<Resource::StatsHandler> resourceshandler = new Resource::StatsHandler(stats.is_open(), mVFS.get());
mViewer->addEventHandler(resourceshandler);
osg::ref_ptr<Resource::StatsHandler> resourcesHandler = new Resource::StatsHandler(stats.is_open(), *mVFS);
mViewer->addEventHandler(resourcesHandler);
if (stats.is_open())
Resource::CollectStatistics(mViewer);
Resource::collectStatistics(*mViewer);
// Start the game
if (!mSaveGameFile.empty())

View File

@ -463,6 +463,6 @@ namespace MWRender
void Groundcover::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
stats->setAttribute(frameNumber, "Groundcover Chunk", mCache->getCacheSize());
Resource::reportStats("Groundcover Chunk", frameNumber, mCache->getStats(), *stats);
}
}

View File

@ -1,7 +1,5 @@
#include "landmanager.hpp"
#include <osg/Stats>
#include <components/esm4/loadwrld.hpp>
#include <components/resource/objectcache.hpp>
#include <components/settings/values.hpp>
@ -53,7 +51,7 @@ namespace MWRender
void LandManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
stats->setAttribute(frameNumber, "Land", mCache->getCacheSize());
Resource::reportStats("Land", frameNumber, mCache->getStats(), *stats);
}
}

View File

@ -1013,7 +1013,7 @@ namespace MWRender
void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
stats->setAttribute(frameNumber, "Object Chunk", mCache->getCacheSize());
Resource::reportStats("Object Chunk", frameNumber, mCache->getStats(), *stats);
}
}

View File

@ -4,6 +4,8 @@
#include <atomic>
#include <limits>
#include <osg/Stats>
#include <components/debug/debuglog.hpp>
#include <components/esm3/loadcell.hpp>
#include <components/loadinglistener/reporter.hpp>
@ -276,6 +278,7 @@ namespace MWWorld
{
oldestCell->second.mWorkItem->abort();
mPreloadCells.erase(oldestCell);
++mEvicted;
}
else
return;
@ -285,7 +288,8 @@ namespace MWWorld
mResourceSystem->getKeyframeManager(), mTerrain, mLandManager, mPreloadInstances));
mWorkQueue->addWorkItem(item);
mPreloadCells[&cell] = PreloadEntry(timestamp, item);
mPreloadCells.emplace(&cell, PreloadEntry(timestamp, item));
++mAdded;
}
void CellPreloader::notifyLoaded(CellStore* cell)
@ -300,6 +304,7 @@ namespace MWWorld
}
mPreloadCells.erase(found);
++mLoaded;
}
}
@ -329,6 +334,7 @@ namespace MWWorld
it->second.mWorkItem = nullptr;
}
mPreloadCells.erase(it++);
++mExpired;
}
else
++it;
@ -467,4 +473,12 @@ namespace MWWorld
mPreloadCells.clear();
}
void CellPreloader::reportStats(unsigned int frameNumber, osg::Stats& stats) const
{
stats.setAttribute(frameNumber, "CellPreloader Count", mPreloadCells.size());
stats.setAttribute(frameNumber, "CellPreloader Added", mAdded);
stats.setAttribute(frameNumber, "CellPreloader Evicted", mEvicted);
stats.setAttribute(frameNumber, "CellPreloader Loaded", mLoaded);
stats.setAttribute(frameNumber, "CellPreloader Expired", mExpired);
}
}

View File

@ -2,11 +2,20 @@
#define OPENMW_MWWORLD_CELLPRELOADER_H
#include <components/sceneutil/workqueue.hpp>
#include <map>
#include <osg/Vec3f>
#include <osg/Vec4i>
#include <osg/ref_ptr>
#include <map>
namespace osg
{
class Stats;
}
namespace Resource
{
class ResourceSystem;
@ -76,6 +85,8 @@ namespace MWWorld
bool isTerrainLoaded(const CellPreloader::PositionCellGrid& position, double referenceTime) const;
void setTerrain(Terrain::World* terrain);
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
private:
void clearAllTasks();
@ -118,6 +129,10 @@ namespace MWWorld
std::vector<PositionCellGrid> mLoadedTerrainPositions;
double mLoadedTerrainTimestamp;
std::size_t mEvicted = 0;
std::size_t mAdded = 0;
std::size_t mExpired = 0;
std::size_t mLoaded = 0;
};
}

View File

@ -1285,4 +1285,9 @@ namespace MWWorld
}
}
}
void Scene::reportStats(unsigned int frameNumber, osg::Stats& stats) const
{
mPreloader->reportStats(frameNumber, stats);
}
}

View File

@ -19,6 +19,7 @@
namespace osg
{
class Vec3f;
class Stats;
}
namespace ESM
@ -203,6 +204,8 @@ namespace MWWorld
void testExteriorCells();
void testInteriorCells();
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
};
}

View File

@ -3786,6 +3786,7 @@ namespace MWWorld
{
DetourNavigator::reportStats(mNavigator->getStats(), frameNumber, stats);
mPhysics->reportStats(frameNumber, stats);
mWorldScene->reportStats(frameNumber, stats);
}
void World::updateSkyDate()

View File

@ -114,9 +114,11 @@ namespace Resource
cache->update(referenceTime, expiryDelay);
ASSERT_THAT(cache->getRefFromObjectCacheOrNone(key), Optional(_));
ASSERT_EQ(cache->getStats().mExpired, 0);
cache->update(referenceTime + expiryDelay, expiryDelay);
EXPECT_EQ(cache->getRefFromObjectCacheOrNone(key), std::nullopt);
ASSERT_EQ(cache->getStats().mExpired, 1);
}
TEST(ResourceGenericObjectCacheTest, updateShouldKeepExternallyReferencedItems)
@ -249,7 +251,7 @@ namespace Resource
EXPECT_THAT(actual, ElementsAre(Pair(1, value1.get()), Pair(2, value2.get()), Pair(3, value3.get())));
}
TEST(ResourceGenericObjectCacheTest, getCacheSizeShouldReturnNumberOrAddedItems)
TEST(ResourceGenericObjectCacheTest, getStatsShouldReturnNumberOrAddedItems)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
@ -258,7 +260,33 @@ namespace Resource
cache->addEntryToObjectCache(13, value1);
cache->addEntryToObjectCache(42, value2);
EXPECT_EQ(cache->getCacheSize(), 2);
const CacheStats stats = cache->getStats();
EXPECT_EQ(stats.mSize, 2);
}
TEST(ResourceGenericObjectCacheTest, getStatsShouldReturnNumberOrGetsAndHits)
{
osg::ref_ptr<GenericObjectCache<int>> cache(new GenericObjectCache<int>);
{
const CacheStats stats = cache->getStats();
EXPECT_EQ(stats.mGet, 0);
EXPECT_EQ(stats.mHit, 0);
}
osg::ref_ptr<Object> value(new Object);
cache->addEntryToObjectCache(13, value);
cache->getRefFromObjectCache(13);
cache->getRefFromObjectCache(42);
{
const CacheStats stats = cache->getStats();
EXPECT_EQ(stats.mGet, 2);
EXPECT_EQ(stats.mHit, 1);
}
}
TEST(ResourceGenericObjectCacheTest, lowerBoundShouldReturnFirstNotLessThatGivenKey)

View File

@ -125,7 +125,7 @@ add_component_dir (vfs
add_component_dir (resource
scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem
resourcemanager stats animation foreachbulletobject errormarker
resourcemanager stats animation foreachbulletobject errormarker cachestats
)
add_component_dir (shader

View File

@ -213,8 +213,8 @@ namespace Resource
void BulletShapeManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
stats->setAttribute(frameNumber, "Shape", mCache->getCacheSize());
stats->setAttribute(frameNumber, "Shape Instance", mInstanceCache->getCacheSize());
Resource::reportStats("Shape", frameNumber, mCache->getStats(), *stats);
Resource::reportStats("Shape Instance", frameNumber, mInstanceCache->getStats(), *stats);
}
}

View File

@ -0,0 +1,40 @@
#include "cachestats.hpp"
#include <osg/Stats>
namespace Resource
{
namespace
{
std::string makeAttribute(std::string_view prefix, std::string_view suffix)
{
std::string result;
result.reserve(prefix.size() + 1 + suffix.size());
result += prefix;
result += ' ';
result += suffix;
return result;
}
}
void addCacheStatsAttibutes(std::string_view prefix, std::vector<std::string>& out)
{
constexpr std::string_view suffixes[] = {
"Count",
"Get",
"Hit",
"Expired",
};
for (std::string_view suffix : suffixes)
out.push_back(makeAttribute(prefix, suffix));
}
void reportStats(std::string_view prefix, unsigned frameNumber, const CacheStats& src, osg::Stats& dst)
{
dst.setAttribute(frameNumber, makeAttribute(prefix, "Count"), static_cast<double>(src.mSize));
dst.setAttribute(frameNumber, makeAttribute(prefix, "Get"), static_cast<double>(src.mGet));
dst.setAttribute(frameNumber, makeAttribute(prefix, "Hit"), static_cast<double>(src.mHit));
dst.setAttribute(frameNumber, makeAttribute(prefix, "Expired"), static_cast<double>(src.mExpired));
}
}

View File

@ -0,0 +1,28 @@
#ifndef OPENMW_COMPONENTS_RESOURCE_CACHESATS
#define OPENMW_COMPONENTS_RESOURCE_CACHESATS
#include <cstddef>
#include <string_view>
#include <vector>
namespace osg
{
class Stats;
}
namespace Resource
{
struct CacheStats
{
std::size_t mSize = 0;
std::size_t mGet = 0;
std::size_t mHit = 0;
std::size_t mExpired = 0;
};
void addCacheStatsAttibutes(std::string_view prefix, std::vector<std::string>& out);
void reportStats(std::string_view prefix, unsigned frameNumber, const CacheStats& src, osg::Stats& dst);
}
#endif

View File

@ -202,7 +202,7 @@ namespace Resource
void ImageManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
stats->setAttribute(frameNumber, "Image", mCache->getCacheSize());
Resource::reportStats("Image", frameNumber, mCache->getStats(), *stats);
}
}

View File

@ -4,7 +4,6 @@
#include <components/vfs/manager.hpp>
#include <osg/Stats>
#include <osgAnimation/Animation>
#include <osgAnimation/BasicAnimationManager>
#include <osgAnimation/Channel>
@ -250,7 +249,7 @@ namespace Resource
void KeyframeManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
stats->setAttribute(frameNumber, "Keyframe", mCache->getCacheSize());
Resource::reportStats("Keyframe", frameNumber, mCache->getStats(), *stats);
}
}

View File

@ -25,6 +25,7 @@ namespace Resource
{
objectsToRemove.push_back(oitr->second);
_objectCache.erase(oitr++);
++mExpired;
}
else
{
@ -57,13 +58,15 @@ namespace Resource
osg::ref_ptr<osg::Object> MultiObjectCache::takeFromObjectCache(const std::string& fileName)
{
std::lock_guard<std::mutex> lock(_objectCacheMutex);
++mGet;
ObjectCacheMap::iterator found = _objectCache.find(fileName);
if (found == _objectCache.end())
return osg::ref_ptr<osg::Object>();
else
{
osg::ref_ptr<osg::Object> object = found->second;
osg::ref_ptr<osg::Object> object = std::move(found->second);
_objectCache.erase(found);
++mHit;
return object;
}
}
@ -79,10 +82,15 @@ namespace Resource
}
}
unsigned int MultiObjectCache::getCacheSize() const
CacheStats MultiObjectCache::getStats() const
{
std::lock_guard<std::mutex> lock(_objectCacheMutex);
return _objectCache.size();
return CacheStats{
.mSize = _objectCache.size(),
.mGet = mGet,
.mHit = mHit,
.mExpired = mExpired,
};
}
}

View File

@ -8,6 +8,8 @@
#include <osg/Referenced>
#include <osg/ref_ptr>
#include "cachestats.hpp"
namespace osg
{
class Object;
@ -37,13 +39,16 @@ namespace Resource
/** call releaseGLObjects on all objects attached to the object cache.*/
void releaseGLObjects(osg::State* state);
unsigned int getCacheSize() const;
CacheStats getStats() const;
protected:
typedef std::multimap<std::string, osg::ref_ptr<osg::Object>> ObjectCacheMap;
ObjectCacheMap _objectCache;
mutable std::mutex _objectCacheMutex;
std::size_t mGet = 0;
std::size_t mHit = 0;
std::size_t mExpired = 0;
};
}

View File

@ -3,7 +3,6 @@
#include <iostream>
#include <osg/Object>
#include <osg/Stats>
#include <components/vfs/manager.hpp>
@ -59,7 +58,7 @@ namespace Resource
void NifFileManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
stats->setAttribute(frameNumber, "Nif", mCache->getCacheSize());
Resource::reportStats("Nif", frameNumber, mCache->getStats(), *stats);
}
}

View File

@ -20,6 +20,8 @@
#ifndef OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE
#define OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE
#include "cachestats.hpp"
#include <osg/Node>
#include <osg/Referenced>
#include <osg/ref_ptr>
@ -29,26 +31,28 @@
#include <mutex>
#include <optional>
#include <string>
#include <vector>
namespace osg
{
class Object;
class State;
class NodeVisitor;
class Stats;
}
namespace Resource
{
struct GenericObjectCacheItem
{
osg::ref_ptr<osg::Object> mValue;
double mLastUsage;
};
template <typename KeyType>
class GenericObjectCache : public osg::Referenced
{
public:
GenericObjectCache()
: osg::Referenced(true)
{
}
// Update last usage timestamp using referenceTime for each cache time if they are not nullptr and referenced
// from somewhere else. Remove items with last usage > expiryTime. Note: last usage might be updated from other
// places so nullptr or not references elsewhere items are not always removed.
@ -64,6 +68,7 @@ namespace Resource
item.mLastUsage = referenceTime;
if (item.mLastUsage > expiryTime)
return false;
++mExpired;
if (item.mValue != nullptr)
objectsToRemove.push_back(std::move(item.mValue));
return true;
@ -105,34 +110,29 @@ namespace Resource
osg::ref_ptr<osg::Object> getRefFromObjectCache(const auto& key)
{
std::lock_guard<std::mutex> lock(mMutex);
const auto itr = mItems.find(key);
if (itr != mItems.end())
return itr->second.mValue;
else
return nullptr;
if (Item* const item = find(key))
return item->mValue;
return nullptr;
}
std::optional<osg::ref_ptr<osg::Object>> getRefFromObjectCacheOrNone(const auto& key)
{
const std::lock_guard<std::mutex> lock(mMutex);
const auto it = mItems.find(key);
if (it == mItems.end())
return std::nullopt;
return it->second.mValue;
if (Item* const item = find(key))
return item->mValue;
return std::nullopt;
}
/** Check if an object is in the cache, and if it is, update its usage time stamp. */
bool checkInObjectCache(const auto& key, double timeStamp)
{
std::lock_guard<std::mutex> lock(mMutex);
const auto itr = mItems.find(key);
if (itr != mItems.end())
if (Item* const item = find(key))
{
itr->second.mLastUsage = timeStamp;
item->mLastUsage = timeStamp;
return true;
}
else
return false;
return false;
}
/** call releaseGLObjects on all objects attached to the object cache.*/
@ -162,13 +162,6 @@ namespace Resource
f(k, v.mValue.get());
}
/** Get the number of objects in the cache. */
unsigned int getCacheSize() const
{
std::lock_guard<std::mutex> lock(mMutex);
return mItems.size();
}
template <class K>
std::optional<std::pair<KeyType, osg::ref_ptr<osg::Object>>> lowerBound(K&& key)
{
@ -179,21 +172,36 @@ namespace Resource
return std::pair(it->first, it->second.mValue);
}
protected:
struct Item
CacheStats getStats() const
{
osg::ref_ptr<osg::Object> mValue;
double mLastUsage;
};
const std::lock_guard<std::mutex> lock(mMutex);
return CacheStats{
.mSize = mItems.size(),
.mGet = mGet,
.mHit = mHit,
.mExpired = mExpired,
};
}
protected:
using Item = GenericObjectCacheItem;
std::map<KeyType, Item, std::less<>> mItems;
mutable std::mutex mMutex;
};
std::size_t mGet = 0;
std::size_t mHit = 0;
std::size_t mExpired = 0;
class ObjectCache : public GenericObjectCache<std::string>
{
Item* find(const auto& key)
{
++mGet;
const auto it = mItems.find(key);
if (it == mItems.end())
return nullptr;
++mHit;
return &it->second;
}
};
}
#endif

View File

@ -1125,7 +1125,7 @@ namespace Resource
stats->setAttribute(frameNumber, "StateSet", mSharedStateManager->getNumSharedStateSets());
}
stats->setAttribute(frameNumber, "Node", mCache->getCacheSize());
Resource::reportStats("Node", frameNumber, mCache->getStats(), *stats);
}
osg::ref_ptr<Shader::ShaderVisitor> SceneManager::createShaderVisitor(const std::string& shaderPrefix)

View File

@ -2,7 +2,11 @@
#include <algorithm>
#include <iomanip>
#include <span>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include <osg/PolygonMode>
@ -16,145 +20,259 @@
#include <components/vfs/manager.hpp>
#include "cachestats.hpp"
namespace Resource
{
static bool collectStatRendering = false;
static bool collectStatCameraObjects = false;
static bool collectStatViewerObjects = false;
static bool collectStatResource = false;
static bool collectStatGPU = false;
static bool collectStatEvent = false;
static bool collectStatFrameRate = false;
static bool collectStatUpdate = false;
static bool collectStatEngine = false;
static const VFS::Path::Normalized sFontName("Fonts/DejaVuLGCSansMono.ttf");
static void setupStatCollection()
namespace
{
const char* envList = getenv("OPENMW_OSG_STATS_LIST");
if (envList == nullptr)
return;
constexpr float statsWidth = 1280.0f;
constexpr float statsHeight = 1024.0f;
constexpr float characterSize = 17.0f;
constexpr float backgroundMargin = 5;
constexpr float backgroundSpacing = 3;
constexpr float maxStatsHeight = 420.0f;
constexpr std::size_t pageSize
= static_cast<std::size_t>((maxStatsHeight - 2 * backgroundMargin) / characterSize);
constexpr int statsHandlerKey = osgGA::GUIEventAdapter::KEY_F4;
const VFS::Path::Normalized fontName("Fonts/DejaVuLGCSansMono.ttf");
std::string_view kwList(envList);
bool collectStatRendering = false;
bool collectStatCameraObjects = false;
bool collectStatViewerObjects = false;
bool collectStatResource = false;
bool collectStatGPU = false;
bool collectStatEvent = false;
bool collectStatFrameRate = false;
bool collectStatUpdate = false;
bool collectStatEngine = false;
auto kwBegin = kwList.begin();
while (kwBegin != kwList.end())
std::vector<std::string> generateAllStatNames()
{
auto kwEnd = std::find(kwBegin, kwList.end(), ';');
constexpr std::string_view firstPage[] = {
"FrameNumber",
"",
"Compiling",
"WorkQueue",
"WorkThread",
"UnrefQueue",
"",
"Texture",
"StateSet",
"Composite",
"",
"Mechanics Actors",
"Mechanics Objects",
"",
"Physics Actors",
"Physics Objects",
"Physics Projectiles",
"Physics HeightFields",
"",
"Lua UsedMemory",
"",
"",
"",
"",
};
const auto kw = kwList.substr(std::distance(kwList.begin(), kwBegin), std::distance(kwBegin, kwEnd));
constexpr std::string_view caches[] = {
"Node",
"Shape",
"Shape Instance",
"Image",
"Nif",
"Keyframe",
"Groundcover Chunk",
"Object Chunk",
"Terrain Chunk",
"Terrain Texture",
"Land",
};
if (kw == "gpu")
collectStatGPU = true;
else if (kw == "event")
collectStatEvent = true;
else if (kw == "frame_rate")
collectStatFrameRate = true;
else if (kw == "update")
collectStatUpdate = true;
else if (kw == "engine")
collectStatEngine = true;
else if (kw == "rendering")
collectStatRendering = true;
else if (kw == "cameraobjects")
collectStatCameraObjects = true;
else if (kw == "viewerobjects")
collectStatViewerObjects = true;
else if (kw == "resource")
collectStatResource = true;
else if (kw == "times")
constexpr std::string_view cellPreloader[] = {
"CellPreloader Count",
"CellPreloader Added",
"CellPreloader Evicted",
"CellPreloader Loaded",
"CellPreloader Expired",
};
constexpr std::string_view navMesh[] = {
"NavMesh Jobs",
"NavMesh Waiting",
"NavMesh Pushed",
"NavMesh Processing",
"NavMesh DbJobs Write",
"NavMesh DbJobs Read",
"NavMesh DbCache Get",
"NavMesh DbCache Hit",
"NavMesh CacheSize",
"NavMesh UsedTiles",
"NavMesh CachedTiles",
"NavMesh Cache Get",
"NavMesh Cache Hit",
};
std::vector<std::string> statNames;
for (std::string_view name : firstPage)
statNames.emplace_back(name);
for (std::size_t i = 0; i < std::size(caches); ++i)
{
collectStatGPU = true;
collectStatEvent = true;
collectStatFrameRate = true;
collectStatUpdate = true;
collectStatEngine = true;
collectStatRendering = true;
Resource::addCacheStatsAttibutes(caches[i], statNames);
if ((i + 1) % 5 != 0)
statNames.emplace_back();
}
if (kwEnd == kwList.end())
break;
for (std::string_view name : cellPreloader)
statNames.emplace_back(name);
kwBegin = std::next(kwEnd);
}
}
statNames.emplace_back();
class SetFontVisitor : public osg::NodeVisitor
{
public:
SetFontVisitor(osgText::Font* font)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mFont(font)
{
for (std::string_view name : navMesh)
statNames.emplace_back(name);
return statNames;
}
void apply(osg::Drawable& node) override
void setupStatCollection()
{
if (osgText::Text* text = dynamic_cast<osgText::Text*>(&node))
const char* envList = getenv("OPENMW_OSG_STATS_LIST");
if (envList == nullptr)
return;
std::string_view kwList(envList);
auto kwBegin = kwList.begin();
while (kwBegin != kwList.end())
{
text->setFont(mFont);
auto kwEnd = std::find(kwBegin, kwList.end(), ';');
const auto kw = kwList.substr(std::distance(kwList.begin(), kwBegin), std::distance(kwBegin, kwEnd));
if (kw == "gpu")
collectStatGPU = true;
else if (kw == "event")
collectStatEvent = true;
else if (kw == "frame_rate")
collectStatFrameRate = true;
else if (kw == "update")
collectStatUpdate = true;
else if (kw == "engine")
collectStatEngine = true;
else if (kw == "rendering")
collectStatRendering = true;
else if (kw == "cameraobjects")
collectStatCameraObjects = true;
else if (kw == "viewerobjects")
collectStatViewerObjects = true;
else if (kw == "resource")
collectStatResource = true;
else if (kw == "times")
{
collectStatGPU = true;
collectStatEvent = true;
collectStatFrameRate = true;
collectStatUpdate = true;
collectStatEngine = true;
collectStatRendering = true;
}
if (kwEnd == kwList.end())
break;
kwBegin = std::next(kwEnd);
}
}
private:
osgText::Font* mFont;
};
osg::ref_ptr<osgText::Font> getMonoFont(VFS::Manager* vfs)
{
if (osgDB::Registry::instance()->getReaderWriterForExtension("ttf") && vfs->exists(sFontName))
osg::ref_ptr<osg::Geometry> createBackgroundRectangle(
const osg::Vec3& pos, const float width, const float height, const osg::Vec4& color)
{
Files::IStreamPtr streamPtr = vfs->get(sFontName);
return osgText::readRefFontStream(*streamPtr.get());
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
geometry->setUseDisplayList(false);
osg::ref_ptr<osg::StateSet> stateSet = new osg::StateSet;
geometry->setStateSet(stateSet);
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
vertices->push_back(osg::Vec3(pos.x(), pos.y(), 0));
vertices->push_back(osg::Vec3(pos.x(), pos.y() - height, 0));
vertices->push_back(osg::Vec3(pos.x() + width, pos.y() - height, 0));
vertices->push_back(osg::Vec3(pos.x() + width, pos.y(), 0));
geometry->setVertexArray(vertices);
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
colors->push_back(color);
geometry->setColorArray(colors, osg::Array::BIND_OVERALL);
osg::ref_ptr<osg::DrawElementsUShort> base
= new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_FAN, 0);
base->push_back(0);
base->push_back(1);
base->push_back(2);
base->push_back(3);
geometry->addPrimitiveSet(base);
return geometry;
}
return nullptr;
osg::ref_ptr<osgText::Font> getMonoFont(const VFS::Manager& vfs)
{
if (osgDB::Registry::instance()->getReaderWriterForExtension("ttf") && vfs.exists(fontName))
{
const Files::IStreamPtr streamPtr = vfs.get(fontName);
return osgText::readRefFontStream(*streamPtr);
}
return nullptr;
}
class SetFontVisitor : public osg::NodeVisitor
{
public:
SetFontVisitor(osgText::Font* font)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mFont(font)
{
}
void apply(osg::Drawable& node) override
{
if (osgText::Text* text = dynamic_cast<osgText::Text*>(&node))
{
text->setFont(mFont);
}
}
private:
osgText::Font* mFont;
};
}
StatsHandler::StatsHandler(bool offlineCollect, VFS::Manager* vfs)
: _key(osgGA::GUIEventAdapter::KEY_F4)
, _initialized(false)
, _statsType(false)
, _offlineCollect(offlineCollect)
, _statsWidth(1280.0f)
, _statsHeight(1024.0f)
, _characterSize(18.0f)
Profiler::Profiler(bool offlineCollect, const VFS::Manager& vfs)
: mOfflineCollect(offlineCollect)
, mTextFont(getMonoFont(vfs))
{
_camera = new osg::Camera;
_camera->getOrCreateStateSet()->setGlobalDefaults();
_camera->setRenderer(new osgViewer::Renderer(_camera.get()));
_camera->setProjectionResizePolicy(osg::Camera::FIXED);
_resourceStatsChildNum = 0;
_textFont = getMonoFont(vfs);
}
Profiler::Profiler(bool offlineCollect, VFS::Manager* vfs)
: _offlineCollect(offlineCollect)
, _initFonts(false)
{
_characterSize = 18;
_characterSize = characterSize;
_font.clear();
_textFont = getMonoFont(vfs);
setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3);
setupStatCollection();
}
void Profiler::setUpFonts()
{
if (_textFont != nullptr)
if (mTextFont != nullptr)
{
SetFontVisitor visitor(_textFont);
SetFontVisitor visitor(mTextFont);
_switch->accept(visitor);
}
_initFonts = true;
mInitFonts = true;
}
bool Profiler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
@ -162,24 +280,45 @@ namespace Resource
osgViewer::ViewerBase* viewer = nullptr;
bool handled = StatsHandler::handle(ea, aa);
if (_initialized && !_initFonts)
if (_initialized && !mInitFonts)
setUpFonts();
auto* view = dynamic_cast<osgViewer::View*>(&aa);
if (view)
viewer = view->getViewerBase();
if (viewer)
if (viewer != nullptr)
{
// Add/remove openmw stats to the osd as necessary
viewer->getViewerStats()->collectStats("engine", _statsType >= StatsHandler::StatsType::VIEWER_STATS);
if (_offlineCollect)
CollectStatistics(viewer);
if (mOfflineCollect)
collectStatistics(*viewer);
}
return handled;
}
StatsHandler::StatsHandler(bool offlineCollect, const VFS::Manager& vfs)
: mOfflineCollect(offlineCollect)
, mSwitch(new osg::Switch)
, mCamera(new osg::Camera)
, mTextFont(getMonoFont(vfs))
, mStatNames(generateAllStatNames())
{
osg::ref_ptr<osg::StateSet> stateset = mSwitch->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
#ifdef OSG_GL1_AVAILABLE
stateset->setAttribute(new osg::PolygonMode(), osg::StateAttribute::PROTECTED);
#endif
mCamera->getOrCreateStateSet()->setGlobalDefaults();
mCamera->setRenderer(new osgViewer::Renderer(mCamera.get()));
mCamera->setProjectionResizePolicy(osg::Camera::FIXED);
mCamera->addChild(mSwitch);
}
bool StatsHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
if (ea.getHandled())
@ -189,18 +328,21 @@ namespace Resource
{
case (osgGA::GUIEventAdapter::KEYDOWN):
{
if (ea.getKey() == _key)
if (ea.getKey() == statsHandlerKey)
{
osgViewer::View* myview = dynamic_cast<osgViewer::View*>(&aa);
if (!myview)
osgViewer::View* const view = dynamic_cast<osgViewer::View*>(&aa);
if (view == nullptr)
return false;
osgViewer::ViewerBase* viewer = myview->getViewerBase();
osgViewer::ViewerBase* const viewer = view->getViewerBase();
toggle(viewer);
if (viewer == nullptr)
return false;
if (_offlineCollect)
CollectStatistics(viewer);
toggle(*viewer);
if (mOfflineCollect)
collectStatistics(*viewer);
aa.requestRedraw();
return true;
@ -223,66 +365,69 @@ namespace Resource
if (width <= 0 || height <= 0)
return;
_camera->setViewport(0, 0, width, height);
if (fabs(height * _statsWidth) <= fabs(width * _statsHeight))
mCamera->setViewport(0, 0, width, height);
if (std::abs(height * statsWidth) <= std::abs(width * statsHeight))
{
_camera->setProjectionMatrix(
osg::Matrix::ortho2D(_statsWidth - width * _statsHeight / height, _statsWidth, 0.0, _statsHeight));
mCamera->setProjectionMatrix(
osg::Matrix::ortho2D(statsWidth - width * statsHeight / height, statsWidth, 0.0, statsHeight));
}
else
{
_camera->setProjectionMatrix(
osg::Matrix::ortho2D(0.0, _statsWidth, _statsHeight - height * _statsWidth / width, _statsHeight));
mCamera->setProjectionMatrix(
osg::Matrix::ortho2D(0.0, statsWidth, statsHeight - height * statsWidth / width, statsHeight));
}
}
void StatsHandler::toggle(osgViewer::ViewerBase* viewer)
void StatsHandler::toggle(osgViewer::ViewerBase& viewer)
{
if (!_initialized)
if (!mInitialized)
{
setUpHUDCamera(viewer);
setUpScene(viewer);
mInitialized = true;
}
_statsType = !_statsType;
if (!_statsType)
if (mPage == mSwitch->getNumChildren())
{
_camera->setNodeMask(0);
_switch->setAllChildrenOff();
mPage = 0;
viewer->getViewerStats()->collectStats("resource", false);
mCamera->setNodeMask(0);
mSwitch->setAllChildrenOff();
viewer.getViewerStats()->collectStats("resource", false);
}
else
{
_camera->setNodeMask(0xffffffff);
_switch->setSingleChildOn(_resourceStatsChildNum);
mCamera->setNodeMask(0xffffffff);
mSwitch->setSingleChildOn(mPage);
viewer->getViewerStats()->collectStats("resource", true);
viewer.getViewerStats()->collectStats("resource", true);
++mPage;
}
}
void StatsHandler::setUpHUDCamera(osgViewer::ViewerBase* viewer)
void StatsHandler::setUpHUDCamera(osgViewer::ViewerBase& viewer)
{
// Try GraphicsWindow first so we're likely to get the main viewer window
osg::GraphicsContext* context = dynamic_cast<osgViewer::GraphicsWindow*>(_camera->getGraphicsContext());
osg::GraphicsContext* context = dynamic_cast<osgViewer::GraphicsWindow*>(mCamera->getGraphicsContext());
if (!context)
{
osgViewer::Viewer::Windows windows;
viewer->getWindows(windows);
viewer.getWindows(windows);
if (!windows.empty())
context = windows.front();
else
{
// No GraphicsWindows were found, so let's try to find a GraphicsContext
context = _camera->getGraphicsContext();
context = mCamera->getGraphicsContext();
if (!context)
{
osgViewer::Viewer::Contexts contexts;
viewer->getContexts(contexts);
viewer.getContexts(contexts);
if (contexts.empty())
return;
@ -292,241 +437,151 @@ namespace Resource
}
}
_camera->setGraphicsContext(context);
mCamera->setGraphicsContext(context);
_camera->setRenderOrder(osg::Camera::POST_RENDER, 11);
mCamera->setRenderOrder(osg::Camera::POST_RENDER, 11);
_camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
_camera->setViewMatrix(osg::Matrix::identity());
mCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
mCamera->setViewMatrix(osg::Matrix::identity());
setWindowSize(context->getTraits()->width, context->getTraits()->height);
// only clear the depth buffer
_camera->setClearMask(0);
_camera->setAllowEventFocus(false);
mCamera->setClearMask(0);
mCamera->setAllowEventFocus(false);
_camera->setRenderer(new osgViewer::Renderer(_camera.get()));
_initialized = true;
mCamera->setRenderer(new osgViewer::Renderer(mCamera.get()));
}
osg::Geometry* createBackgroundRectangle(
const osg::Vec3& pos, const float width, const float height, osg::Vec4& color)
namespace
{
osg::StateSet* ss = new osg::StateSet;
osg::Geometry* geometry = new osg::Geometry;
geometry->setUseDisplayList(false);
geometry->setStateSet(ss);
osg::Vec3Array* vertices = new osg::Vec3Array;
geometry->setVertexArray(vertices);
vertices->push_back(osg::Vec3(pos.x(), pos.y(), 0));
vertices->push_back(osg::Vec3(pos.x(), pos.y() - height, 0));
vertices->push_back(osg::Vec3(pos.x() + width, pos.y() - height, 0));
vertices->push_back(osg::Vec3(pos.x() + width, pos.y(), 0));
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back(color);
geometry->setColorArray(colors, osg::Array::BIND_OVERALL);
osg::DrawElementsUShort* base = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_FAN, 0);
base->push_back(0);
base->push_back(1);
base->push_back(2);
base->push_back(3);
geometry->addPrimitiveSet(base);
return geometry;
}
class ResourceStatsTextDrawCallback : public osg::Drawable::DrawCallback
{
public:
ResourceStatsTextDrawCallback(osg::Stats* stats, const std::vector<std::string>& statNames)
: mStats(stats)
, mStatNames(statNames)
class ResourceStatsTextDrawCallback : public osg::Drawable::DrawCallback
{
}
void drawImplementation(osg::RenderInfo& renderInfo, const osg::Drawable* drawable) const override
{
if (!mStats)
return;
osgText::Text* text = (osgText::Text*)(drawable);
std::ostringstream viewStr;
viewStr.setf(std::ios::left, std::ios::adjustfield);
viewStr.width(14);
// Used fixed formatting, as scientific will switch to "...e+.." notation for
// large numbers of vertices/drawables/etc.
viewStr.setf(std::ios::fixed);
viewStr.precision(0);
unsigned int frameNumber = renderInfo.getState()->getFrameStamp()->getFrameNumber() - 1;
for (const auto& statName : mStatNames.get())
public:
explicit ResourceStatsTextDrawCallback(osg::Stats* stats, std::span<const std::string> statNames)
: mStats(stats)
, mStatNames(statNames)
{
if (statName.empty())
viewStr << std::endl;
else
{
double value = 0.0;
if (mStats->getAttribute(frameNumber, statName, value))
viewStr << std::setw(8) << value << std::endl;
else
viewStr << std::setw(8) << "." << std::endl;
}
}
text->setText(viewStr.str());
void drawImplementation(osg::RenderInfo& renderInfo, const osg::Drawable* drawable) const override
{
if (mStats == nullptr)
return;
text->drawImplementation(renderInfo);
}
osgText::Text* text = (osgText::Text*)(drawable);
osg::ref_ptr<osg::Stats> mStats;
std::reference_wrapper<const std::vector<std::string>> mStatNames;
};
std::ostringstream viewStr;
viewStr.setf(std::ios::left, std::ios::adjustfield);
viewStr.width(14);
// Used fixed formatting, as scientific will switch to "...e+.." notation for
// large numbers of vertices/drawables/etc.
viewStr.setf(std::ios::fixed);
viewStr.precision(0);
void StatsHandler::setUpScene(osgViewer::ViewerBase* viewer)
const unsigned int frameNumber = renderInfo.getState()->getFrameStamp()->getFrameNumber() - 1;
for (const std::string& statName : mStatNames)
{
if (statName.empty())
viewStr << std::endl;
else
{
double value = 0.0;
if (mStats->getAttribute(frameNumber, statName, value))
viewStr << std::setw(8) << value << std::endl;
else
viewStr << std::setw(8) << "." << std::endl;
}
}
text->setText(viewStr.str());
text->drawImplementation(renderInfo);
}
private:
osg::ref_ptr<osg::Stats> mStats;
std::span<const std::string> mStatNames;
};
}
void StatsHandler::setUpScene(osgViewer::ViewerBase& viewer)
{
_switch = new osg::Switch;
const osg::Vec4 backgroundColor(0.0, 0.0, 0.0f, 0.3);
const osg::Vec4 staticTextColor(1.0, 1.0, 0.0f, 1.0);
const osg::Vec4 dynamicTextColor(1.0, 1.0, 1.0f, 1.0);
_camera->addChild(_switch);
const auto longest = std::max_element(mStatNames.begin(), mStatNames.end(),
[](const std::string& lhs, const std::string& rhs) { return lhs.size() < rhs.size(); });
const std::size_t longestSize = longest->size();
const float statNamesWidth = longestSize * characterSize * 0.6 + 2 * backgroundMargin;
const float statTextWidth = 7 * characterSize + 2 * backgroundMargin;
const float statHeight = pageSize * characterSize + 2 * backgroundMargin;
const float width = statNamesWidth + backgroundSpacing + statTextWidth;
osg::StateSet* stateset = _switch->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
#ifdef OSG_GL1_AVAILABLE
stateset->setAttribute(new osg::PolygonMode(), osg::StateAttribute::PROTECTED);
#endif
osg::Vec4 backgroundColor(0.0, 0.0, 0.0f, 0.3);
osg::Vec4 staticTextColor(1.0, 1.0, 0.0f, 1.0);
osg::Vec4 dynamicTextColor(1.0, 1.0, 1.0f, 1.0);
float backgroundMargin = 5;
float backgroundSpacing = 3;
// resource stats
for (std::size_t offset = 0; offset < mStatNames.size(); offset += pageSize)
{
osg::Group* group = new osg::Group;
osg::ref_ptr<osg::Group> group = new osg::Group;
group->setCullingActive(false);
_resourceStatsChildNum = _switch->getNumChildren();
_switch->addChild(group, false);
static const std::vector<std::string> statNames({
"FrameNumber",
"",
"Compiling",
"WorkQueue",
"WorkThread",
"UnrefQueue",
"",
"Texture",
"StateSet",
"Node",
"Shape",
"Shape Instance",
"Image",
"Nif",
"Keyframe",
"",
"Groundcover Chunk",
"Object Chunk",
"Terrain Chunk",
"Terrain Texture",
"Land",
"Composite",
"",
"NavMesh Jobs",
"NavMesh Waiting",
"NavMesh Pushed",
"NavMesh Processing",
"NavMesh DbJobs Write",
"NavMesh DbJobs Read",
"NavMesh DbCache Get",
"NavMesh DbCache Hit",
"NavMesh CacheSize",
"NavMesh UsedTiles",
"NavMesh CachedTiles",
"NavMesh Cache Get",
"NavMesh Cache Hit",
"",
"Mechanics Actors",
"Mechanics Objects",
"",
"Physics Actors",
"Physics Objects",
"Physics Projectiles",
"Physics HeightFields",
"",
"Lua UsedMemory",
});
static const auto longest = std::max_element(statNames.begin(), statNames.end(),
[](const std::string& lhs, const std::string& rhs) { return lhs.size() < rhs.size(); });
const float statNamesWidth = 13 * _characterSize + 2 * backgroundMargin;
const float statTextWidth = 7 * _characterSize + 2 * backgroundMargin;
const float statHeight = statNames.size() * _characterSize + 2 * backgroundMargin;
osg::Vec3 pos(_statsWidth - statNamesWidth - backgroundSpacing - statTextWidth, statHeight, 0.0f);
const std::size_t count = std::min(mStatNames.size() - offset, pageSize);
std::span<const std::string> currentStatNames(mStatNames.data() + offset, count);
osg::Vec3 pos(statsWidth - width, statHeight - characterSize, 0.0f);
group->addChild(
createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0),
createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, backgroundMargin + characterSize, 0),
statNamesWidth, statHeight, backgroundColor));
osg::ref_ptr<osgText::Text> staticText = new osgText::Text;
group->addChild(staticText.get());
staticText->setColor(staticTextColor);
staticText->setCharacterSize(_characterSize);
staticText->setCharacterSize(characterSize);
staticText->setPosition(pos);
std::ostringstream viewStr;
viewStr.clear();
viewStr.setf(std::ios::left, std::ios::adjustfield);
viewStr.width(longest->size());
for (const auto& statName : statNames)
{
viewStr.width(longestSize);
for (const std::string& statName : currentStatNames)
viewStr << statName << std::endl;
}
staticText->setText(viewStr.str());
pos.x() += statNamesWidth + backgroundSpacing;
group->addChild(
createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, _characterSize + backgroundMargin, 0),
createBackgroundRectangle(pos + osg::Vec3(-backgroundMargin, backgroundMargin + characterSize, 0),
statTextWidth, statHeight, backgroundColor));
osg::ref_ptr<osgText::Text> statsText = new osgText::Text;
group->addChild(statsText.get());
statsText->setColor(dynamicTextColor);
statsText->setCharacterSize(_characterSize);
statsText->setCharacterSize(characterSize);
statsText->setPosition(pos);
statsText->setText("");
statsText->setDrawCallback(new ResourceStatsTextDrawCallback(viewer->getViewerStats(), statNames));
statsText->setDrawCallback(new ResourceStatsTextDrawCallback(viewer.getViewerStats(), currentStatNames));
if (_textFont)
if (mTextFont != nullptr)
{
staticText->setFont(_textFont);
statsText->setFont(_textFont);
staticText->setFont(mTextFont);
statsText->setFont(mTextFont);
}
mSwitch->addChild(group, false);
}
}
void StatsHandler::getUsage(osg::ApplicationUsage& usage) const
{
usage.addKeyboardMouseBinding(_key, "On screen resource usage stats.");
usage.addKeyboardMouseBinding(statsHandlerKey, "On screen resource usage stats.");
}
void CollectStatistics(osgViewer::ViewerBase* viewer)
void collectStatistics(osgViewer::ViewerBase& viewer)
{
osgViewer::Viewer::Cameras cameras;
viewer->getCameras(cameras);
viewer.getCameras(cameras);
for (auto* camera : cameras)
{
if (collectStatGPU)
@ -537,17 +592,16 @@ namespace Resource
camera->getStats()->collectStats("scene", true);
}
if (collectStatEvent)
viewer->getViewerStats()->collectStats("event", true);
viewer.getViewerStats()->collectStats("event", true);
if (collectStatFrameRate)
viewer->getViewerStats()->collectStats("frame_rate", true);
viewer.getViewerStats()->collectStats("frame_rate", true);
if (collectStatUpdate)
viewer->getViewerStats()->collectStats("update", true);
viewer.getViewerStats()->collectStats("update", true);
if (collectStatResource)
viewer->getViewerStats()->collectStats("resource", true);
viewer.getViewerStats()->collectStats("resource", true);
if (collectStatViewerObjects)
viewer->getViewerStats()->collectStats("scene", true);
viewer.getViewerStats()->collectStats("scene", true);
if (collectStatEngine)
viewer->getViewerStats()->collectStats("engine", true);
viewer.getViewerStats()->collectStats("engine", true);
}
}

View File

@ -28,57 +28,47 @@ namespace Resource
class Profiler : public osgViewer::StatsHandler
{
public:
Profiler(bool offlineCollect, VFS::Manager* vfs);
explicit Profiler(bool offlineCollect, const VFS::Manager& vfs);
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) override;
private:
void setUpFonts();
bool _offlineCollect;
bool _initFonts;
osg::ref_ptr<osgText::Font> _textFont;
bool mInitFonts = false;
bool mOfflineCollect;
osg::ref_ptr<osgText::Font> mTextFont;
};
class StatsHandler : public osgGA::GUIEventHandler
{
public:
StatsHandler(bool offlineCollect, VFS::Manager* vfs);
void setKey(int key) { _key = key; }
int getKey() const { return _key; }
explicit StatsHandler(bool offlineCollect, const VFS::Manager& vfs);
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) override;
void setWindowSize(int w, int h);
void toggle(osgViewer::ViewerBase* viewer);
void setUpHUDCamera(osgViewer::ViewerBase* viewer);
void setUpScene(osgViewer::ViewerBase* viewer);
/** Get the keyboard and mouse usage of this manipulator.*/
void getUsage(osg::ApplicationUsage& usage) const override;
private:
osg::ref_ptr<osg::Switch> _switch;
int _key;
osg::ref_ptr<osg::Camera> _camera;
bool _initialized;
bool _statsType;
bool _offlineCollect;
unsigned mPage = 0;
bool mInitialized = false;
bool mOfflineCollect;
osg::ref_ptr<osg::Switch> mSwitch;
osg::ref_ptr<osg::Camera> mCamera;
osg::ref_ptr<osgText::Font> mTextFont;
std::vector<std::string> mStatNames;
float _statsWidth;
float _statsHeight;
void setWindowSize(int w, int h);
float _characterSize;
void toggle(osgViewer::ViewerBase& viewer);
int _resourceStatsChildNum;
void setUpHUDCamera(osgViewer::ViewerBase& viewer);
osg::ref_ptr<osgText::Font> _textFont;
void setUpScene(osgViewer::ViewerBase& viewer);
};
void CollectStatistics(osgViewer::ViewerBase* viewer);
void collectStatistics(osgViewer::ViewerBase& viewer);
}
#endif

View File

@ -63,7 +63,7 @@ namespace Terrain
void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
stats->setAttribute(frameNumber, "Terrain Chunk", mCache->getCacheSize());
Resource::reportStats("Terrain Chunk", frameNumber, mCache->getStats(), *stats);
}
void ChunkManager::clearCache()

View File

@ -1,6 +1,5 @@
#include "texturemanager.hpp"
#include <osg/Stats>
#include <osg/Texture2D>
#include <components/resource/imagemanager.hpp>
@ -56,7 +55,7 @@ namespace Terrain
void TextureManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
stats->setAttribute(frameNumber, "Terrain Texture", mCache->getCacheSize());
Resource::reportStats("Terrain Texture", frameNumber, mCache->getStats(), *stats);
}
}