|
|
|
@ -2,7 +2,11 @@
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <span>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <string_view>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include <osg/PolygonMode>
|
|
|
|
|
|
|
|
|
@ -18,143 +22,207 @@
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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",
|
|
|
|
|
"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",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void setupStatCollection()
|
|
|
|
|
{
|
|
|
|
|
const char* envList = getenv("OPENMW_OSG_STATS_LIST");
|
|
|
|
|
if (envList == nullptr)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
std::string_view kwList(envList);
|
|
|
|
|
std::string_view kwList(envList);
|
|
|
|
|
|
|
|
|
|
auto kwBegin = kwList.begin();
|
|
|
|
|
auto kwBegin = kwList.begin();
|
|
|
|
|
|
|
|
|
|
while (kwBegin != kwList.end())
|
|
|
|
|
{
|
|
|
|
|
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")
|
|
|
|
|
while (kwBegin != kwList.end())
|
|
|
|
|
{
|
|
|
|
|
collectStatGPU = true;
|
|
|
|
|
collectStatEvent = true;
|
|
|
|
|
collectStatFrameRate = true;
|
|
|
|
|
collectStatUpdate = true;
|
|
|
|
|
collectStatEngine = true;
|
|
|
|
|
collectStatRendering = true;
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
if (kwEnd == kwList.end())
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
kwBegin = std::next(kwEnd);
|
|
|
|
|
kwBegin = std::next(kwEnd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class SetFontVisitor : public osg::NodeVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SetFontVisitor(osgText::Font* font)
|
|
|
|
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
|
|
|
|
, mFont(font)
|
|
|
|
|
osg::ref_ptr<osg::Geometry> createBackgroundRectangle(
|
|
|
|
|
const osg::Vec3& pos, const float width, const float height, const osg::Vec4& color)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void apply(osg::Drawable& node) override
|
|
|
|
|
osg::ref_ptr<osgText::Font> getMonoFont(const VFS::Manager& vfs)
|
|
|
|
|
{
|
|
|
|
|
if (osgText::Text* text = dynamic_cast<osgText::Text*>(&node))
|
|
|
|
|
if (osgDB::Registry::instance()->getReaderWriterForExtension("ttf") && vfs.exists(fontName))
|
|
|
|
|
{
|
|
|
|
|
text->setFont(mFont);
|
|
|
|
|
const Files::IStreamPtr streamPtr = vfs.get(fontName);
|
|
|
|
|
return osgText::readRefFontStream(*streamPtr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
osgText::Font* mFont;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<osgText::Font> getMonoFont(VFS::Manager* vfs)
|
|
|
|
|
{
|
|
|
|
|
if (osgDB::Registry::instance()->getReaderWriterForExtension("ttf") && vfs->exists(sFontName))
|
|
|
|
|
{
|
|
|
|
|
Files::IStreamPtr streamPtr = vfs->get(sFontName);
|
|
|
|
|
return osgText::readRefFontStream(*streamPtr.get());
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
_camera = new osg::Camera;
|
|
|
|
|
_camera->getOrCreateStateSet()->setGlobalDefaults();
|
|
|
|
|
_camera->setRenderer(new osgViewer::Renderer(_camera.get()));
|
|
|
|
|
_camera->setProjectionResizePolicy(osg::Camera::FIXED);
|
|
|
|
|
class SetFontVisitor : public osg::NodeVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SetFontVisitor(osgText::Font* font)
|
|
|
|
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
|
|
|
|
, mFont(font)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_resourceStatsChildNum = 0;
|
|
|
|
|
void apply(osg::Drawable& node) override
|
|
|
|
|
{
|
|
|
|
|
if (osgText::Text* text = dynamic_cast<osgText::Text*>(&node))
|
|
|
|
|
{
|
|
|
|
|
text->setFont(mFont);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_textFont = getMonoFont(vfs);
|
|
|
|
|
private:
|
|
|
|
|
osgText::Font* mFont;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Profiler::Profiler(bool offlineCollect, VFS::Manager* vfs)
|
|
|
|
|
: _offlineCollect(offlineCollect)
|
|
|
|
|
, _initFonts(false)
|
|
|
|
|
Profiler::Profiler(bool offlineCollect, const VFS::Manager& vfs)
|
|
|
|
|
: mOfflineCollect(offlineCollect)
|
|
|
|
|
, mTextFont(getMonoFont(vfs))
|
|
|
|
|
{
|
|
|
|
|
_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 +230,44 @@ 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))
|
|
|
|
|
{
|
|
|
|
|
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 +277,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 +314,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;
|
|
|
|
|
|
|
|
|
|
mCamera->setNodeMask(0);
|
|
|
|
|
mSwitch->setAllChildrenOff();
|
|
|
|
|
|
|
|
|
|
viewer->getViewerStats()->collectStats("resource", false);
|
|
|
|
|
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 +386,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);
|
|
|
|
|
|
|
|
|
|
_camera->setRenderer(new osgViewer::Renderer(_camera.get()));
|
|
|
|
|
mCamera->setClearMask(0);
|
|
|
|
|
mCamera->setAllowEventFocus(false);
|
|
|
|
|
|
|
|
|
|
_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
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
explicit ResourceStatsTextDrawCallback(osg::Stats* stats, std::span<const std::string> statNames)
|
|
|
|
|
: mStats(stats)
|
|
|
|
|
, mStatNames(statNames)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void drawImplementation(osg::RenderInfo& renderInfo, const osg::Drawable* drawable) const override
|
|
|
|
|
{
|
|
|
|
|
if (!mStats)
|
|
|
|
|
return;
|
|
|
|
|
void drawImplementation(osg::RenderInfo& renderInfo, const osg::Drawable* drawable) const override
|
|
|
|
|
{
|
|
|
|
|
if (mStats == nullptr)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
osgText::Text* text = (osgText::Text*)(drawable);
|
|
|
|
|
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);
|
|
|
|
|
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;
|
|
|
|
|
const unsigned int frameNumber = renderInfo.getState()->getFrameStamp()->getFrameNumber() - 1;
|
|
|
|
|
|
|
|
|
|
for (const auto& statName : mStatNames.get())
|
|
|
|
|
{
|
|
|
|
|
if (statName.empty())
|
|
|
|
|
viewStr << std::endl;
|
|
|
|
|
else
|
|
|
|
|
for (const std::string& statName : mStatNames)
|
|
|
|
|
{
|
|
|
|
|
double value = 0.0;
|
|
|
|
|
if (mStats->getAttribute(frameNumber, statName, value))
|
|
|
|
|
viewStr << std::setw(8) << value << std::endl;
|
|
|
|
|
if (statName.empty())
|
|
|
|
|
viewStr << std::endl;
|
|
|
|
|
else
|
|
|
|
|
viewStr << std::setw(8) << "." << std::endl;
|
|
|
|
|
{
|
|
|
|
|
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->setText(viewStr.str());
|
|
|
|
|
|
|
|
|
|
text->drawImplementation(renderInfo);
|
|
|
|
|
}
|
|
|
|
|
text->drawImplementation(renderInfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::Stats> mStats;
|
|
|
|
|
std::reference_wrapper<const std::vector<std::string>> mStatNames;
|
|
|
|
|
};
|
|
|
|
|
private:
|
|
|
|
|
osg::ref_ptr<osg::Stats> mStats;
|
|
|
|
|
std::span<const std::string> mStatNames;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StatsHandler::setUpScene(osgViewer::ViewerBase* viewer)
|
|
|
|
|
void StatsHandler::setUpScene(osgViewer::ViewerBase& viewer)
|
|
|
|
|
{
|
|
|
|
|
_switch = new osg::Switch;
|
|
|
|
|
|
|
|
|
|
_camera->addChild(_switch);
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
const auto longest = std::max_element(allStatNames.begin(), allStatNames.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;
|
|
|
|
|
|
|
|
|
|
for (std::size_t offset = 0; offset < allStatNames.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(allStatNames.size() - offset, pageSize);
|
|
|
|
|
std::span<const std::string> currentStatNames(allStatNames.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 +541,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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|