void ViewerBase::frame(double simulationTime)
{
if (_done) return; // OSG_NOTICE<<std::endl<<"CompositeViewer::frame()"<<std::endl<<std::endl; if (_firstFrame)
{
viewerInit(); if (!isRealized())
{
realize();
} _firstFrame = false;
}
advance(simulationTime); eventTraversal();
updateTraversal();
renderingTraversals();
}

  当前位置:osgViewer/Viewer.cpp 第 643行,osgViewer:: Viewer::advance()

  好的,现在我们终于正式进入仿真循环当中了,之前的 realize 函数虽然十分重要,但它实际上是循环运行前的准备工作。而从这一日开始介绍的 advance,eventTraversal,updateTraversal 和 renderingTraversals 函数,才是真正的一帧的组成部分。

advance 函数的工作内容如下:
  1、获取上一次记录的参考时间(Reference Time);
  2、根据当前时刻,重新记录参考时间,并因此得到两次记录之间的差值,即一帧经历的时间;
  3、记录已经经过的帧数;
  4、有的时候我们需要将帧速率,参考时间等内容予以记录并显示给用户,此时需要通过 ViewerBase::getStats 函数获得 osg::Stats 对象,用以进行帧状态的保存和显示;
  5、如果需要的话,使用 Referenced::getDeleteHandler()来处理 osg::Referenced 对象被弃用之后的删除工作。

  仿真循环运行的参考时间,总时间和总帧数都是由 osg::FrameStamp 变量_frameStamp来处理的,如果用户程序需要获取这些信息的话,也可以通过读取这个变量的成员函数来实现。当然,使用 Viewer 中的 osg::Stats 变量_stats 也是可以的,缺省情况下,这个变量会忠实地记录当前帧以及之前的 24 帧的每帧用时,事件遍历用时,更新遍历用时,以及渲染遍历用时信息。如果我们想获得更多的历史数据,抑或对于频繁的记录操作感到厌烦,可以在开始仿真循环之前执行 ViewerBase::setStats 函数,重新设置这个记录器的参数,或者简单地将其置为 NULL。

void Viewer::advance(double simulationTime)
{
if (_done) return; double previousReferenceTime = _frameStamp->getReferenceTime();
unsigned int previousFrameNumber = _frameStamp->getFrameNumber(); _frameStamp->setFrameNumber(_frameStamp->getFrameNumber()+); _frameStamp->setReferenceTime( osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick()) ); if (simulationTime==USE_REFERENCE_TIME)
{
_frameStamp->setSimulationTime(_frameStamp->getReferenceTime());
}
else
{
_frameStamp->setSimulationTime(simulationTime);
} if (getViewerStats() && getViewerStats()->collectStats("frame_rate"))
{
// update previous frame stats
double deltaFrameTime = _frameStamp->getReferenceTime() - previousReferenceTime;
getViewerStats()->setAttribute(previousFrameNumber, "Frame duration", deltaFrameTime);
getViewerStats()->setAttribute(previousFrameNumber, "Frame rate", 1.0/deltaFrameTime); // update current frames stats
getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Reference time", _frameStamp->getReferenceTime());
}    //简单说来,它的工作是收集所有已经弃用的 OSG 场景对象,并在需要的时候(例如advance 函数代码的相应部分)执行 osg::DeleteHandler::flush,将它们统一删除。
   //这里所说的“弃用”,与我们非常熟悉的 osg::ref_ptr 智能指针是密切相关的。我们已经知道,ref_ptr 采用内存引用计数的方式,当一个场景对象(通常是 Node 节点)链接到根节点或者其他节点时,它的引用计数加一,这一动作是通过 ref_ptr::ref()函数实现的;如果它被剔除出节点,那么它的引用计数减一,执行这一工作的函数是 ref_ptr::unref()。unref 函数的另一个重要任务是检查对象的引用计数值是否到达零,如果已经没有被其它对象所引用,那么称这个对象被“弃用”,它应当被立即删除,以释放相应的内存空间,避免泄露。
