Report more stats from caches

animationblending
elsid 1 year 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,33 +110,28 @@ 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
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())
if (Item* const item = find(key))
return item->mValue;
return std::nullopt;
return it->second.mValue;
}
/** 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;
}
@ -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,14 +47,36 @@ namespace Resource
bool collectStatUpdate = false;
bool collectStatEngine = false;
const std::vector<std::string> allStatNames = {
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",
@ -64,19 +88,17 @@ namespace Resource
"Terrain Chunk",
"Terrain Texture",
"Land",
"Composite",
"Mechanics Actors",
"Mechanics Objects",
"Physics Actors",
"Physics Objects",
"Physics Projectiles",
"Physics HeightFields",
"Lua UsedMemory",
};
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",
@ -92,6 +114,29 @@ namespace Resource
"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()
{
const char* envList = getenv("OPENMW_OSG_STATS_LIST");
@ -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