osg::Stats类用法及该类源码剖析

目录

1. 概述

2.代码环境说明

3.Stats类用法说明

3.1. 函数接口说明

3.2. 用法说明

4. 源码分析

5. 后记


1. 概述

      Stats是英文单词statistics的简写。顾名思义,就知道osg::Stats是osg中用来统计某些信息的类,如:帧率等,在osg中很多地方用到该类统计某些信息,如下代码是摘自osg的Viewer::eventTraversal()函数,该段代码记录了osg事件遍历开始时刻、事件遍历完成时刻、遍历耗时时长信息:

void Viewer::eventTraversal()
{
   ...... // 其它代码略

   if (getViewerStats() && getViewerStats()->collectStats("event"))
    {
        double endEventTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());

        // update current frames stats
        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal begin time", beginEventTraversal);
        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal end time", endEventTraversal);
        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal time taken", endEventTraversal-beginEventTraversal);
    }

    ...... // 其它代码略
}

如下代码是摘自osg的Viewer::renderingTraversals()函数,记录了某些必要的信息:

void ViewerBase::renderingTraversals()
{
    ...... // 其它代码略


                stats->setAttribute(frameNumber, "Number of unique StateSet", static_cast<double>(statsVisitor._statesetSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Group", static_cast<double>(statsVisitor._groupSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Transform", static_cast<double>(statsVisitor._transformSet.size()));
                stats->setAttribute(frameNumber, "Number of unique LOD", static_cast<double>(statsVisitor._lodSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Switch", static_cast<double>(statsVisitor._switchSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Geode", static_cast<double>(statsVisitor._geodeSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Drawable", static_cast<double>(statsVisitor._drawableSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Geometry", static_cast<double>(statsVisitor._geometrySet.size()));
                stats->setAttribute(frameNumber, "Number of unique Vertices", static_cast<double>(statsVisitor._uniqueStats._vertexCount));
                stats->setAttribute(frameNumber, "Number of unique Primitives", static_cast<double>(unique_primitives));


    ...... // 其它代码略

                stats->setAttribute(frameNumber, "Number of instanced Stateset", static_cast<double>(statsVisitor._numInstancedStateSet));
                stats->setAttribute(frameNumber, "Number of instanced Group", static_cast<double>(statsVisitor._numInstancedGroup));
                stats->setAttribute(frameNumber, "Number of instanced Transform", static_cast<double>(statsVisitor._numInstancedTransform));
                stats->setAttribute(frameNumber, "Number of instanced LOD", static_cast<double>(statsVisitor._numInstancedLOD));
                stats->setAttribute(frameNumber, "Number of instanced Switch", static_cast<double>(statsVisitor._numInstancedSwitch));
                stats->setAttribute(frameNumber, "Number of instanced Geode", static_cast<double>(statsVisitor._numInstancedGeode));
                stats->setAttribute(frameNumber, "Number of instanced Drawable", static_cast<double>(statsVisitor._numInstancedDrawable));
                stats->setAttribute(frameNumber, "Number of instanced Geometry", static_cast<double>(statsVisitor._numInstancedGeometry));
                stats->setAttribute(frameNumber, "Number of instanced Vertices", static_cast<double>(statsVisitor._instancedStats._vertexCount));
                stats->setAttribute(frameNumber, "Number of instanced Primitives", static_cast<double>(instanced_primitives));
...... // 其它代码略


        getViewerStats()->setAttribute(frameNumber, "Rendering traversals begin time ", beginRenderingTraversals);
        getViewerStats()->setAttribute(frameNumber, "Rendering traversals end time ", endRenderingTraversals);
        getViewerStats()->setAttribute(frameNumber, "Rendering traversals time taken", endRenderingTraversals-beginRenderingTraversals);

...... // 其它代码略
}

本博文讲述osg::Stats类用法及对其实现进行源码剖析。

2.代码环境说明

环境说明如下:

  • OpenSceneGraph-3.6.2。
  • Windows 10。
  • Microsoft Visual Studio Community 2022 (64 位) - Current
    版本 17.5.5。

说明:本博文是基于OpenSceneGraph的3.6.2版本来讲解的,读者版本可能和本人的版本不同,故本人的源码或功能可能在细节上和读者的有所不同。

3.Stats类用法说明

3.1. 函数接口说明

本节只讲述public接口,Stats类内部调用的private、protected接口不讲述。

Stats(const std::string& name);
Stats(const std::string& name, unsigned int numberOfFrames);

        这两个都为构造函数,参数name用来区分不同Stats类对象,参数numberOfFrames表示Stats类每次统计多少帧的帧信息,如果不传入该参数(此时是调用第1个构造函数的情况),则默认统计25帧的帧信息。

void allocate(unsigned int numberOfFrames);

       本函数为统计帧数为numberOfFrames的帧开辟内存空间,看看源码就知道其实就是分配一个大小为numberOfFrames的vector,该vector中的每个元素是一个map,该map存放该帧的每种属性键值对。

 bool setAttribute(unsigned int frameNumber, const std::string& attributeName, double value);

        本函数将帧索引号为frameNumber的帧的属性名为attributeName,属性值为value保存到内存,即allocate开辟的内存空间。

inline bool getAttribute(unsigned int frameNumber, const std::string& attributeName, double& value) const

本函数是setAttribute函数的逆函数,其功能将帧索引号为frameNumber的帧,该帧属性为attributeName的取出来存放到第三个参数value中。

bool getAveragedAttribute(const std::string& attributeName, double& value, bool averageInInverseSpace=false) const;
bool getAveragedAttribute(unsigned int startFrameNumber, unsigned int endFrameNumber, const std::string& attributeName, double& value, bool averageInInverseSpace=false) const;

        第1个函数获取第1次记录(最早)的帧(含)到当前最近(最晚)的一帧(含)的属性名为attributeName的属性的平均值(averageInInverseSpace为false)或者平均值的倒数值(averageInInverseSpace为true时)。

        第2个函数获取[startFrameNumber, endFrameNumber]属性名为attributeName的属性的平均值(averageInInverseSpace为false)或者平均值的倒数值(averageInInverseSpace为true时)。

void report(std::ostream& out, const char* indent=0) const;
void report(std::ostream& out, unsigned int frameNumber, const char* indent=0) const;

        第1个函数是向控制台输出第1次记录(最早)的帧(含)到当前最近(最晚)的一帧(含)的所有帧的所有属性值,并以ident为缩进量缩进打印输出。

        第2个函数是向控制台输出帧号为第2个参数的帧的所有属性值,并以ident为缩进量缩进打印输出。

void collectStats(const std::string& str, bool flag) { _collectMap[str] = flag; }

        这个函数收集(flag为true时)或不收集 (flag为false时)属性名为第1个参数表示的属性信息。

3.2. 用法说明

        视景器osgViewer::Viewer(单视图)或osgViewer::CompositeViewer(多视图)对象(为了便于后文描述,暂称为viewer)可以通过setViewerStats函数设置统计类对象。如下代码设置osg::Stats对象到视景器:

osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
auto pStats = new osg::Stats("test"); // 构造一个统计类对象,参数为统计类对象名称,最好根据业务需求取名
pStats->collectStats("scene", true);  // 统计场景数据。如果设置为false,则场景有关的数据不统计
viewer->setViewerStats(pStats); // 将统计类对象设置到视景器。
 
 
...... // 其它代码
 
viewer->run();

在执行视景器的run函数时会进入到ViewerBase类的renderingTraversals函数,在该函数中统计了场景相关信息,如下:

void ViewerBase::renderingTraversals()
{
    ...... // 其它代码略
 
    // 如果为视景器设置了统计对象且需要统计场景类的各种属性
    if (getViewerStats() && getViewerStats()->collectStats("scene"))
    {
 
        Views views;
        getViews(views);
        for(Views::iterator vitr = views.begin();
            vitr != views.end();
            ++vitr)
        {
            View* view = *vitr;
            osg::Stats* stats = view->getStats();
            osg::Node* sceneRoot = view->getSceneData();
            if (sceneRoot && stats)
            {
                osgUtil::StatsVisitor statsVisitor;
                sceneRoot->accept(statsVisitor);
                statsVisitor.totalUpStats();
 
                unsigned int unique_primitives = 0;
                osgUtil::Statistics::PrimitiveCountMap::iterator pcmitr;
                for(pcmitr = statsVisitor._uniqueStats.GetPrimitivesBegin();
                    pcmitr != statsVisitor._uniqueStats.GetPrimitivesEnd();
                    ++pcmitr)
                {
                    unique_primitives += pcmitr->second;
                }
 
                stats->setAttribute(frameNumber, "Number of unique StateSet", static_cast<double>(statsVisitor._statesetSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Group", static_cast<double>(statsVisitor._groupSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Transform", static_cast<double>(statsVisitor._transformSet.size()));
                stats->setAttribute(frameNumber, "Number of unique LOD", static_cast<double>(statsVisitor._lodSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Switch", static_cast<double>(statsVisitor._switchSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Geode", static_cast<double>(statsVisitor._geodeSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Drawable", static_cast<double>(statsVisitor._drawableSet.size()));
                stats->setAttribute(frameNumber, "Number of unique Geometry", static_cast<double>(statsVisitor._geometrySet.size()));
                stats->setAttribute(frameNumber, "Number of unique Vertices", static_cast<double>(statsVisitor._uniqueStats._vertexCount));
                stats->setAttribute(frameNumber, "Number of unique Primitives", static_cast<double>(unique_primitives));
 
                unsigned int instanced_primitives = 0;
                for(pcmitr = statsVisitor._instancedStats.GetPrimitivesBegin();
                    pcmitr != statsVisitor._instancedStats.GetPrimitivesEnd();
                    ++pcmitr)
                {
                    instanced_primitives += pcmitr->second;
                }
 
                stats->setAttribute(frameNumber, "Number of instanced Stateset", static_cast<double>(statsVisitor._numInstancedStateSet));
                stats->setAttribute(frameNumber, "Number of instanced Group", static_cast<double>(statsVisitor._numInstancedGroup));
                stats->setAttribute(frameNumber, "Number of instanced Transform", static_cast<double>(statsVisitor._numInstancedTransform));
                stats->setAttribute(frameNumber, "Number of instanced LOD", static_cast<double>(statsVisitor._numInstancedLOD));
                stats->setAttribute(frameNumber, "Number of instanced Switch", static_cast<double>(statsVisitor._numInstancedSwitch));
                stats->setAttribute(frameNumber, "Number of instanced Geode", static_cast<double>(statsVisitor._numInstancedGeode));
                stats->setAttribute(frameNumber, "Number of instanced Drawable", static_cast<double>(statsVisitor._numInstancedDrawable));
                stats->setAttribute(frameNumber, "Number of instanced Geometry", static_cast<double>(statsVisitor._numInstancedGeometry));
                stats->setAttribute(frameNumber, "Number of instanced Vertices", static_cast<double>(statsVisitor._instancedStats._vertexCount));
                stats->setAttribute(frameNumber, "Number of instanced Primitives", static_cast<double>(instanced_primitives));
           }
        }
    }
 
...... // 其它代码略

如果像下面那样调用前文到的collectStats函数关闭场景统计信息,则所有场景有关的属性都不会统计:

// 关闭场景属性的统计
getViewerStats() && getViewerStats()->collectStats("scene", false);

4. 源码分析

本类源码很简单,唯一有点难理解的是setAttribute函数,该函数如下:

bool Stats::setAttribute(unsigned int frameNumber, const std::string& attributeName, double value)
{
    if (frameNumber<getEarliestFrameNumber()) return false;

    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);

    if (frameNumber>_latestFrameNumber)
    {
        // need to advance

        // first clear the entries up to and including the new frameNumber
        for(unsigned int i = _latestFrameNumber+1; i<= frameNumber; ++i)
        {
            unsigned int index = (i - _baseFrameNumber) % _attributeMapList.size();
            _attributeMapList[index].clear();
        }

        if ( (frameNumber-_baseFrameNumber) >= static_cast<unsigned int>(_attributeMapList.size()))
        {
            _baseFrameNumber = (frameNumber/_attributeMapList.size())*_attributeMapList.size();
        }

        _latestFrameNumber = frameNumber;

    }

    int index = getIndex(frameNumber);
    if (index<0)
    {
        OSG_NOTICE<<"Failed to assign valid index for Stats::setAttribute("<<frameNumber<<","<<attributeName<<","<<value<<")"<<std::endl;
        return false;
    }

    AttributeMap& attributeMap = _attributeMapList[index];
    attributeMap[attributeName] = value;

    return true;
}

这个函数的基本思想是:利用vector构建一个环形、首尾相接的顺序表容器,该容器存放从最早的一帧到最近的一帧。容器中的元素是按帧号来索引的,容器中的每个元素是个map,该map的key该帧的属性名,value是该属性对应的属性值。当超过预定设置的帧数目时(如:默认的25帧或通过allocate设置的帧数目),则更新环形容器存放的帧编号,以保证环形顺序表容器始终存放的是当前最近的帧数目(如:默认的25帧或通过allocate设置的帧数目)。如下所示:

5. 后记

      osg::Stats一般和osgViewer::StatsHandler一起使用,关于osgViewer::StatsHandler的使用,可参考:浅谈osgViewer::StatsHandler、osg::Stats类的用法

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值