if (osg::Referenced::getDeleteHandler())
{
osg::Referenced::getDeleteHandler()->flush();
osg::Referenced::getDeleteHandler()->setFrameNumber(_frameStamp->getFrameNumber());
} }
  C++中最通用的删除对象的方法是 delete,OSG 的智能指针也是采用这种方式来释放对象的,不过由于OSG采用多线程更新/渲染的方式(这一点我们会在后面的日子中详细介绍),这样做可能带来会某些隐患,想象这样一种情况:
  1、场景某个的节点负责显示某种图形,它的工作一直很正常;
  2、我们采用 DrawThreadPerContext 或者 CullThreadPerCameraDrawThreadPerContext 线程模型,根据前一日中我们所知的,这两种模式中存在“上次的渲染工作与下次的更新工作交叠”这一情形。
  3、假设我们在更新工作中立即将这个节点删除,而上次渲染工作可能正要将这个节点中的数据送往 OpenGL 图形渲染管线,那么灾难就发生了……
  看到这里,读者您一定已经想到了一种解决方案。对,就是在渲染后台也使用 ref_ptr来引用(ref)图形节点,然后在渲染结束取消引用(unref),这样不就可以避免无谓的牺牲了吗?也省却用户的很多麻烦。
  说得有道理,不过这其中恐怕忽视了一个核心的问题:渲染效率。是的,假设我们要渲染成千上万个这样的几何体节点(这对您来说也许简直是家常便饭),如果每个节点的渲染都要多执行一次 ref/unref 的话,效率的损失将是无法被忽略的。事实上经过测算,CPU 时间的流失大概可以达到 6%,对于一个实时渲染系统来说,这的确值得斟酌。
  因此,OSG 的新版本中提出了 DeleteHandler 的概念,也就是“垃圾收集”,把那些引用计数已经为零的对象统一收集起来,确保它们不会再被渲染线程用到之后,再在适当的地方予以释放。DeleteHandler 有一个重要的参数_numFramesToRetainObjects,它的意义是,垃圾对象被收集之后,再经过多少帧(默认设置是 2),方予以释放。因此,OSG 的垃圾收集器同样需要使用 DeleteHandler::setFrameNumber 来记录当前的帧数。
  这个概念提出的时间并不长,也许还需要一段时间的测试,也许会有更好的方案来替代它。目前,OSG 的发行版本仍然采用第一种方式,也就是渲染后台采用 ref_ptr 引用计数的方式来避免删除对象造成的问题;如果您想要尝试使用和帮助调试 DeleteHandler 的话,可以在自己的程序中(main 函数之前)加入:
  #undef OSGUTIL_RENDERBACKEND_USE_REF_PTR
以请求使用 DeleteHandler。
DeleteHandler* Referenced::getDeleteHandler()
{
return s_deleteHandler.get();
}

又一次回到初始函数

void ViewerBase::frame(double simulationTime)
{
if (_done) return; // OSG_NOTICE<<std::endl<<"CompositeViewer::frame()"<<std::endl<<std::endl; if (_firstFrame)
{
viewerInit(); if (!isRealized())
{
realize();
} _firstFrame = false;
}
advance(simulationTime); eventTraversal();
updateTraversal();
renderingTraversals();
}

eventTraversal()这个函数有二百多行,在我们实验室的编码标准中是不允许的,这种情况下必须拆分成好几个函数,哈哈哈,言归正传,继续看王锐老师的解读

