Report more stats from caches

animationblending
elsid 5 months ago
parent ae41ebfc83
commit 215404e126
No known key found for this signature in database
GPG Key ID: 4DE04C198CBA7625

@ -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);
}
}

@ -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);
}
}

@ -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);
}
}

@ -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)

@ -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

@ -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);
}
}

@ -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));
}
}

@ -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

@ -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);
}
}

@ -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);
}
}

@ -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,
};
}
}

@ -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;
};
}

@ -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);
}
}

@ -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

@ -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)

@ -20,6 +20,8 @@
#include <components/vfs/manager.hpp>
#include "cachestats.hpp"
namespace Resource
{
namespace
@ -45,52 +47,95 @@ namespace Resource
bool collectStatUpdate = false;
bool collectStatEngine = false;
const std::vector<std::string> allStatNames = {
"FrameNumber",
"Compiling",
"WorkQueue",
"WorkThread",
"UnrefQueue",
"Texture",
"StateSet",
"Node",
"Shape",
"Shape Instance",
"Image",
"Nif",
"Keyframe",
"Groundcover Chunk",
"Object Chunk",
"Terrain Chunk",
"Terrain Texture",
"Land",
"Composite",
"Mechanics Actors",
"Mechanics Objects",
"Physics Actors",
"Physics Objects",
"Physics Projectiles",
"Physics HeightFields",
"Lua UsedMemory",
"CellPreloader Count",
"CellPreloader Added",
"CellPreloader Evicted",
"CellPreloader Loaded",
"CellPreloader Expired",
"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> generateAllStatNames()
{
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",
"",
"",
"",
"",
};
constexpr std::string_view caches[] = {
"Node",
"Shape",
"Shape Instance",
"Image",
"Nif",
"Keyframe",
"Groundcover Chunk",
"Object Chunk",
"Terrain Chunk",
"Terrain Texture",
"Land",
};
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)
{
Resource::addCacheStatsAttibutes(caches[i], statNames);
if ((i + 1) % 5 != 0)
statNames.emplace_back();
}
for (std::string_view name : cellPreloader)
statNames.emplace_back(name);
statNames.emplace_back();
for (std::string_view name : navMesh)
statNames.emplace_back(name);
return statNames;
}
void setupStatCollection()
{
@ -258,6 +303,7 @@ namespace Resource
, 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);
@ -465,7 +511,7 @@ namespace Resource
const osg::Vec4 staticTextColor(1.0, 1.0, 0.0f, 1.0);
const osg::Vec4 dynamicTextColor(1.0, 1.0, 1.0f, 1.0);
const auto longest = std::max_element(allStatNames.begin(), allStatNames.end(),
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;
@ -473,14 +519,14 @@ namespace Resource
const float statHeight = pageSize * characterSize + 2 * backgroundMargin;
const float width = statNamesWidth + backgroundSpacing + statTextWidth;
for (std::size_t offset = 0; offset < allStatNames.size(); offset += pageSize)
for (std::size_t offset = 0; offset < mStatNames.size(); offset += pageSize)
{
osg::ref_ptr<osg::Group> group = new osg::Group;
group->setCullingActive(false);
const std::size_t count = std::min(allStatNames.size() - offset, pageSize);
std::span<const std::string> currentStatNames(allStatNames.data() + offset, count);
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(

@ -57,6 +57,7 @@ namespace Resource
osg::ref_ptr<osg::Switch> mSwitch;
osg::ref_ptr<osg::Camera> mCamera;
osg::ref_ptr<osgText::Font> mTextFont;
std::vector<std::string> mStatNames;
void setWindowSize(int w, int h);

@ -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()

@ -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);
}
}

Loading…
Cancel
Save