在开始了解 OSG 事件遍历的过程之前,也许有必要先总结一下 OSG 视景器、摄像机与场景的关系。如下图所示:
 
  视景器包括几个最主要的组件:漫游器_cameraManipulator,用于实现交互式的场景漫游;事件处理器组_eventHandlers,负责处理视景器的事件队列_eventQueue,主要是键盘/鼠标等事的处理;场景_scene,它包括视景器所对应的场景图形根节点,以及用于提高节点和图像数据处理速度的两个分页数据库;摄像机_camera 和_slaves,前者为场景的主摄像机,后者为从摄像机组,不过 OSG 并没有规定一定要使用主摄像机来显示场景,它的更重要的作用是为 OSG 世界矩阵的计算提供依据。
  摄像机是 OSG 视图显示的核心器件,没有摄像机就没有办法将场景图形的实景展现给用户。它包括:
  1、视口(Viewport),它指示了摄像机显示窗口的位置和尺寸。
  2、图形上下文(GraphicsContext),通常这也就是平台相关的图形显示窗口(即GraphicsWindow,对于 Win32 系统而言,它实际上是通过 CreateWindowEx 这个熟悉的 API来创建的),不过也可能是离屏渲染的设备(例如 PixelBufferWin32)。
  图形窗口的另一个任务是及时把系统和用户交互的事件反馈到事件处理器组中去,观察Win32 平台下的窗口设备GraphicsWindowWin32 中的handleNativeWindowingEvent函数和它的传入参数,hwnd,msg,lParam,wParam……没错,相信您已经找到熟悉的感觉了。OSG所处理的事件正是来源于 Win32 SDK 编程中常见的窗口消息。如果您正好有些新的想法想要实践(例如,捕捉 WM_HOTKEY 系统热键消息,并传递给 OSG 的事件队列),不妨试一试修改相关的内容,并记得把好的建议发送到 osg-users。
  3、渲染器(GraphicsOperation,更多时候是 osgViewer::Renderer),这是整个 OSG 筛选(CULL)和绘制(DRAW)的关键,它的功能我们会在后面的日子里慢慢展开。此外,OSG 的显示设置工具 DisplaySettings 也会直接对摄像机的处理工作负责,大部分设置选项都可以传递到摄像机对应的窗口特性(GraphicsContext::Traits)中,并在渲染过程中发挥作用。
 
void Viewer::eventTraversal()
{
if (_done) return; double cutOffTime = _frameStamp->getReferenceTime(); double beginEventTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick()); // OSG_NOTICE<<"Viewer::frameEventTraversal()."<<std::endl; // need to copy events from the GraphicsWindow's into local EventQueue;
osgGA::EventQueue::Events events; Contexts contexts;
getContexts(contexts); // set done if there are no windows
checkWindowStatus(contexts);
if (_done) return; osgGA::GUIEventAdapter* eventState = getEventQueue()->getCurrentEventState(); // get events from user Devices attached to Viewer.
for(Devices::iterator eitr = _eventSources.begin();
eitr != _eventSources.end();
++eitr)
{
osgGA::Device* es = eitr->get();
if (es->getCapabilities() & osgGA::Device::RECEIVE_EVENTS)
es->checkEvents(); // open question, will we need to reproject mouse coordinates into current view's coordinate frame as is down for GraphicsWindow provided events?
// for now assume now and just get the events directly without any reprojection.
es->getEventQueue()->takeEvents(events, cutOffTime);
} // get events from all windows attached to Viewer.
for(Contexts::iterator citr = contexts.begin();
citr != contexts.end();
++citr)
{
osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(*citr);
if (gw)
{
gw->checkEvents(); osgGA::EventQueue::Events gw_events;
gw->getEventQueue()->takeEvents(gw_events, cutOffTime); osgGA::EventQueue::Events::iterator itr;
for(itr = gw_events.begin();
itr != gw_events.end();
++itr)
{
osgGA::GUIEventAdapter* event = (*itr)->asGUIEventAdapter();
if (!event) continue; event->setGraphicsContext(gw); switch(event->getEventType())
{
case(osgGA::GUIEventAdapter::PUSH):
case(osgGA::GUIEventAdapter::RELEASE):
case(osgGA::GUIEventAdapter::DOUBLECLICK):
case(osgGA::GUIEventAdapter::MOVE):
case(osgGA::GUIEventAdapter::DRAG):
{
if (event->getEventType()!=osgGA::GUIEventAdapter::DRAG ||
eventState->getGraphicsContext()!=event->getGraphicsContext() ||
eventState->getNumPointerData()<)
{
generatePointerData(*event);
}
else
{
reprojectPointerData(*eventState, *event);
} eventState->copyPointerDataFrom(*event); break;
}
default:
event->copyPointerDataFrom(*eventState);
break;
} events.push_back(event);
} for(itr = gw_events.begin();
itr != gw_events.end();
++itr)
{
osgGA::GUIEventAdapter* event = (*itr)->asGUIEventAdapter();
if (!event) continue;
switch(event->getEventType())
{
case(osgGA::GUIEventAdapter::CLOSE_WINDOW):
{
bool wasThreading = areThreadsRunning();
if (wasThreading) stopThreading(); gw->close();
_currentContext = NULL; if (wasThreading) startThreading(); break;
}
default:
break;
}
} }
} // create a frame event for the new frame.
{
osg::ref_ptr<osgGA::GUIEventAdapter> event = _eventQueue->frame( getFrameStamp()->getReferenceTime() ); if (!eventState || eventState->getNumPointerData()<)
{
generatePointerData(*event);
}
else
{
reprojectPointerData(*eventState, *event);
}
} // OSG_NOTICE<<"mouseEventState Xmin = "<<eventState->getXmin()<<" Ymin="<<eventState->getYmin()<<" xMax="<<eventState->getXmax()<<" Ymax="<<eventState->getYmax()<<std::endl; _eventQueue->takeEvents(events, cutOffTime); // OSG_NOTICE<<"Events "<<events.size()<<std::endl; if ((_keyEventSetsDone!=) || _quitEventSetsDone)
{
for(osgGA::EventQueue::Events::iterator itr = events.begin();
itr != events.end();
++itr)
{
osgGA::GUIEventAdapter* event = (*itr)->asGUIEventAdapter();
if (!event) continue; // ignore event if it's already been handled.
if (event->getHandled()) continue; switch(event->getEventType())
{
case(osgGA::GUIEventAdapter::KEYUP):
if (_keyEventSetsDone && event->getKey()==_keyEventSetsDone) _done = true;
break; case(osgGA::GUIEventAdapter::QUIT_APPLICATION):
if (_quitEventSetsDone) _done = true;
break; default:
break;
}
}
} if (_done) return; if (_eventVisitor.valid() && getSceneData())
{
_eventVisitor->setFrameStamp(getFrameStamp());
_eventVisitor->setTraversalNumber(getFrameStamp()->getFrameNumber()); for(osgGA::EventQueue::Events::iterator itr = events.begin();
itr != events.end();
++itr)
{
osgGA::GUIEventAdapter* event = (*itr)->asGUIEventAdapter();
if (!event) continue; _eventVisitor->reset();
_eventVisitor->addEvent( event ); getSceneData()->accept(*_eventVisitor); // Do EventTraversal for slaves with their own subgraph
for(unsigned int i=; i<getNumSlaves(); ++i)
{
osg::View::Slave& slave = getSlave(i);
osg::Camera* camera = slave._camera.get();
if(camera && !slave._useMastersSceneData)
{
camera->accept(*_eventVisitor);
}
} // call any camera event callbacks, but only traverse that callback, don't traverse its subgraph
// leave that to the scene update traversal.
osg::NodeVisitor::TraversalMode tm = _eventVisitor->getTraversalMode();
_eventVisitor->setTraversalMode(osg::NodeVisitor::TRAVERSE_NONE); if (_camera.valid()) _camera->accept(*_eventVisitor); for(unsigned int i=; i<getNumSlaves(); ++i)
{
osg::View::Slave& slave = getSlave(i);
osg::Camera* camera = slave._camera.get();
if (camera && slave._useMastersSceneData)
{
camera->accept(*_eventVisitor);
}
} _eventVisitor->setTraversalMode(tm); }
} for(osgGA::EventQueue::Events::iterator itr = events.begin();
itr != events.end();
++itr)
{
osgGA::Event* event = itr->get();
for(EventHandlers::iterator hitr = _eventHandlers.begin();
hitr != _eventHandlers.end();
++hitr)
{
(*hitr)->handle( event, , _eventVisitor.get());
} } for(osgGA::EventQueue::Events::iterator itr = events.begin();
itr != events.end();
++itr)
{
osgGA::Event* event = itr->get();
if (event && _cameraManipulator.valid())
{
_cameraManipulator->handle( event, , _eventVisitor.get());
}
} 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);
} }

osgViewer:: Viewer::advance() osg多线程与智能指针的更多相关文章

  1. OSG中的智能指针

    在OpenSceneGraph中,智能指针(Smart pointer)的概念指的是一种类的模板,它针对某一特定类型的对象(即Referenced类及其派生类)构建,提供了自己的管理模式,以避免因为用 ...

  2. 不可不表的OSG智能指针之强指针与弱指针 《转载》

    不可不表的OSG智能指针之强指针与弱指针 <转载> 使用OSG的人都知道OSG的内存管理方式采用了智能指针,通过智能指针的方式让OSG自己处理对象的销毁工作.在OSG中有两个智能指针类型, ...

  3. osg(OpenSceneGraph)学习笔记1:智能指针osg::ref_ptr<>

    OSG的智能指针,osg::ref_ptr<> osg::Referenced类管理引用计数内存块,osg::ref_ptr需要使用以它为基类的其它类作为模板参数. osg::ref_pt ...

  4. 【C/C++学院】0904-boost智能指针/boost多线程锁定/哈希库/正則表達式

    boost_array_bind_fun_ref Array.cpp #include<boost/array.hpp> #include <iostream> #includ ...

  5. 引用内部函数绑定机制,R转义字符,C++引用,别名,模板元,宏,断言,C++多线程,C++智能指针

     1.引用内部函数绑定机制 #include<iostream> #include<functional> usingnamespacestd; usingnamespac ...

  6. Qt osg QWidget osgViewer::Viewer

    osgViewer::Viewer* _viewer = nullptr; _viewer = new osgViewer::Viewer;osg::ref_ptr<osg::Group> ...

  7. 根据OSG中的ref_ptr和Reference简化的智能指针

    main.cpp测试代码 #include "TestSmartPointer" void fun() { SP<TestSmartPointer> sp1=new T ...

  8. 《最长的一帧》 osg3.4 osgViewer::View::init() osgViewer::Viewer::getContexts()

    开始:osgViewer/ViewerBase.cpp   389行,startThreading()函数,启动线程   void ViewerBase::startThreading() { if ...

  9. enote笔记法使用范例(2)——指针(1)智能指针

    要知道什么是智能指针,首先了解什么称为 “资源分配即初始化” what RAII:RAII—Resource Acquisition Is Initialization,即“资源分配即初始化” 在&l ...

随机推荐

  1. lvs+keepalived集群架构服务

    一,LVS功能详解 1.1 LVS(Linux Virtual Server)介绍 LVS是Linux Virtual Server 的简写(也叫做IPVS),意即Linux虚拟服务器,是一个虚拟的服 ...

  2. Vue外卖的学习之路

    用Vue打造外卖项目 一.项目前整理思绪 (1)项目所需的技术栈 (2)项目分布 (3)整体项目文件介绍

  3. Codeforces 348 D - Turtles Lindström–Gessel–Viennot lemma

    #include<bits/stdc++.h> using namespace std; #define y1 y11 #define fi first #define se second ...

  4. MySQL进阶14--标识列(自增序列/auto_increment)--设置/展示步长--设置/删除标示列

    /*进阶14 标识列 又称为自增序列; 含义 : 可以不用手动的插入值, 系统提供默认的序列值(1-->n) 特点 : 1.标识列必须和主键搭配? 不一定,但要求是一个key 2.一个表可以有几 ...

  5. postgresql sql查询结果添加序号列与每组第一个序号应用

    1.postgresql 查询每组第一个 ROW_NUMBER () OVER (partition by 字段 ORDER BY  字段  DESC) 写法:SELECT  ROW_NUMBER ( ...

  6. bzoj3097 hash killer 1——构造题

    题意 在 $u64$ 自然溢出下,请输出一串字符串和 $L$,使得对任意 $Base$ 都能找到两个长度为 $L$ 的字串的 $Hash$ 值相同. 分析 $u64$ 自然溢出等价于两个哈希值模 $2 ...

  7. PHP+下载文件夹

    php下载文件我整理了这三种方法,和大家分享一下: 第一种:直接添加文件下载的绝对路径连接 //如:我有一个文件在demo.xx.cn/demo.zip<button>    <a ...

  8. Java 中的 SimpleDateFormat 【 parse 和 format 】【转换时间格式】

    在 Java 里面有很多特别方便的函数(尽管术语可能不这么说)可以供我们使用,让一些本来要写好长好多的代码的事情变得仅仅几行就解决了. 在 SimpleDateFormat 中,有以下特定的规则: G ...

  9. es 修改 mapping 字段类型

    一.原索引 PUT my_index { "mappings": { "_doc": { "properties": { "cre ...

  10. Pyhton3异常处理

    例: while True:        try:            x = int(input("Please enter a number: "))            ...