root/OpenSceneGraph/trunk/examples/osguserstats/osguserstats.cpp @ 11900

Revision 11900, 14.9 kB (checked in by robert, 3 years ago)

From Jean-Sebastien Guay, "As promised, here is the fix for the background size. I also added another instance variable _lineHeight to clean up the code a bit more.

Also I've done the osguserstats example. I've kept the "toy example" that was in the modified osgviewer.cpp I had sent you, because they show different uses of custom stats lines (a value displayed directly, a value without bars and a value with bars and graph). I also added a function and a thread that will sleep for a given number of milliseconds and record this time in the stats. I think it clearly shows how to record the time some processing takes and add that to the stats graph, whether the processing takes place on the same thread as the viewer or on another thread.

BTW, feel free to modify the colors I've given to each user stats line... I'm not very artistic. :-)

I've also added more doc comments to the addUserStats() method in ViewerEventHandlers?, so hopefully the arguments are clear and the way to get the results you want is also clear. Maybe I went overboard, but the function makes some assumptions that may not be obvious and has many arguments, so I preferred to be explicit."

Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield
2 *
3 * This application is open source and may be redistributed and/or modified
4 * freely and without restriction, both in commericial and non commericial applications,
5 * as long as this copyright notice is maintained.
6 *
7 * This application is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10*/
11
12#include <osgDB/ReadFile>
13#include <osgUtil/Optimizer>
14
15#include <osgViewer/Viewer>
16#include <osgViewer/ViewerEventHandlers>
17
18#include <osgGA/TrackballManipulator>
19#include <osgGA/StateSetManipulator>
20
21#include <iostream>
22
23
24// The idea of user stats is that you record times or values in the viewer's
25// stats, and you also tell the stats handler to watch those values each
26// frame. The stats handler can display the stats in three ways:
27//  - A numeric time beside the stat name
28//    Requires that an elapsed time be recorded in the viewer's stats for the
29//    "timeTakenName".
30//  - A bar in the top bar graph
31//    Requires that two times (relative to the viewer's start tick) be
32//    recorded in the viewer's stats for the "beginTimeName" and "endTimeName".
33//  - A line in the bottom graph
34//    Requires that an elapsed time be recorded in the viewer's stats for the
35//    "timeTakenName".
36
37
38// Anything you want to time has to use a consistent name in both the stats
39// handler and the viewer stats, so it's a good idea to use constants to make
40// sure the names are the same everywhere.
41const std::string frameNumberName    = "Custom Frame Number";
42const std::string frameTimeName      = "Custom Frame Time";
43const std::string customTimeName     = "Custom";
44const std::string operation1TimeName = "Operation1";
45const std::string operation2TimeName = "Operation2";
46const std::string otherThreadTimeName = "Thread";
47
48
49void initUserStats(osgViewer::StatsHandler* statsHandler)
50{
51    // This line displays the frame number. It's not averaged, just displayed as is.
52    statsHandler->addUserStatsLine("Frame", osg::Vec4(0.7,0.7,0.7,1), osg::Vec4(0.7,0.7,0.7,0.5),
53                                   frameNumberName, 1.0, false, false, "", "", 0.0);
54
55    // This line displays the frame time (from beginning of event to end of draw). No bars.
56    statsHandler->addUserStatsLine("MS/frame", osg::Vec4(1,0,1,1), osg::Vec4(1,0,1,0.5),
57                                   frameTimeName, 1000.0, true, false, "", "", 0.02);
58
59    // This line displays the sum of update and main camera cull times.
60    statsHandler->addUserStatsLine("Custom", osg::Vec4(1,1,1,1), osg::Vec4(1,1,1,0.5),
61                                   customTimeName + " time taken", 1000.0, true, false, customTimeName + " begin", customTimeName + " end", 0.016);
62
63    // This line displays the time taken by a function below ( doSomethingAndTimeIt() )
64    statsHandler->addUserStatsLine("Sleep1", osg::Vec4(1,0,0,1), osg::Vec4(1,0,0,0.5),
65                                   operation1TimeName + " time taken", 1000.0, true, false, operation1TimeName + " begin", operation1TimeName + " end", 0.016);
66
67    // This line displays the time taken by a function below ( doSomethingAndTimeIt() )
68    statsHandler->addUserStatsLine("Sleep2", osg::Vec4(1,0.5,0.5,1), osg::Vec4(1,0.5,0.5,0.5),
69                                   operation2TimeName + " time taken", 1000.0, true, false, operation2TimeName + " begin", operation2TimeName + " end", 0.016);
70
71    // This line displays the time taken by a function below ( doSomethingAndTimeIt() )
72    statsHandler->addUserStatsLine("Thread", osg::Vec4(0,0.5,0,1), osg::Vec4(0,0.5,0,0.5),
73                                   otherThreadTimeName + " time taken", 1000.0, true, false, otherThreadTimeName + " begin", otherThreadTimeName + " end", 0.016);
74}
75
76
77void updateUserStats(osgViewer::Viewer& viewer)
78{
79    // Test the custom stats line by just adding up the update and cull
80    // times for the viewer main camera for the previous frame.
81    if (viewer.getViewerStats()->collectStats("update") && viewer.getCamera()->getStats()->collectStats("rendering"))
82    {
83        // First get the frame number. The code below assumes that
84        // updateUserStats() is called after advance(), so the frame number
85        // that will be returned is for the frame that has just started and is
86        // not rendered yet. The previous frame is framenumber-1, but we can't
87        // use that frame's timings because it's probably not finished
88        // rendering yet (in multithreaded viewer modes). So we'll use the
89        // timings for framenumber-2 for this demo.
90        int framenumber = viewer.getFrameStamp()->getFrameNumber();
91
92        // Get the update time and the viewer main camera's cull time. We use
93        // getAveragedAttribute() in order to get the actual time elapsed as
94        // calculated by the stats.
95        double update = 0.0, cull = 0.0;
96        viewer.getViewerStats()->getAveragedAttribute("Update traversal time taken", update);
97        viewer.getCamera()->getStats()->getAveragedAttribute("Cull traversal time taken", cull);
98
99        // Get various begin and end times, note these are not elapsed times
100        // in a frame but rather the simulation time at those moments.
101        double eventBegin = 0.0, updateBegin = 0.0, cullEnd = 0.0, drawEnd = 0.0;
102        viewer.getViewerStats()->getAttribute(framenumber-2, "Event traversal begin time", eventBegin);
103        viewer.getViewerStats()->getAttribute(framenumber-2, "Update traversal begin time", updateBegin);
104        viewer.getCamera()->getStats()->getAttribute(framenumber-2, "Cull traversal end time", cullEnd);
105        viewer.getCamera()->getStats()->getAttribute(framenumber-2, "Draw traversal end time", drawEnd);
106
107        // This line displays the frame number. It's not averaged, just displayed as is.
108        viewer.getViewerStats()->setAttribute(framenumber, frameNumberName, framenumber);
109
110        // This line displays the frame time (from beginning of event to end of draw). No bars.
111        viewer.getViewerStats()->setAttribute(framenumber-1, frameTimeName, drawEnd - eventBegin);
112
113        // This line displays the sum of update and main camera cull times.
114        viewer.getViewerStats()->setAttribute(framenumber-1, customTimeName + " time taken", update+cull);
115        // Since we give begin and end times that correspond to the begin of
116        // the update phase and the end of the cull phase, the bar in the
117        // graph will not correspond to the summed times above if something
118        // happened between update and cull (as in this demo). Also, we need
119        // to translate the updateBegin and cullEnd times by one frame since
120        // we're taking the times for framenumber-2 but using them to display
121        // in the framenumber-1 graph.
122        viewer.getViewerStats()->setAttribute(framenumber-1, customTimeName + " begin", updateBegin + (1.0/60.0));
123        viewer.getViewerStats()->setAttribute(framenumber-1, customTimeName + " end", cullEnd + (1.0/60.0));
124    }
125}
126
127
128/// Utility function you call before something you want to time, so that the
129/// recorded times will all be consistent using the viewer's time.
130void startTiming(osgViewer::Viewer& viewer, const std::string& name)
131{
132    osg::Timer_t tick = osg::Timer::instance()->tick();
133    double currentTime = osg::Timer::instance()->delta_s(viewer.getStartTick(), tick);
134    int framenumber = viewer.getFrameStamp()->getFrameNumber();
135
136    viewer.getViewerStats()->setAttribute(framenumber, name + " begin", currentTime);
137}
138
139/// Utility function you call after something you want to time, so that the
140/// recorded times will all be consistent using the viewer's time.
141void endTiming(osgViewer::Viewer& viewer, const std::string& name)
142{
143    osg::Timer_t tick = osg::Timer::instance()->tick();
144    double currentTime = osg::Timer::instance()->delta_s(viewer.getStartTick(), tick);
145    int framenumber = viewer.getFrameStamp()->getFrameNumber();
146
147    viewer.getViewerStats()->setAttribute(framenumber, name + " end", currentTime);
148
149    double begin = 0.0;
150    double elapsed = 0.0;
151    if (viewer.getViewerStats()->getAttribute(framenumber, name + " begin", begin))
152    {
153        elapsed = currentTime - begin;
154    }
155
156    viewer.getViewerStats()->setAttribute(framenumber, name + " time taken", elapsed);
157}
158
159
160/// Will just sleep for the given number of milliseconds in the same thread
161/// as the caller, recording the time taken in the viewer's stats.
162void doSomethingAndTimeIt(osgViewer::Viewer& viewer, const std::string& name, double milliseconds)
163{
164    startTiming(viewer, name);
165
166    //------------------------------------------------------------
167    // Your processing goes here.
168
169    // Do nothing for the specified number of  milliseconds, just so we can
170    // see it in the stats.
171    osg::Timer_t startTick = osg::Timer::instance()->tick();
172    while (osg::Timer::instance()->delta_m(startTick, osg::Timer::instance()->tick()) < milliseconds)
173    {
174        OpenThreads::Thread::YieldCurrentThread();
175    }
176    //------------------------------------------------------------
177
178    endTiming(viewer, name);
179}
180
181
182/// Thread that will sleep for the given number of milliseconds, recording
183/// the time taken in the viewer's stats, whenever its process() method is
184/// called.
185class UselessThread : public OpenThreads::Thread
186{
187public:
188    UselessThread(osgViewer::Viewer& viewer, double timeToRun)
189        : _viewer(viewer)
190        , _timeToRun(timeToRun)
191        , _done(false)
192        , _process(false)
193    {
194    }
195
196    void run()
197    {
198        while (!_done)
199        {
200            if (_process)
201            {
202                startTiming(_viewer, otherThreadTimeName);
203
204                //------------------------------------------------------------
205                // Your processing goes here.
206
207                // Do nothing for the specified number of  milliseconds, just so we can
208                // see it in the stats.
209                osg::Timer_t startTick = osg::Timer::instance()->tick();
210                while (osg::Timer::instance()->delta_m(startTick, osg::Timer::instance()->tick()) < _timeToRun)
211                {
212                    OpenThreads::Thread::YieldCurrentThread();
213                }
214                //------------------------------------------------------------
215
216                endTiming(_viewer, otherThreadTimeName);
217
218                _process = false;
219            }
220            else
221            {
222                OpenThreads::Thread::microSleep(50);
223            }
224        }
225    }
226
227    int cancel()
228    {
229        _done = true;
230        return OpenThreads::Thread::cancel();
231    }
232
233    void process()
234    {
235        _process = true;
236    }
237
238protected:
239    osgViewer::Viewer& _viewer;
240    double _timeToRun;
241    bool _done;
242    bool _process;
243};
244
245
246int main(int argc, char** argv)
247{
248    // use an ArgumentParser object to manage the program arguments.
249    osg::ArgumentParser arguments(&argc,argv);
250
251    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
252    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models.");
253    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
254    arguments.getApplicationUsage()->addCommandLineOption("--image <filename>","Load an image and render it on a quad");
255    arguments.getApplicationUsage()->addCommandLineOption("--dem <filename>","Load an image/DEM and render it on a HeightField");
256    arguments.getApplicationUsage()->addCommandLineOption("--login <url> <username> <password>","Provide authentication information for http file access.");
257
258    osgViewer::Viewer viewer(arguments);
259
260    unsigned int helpType = 0;
261    if ((helpType = arguments.readHelpType()))
262    {
263        arguments.getApplicationUsage()->write(std::cout, helpType);
264        return 1;
265    }
266
267    // report any errors if they have occurred when parsing the program arguments.
268    if (arguments.errors())
269    {
270        arguments.writeErrorMessages(std::cout);
271        return 1;
272    }
273
274    if (arguments.argc()<=1)
275    {
276        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
277        return 1;
278    }
279
280    std::string url, username, password;
281    while(arguments.read("--login",url, username, password))
282    {
283        if (!osgDB::Registry::instance()->getAuthenticationMap())
284        {
285            osgDB::Registry::instance()->setAuthenticationMap(new osgDB::AuthenticationMap);
286            osgDB::Registry::instance()->getAuthenticationMap()->addAuthenticationDetails(
287                url,
288                new osgDB::AuthenticationDetails(username, password)
289            );
290        }
291    }
292
293    viewer.setCameraManipulator(new osgGA::TrackballManipulator);
294
295    // add the state manipulator
296    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
297
298    // add the thread model handler
299    viewer.addEventHandler(new osgViewer::ThreadingHandler);
300
301    // add the window size toggle handler
302    viewer.addEventHandler(new osgViewer::WindowSizeHandler);
303
304    // add the stats handler
305    osgViewer::StatsHandler* statsHandler = new osgViewer::StatsHandler;
306    viewer.addEventHandler(statsHandler);
307
308    initUserStats(statsHandler);
309
310    // add the help handler
311    viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
312
313    // load the data
314    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
315    if (!loadedModel)
316    {
317        std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
318        return 1;
319    }
320
321    // any option left unread are converted into errors to write out later.
322    arguments.reportRemainingOptionsAsUnrecognized();
323
324    // report any errors if they have occurred when parsing the program arguments.
325    if (arguments.errors())
326    {
327        arguments.writeErrorMessages(std::cout);
328        return 1;
329    }
330
331
332    // optimize the scene graph, remove redundant nodes and state etc.
333    osgUtil::Optimizer optimizer;
334    optimizer.optimize(loadedModel.get());
335
336    viewer.setSceneData( loadedModel.get() );
337
338    viewer.realize();
339
340    // Start up a thread that will just run for a fixed time each frame, in
341    // parallel to the frame loop.
342    UselessThread thread(viewer, 6.0);
343    thread.start();
344
345    while (!viewer.done())
346    {
347        viewer.advance();
348
349        updateUserStats(viewer);
350
351        // Eat up some time on the viewer thread before the event phase.
352        doSomethingAndTimeIt(viewer, operation1TimeName, 2.0);
353
354        // Start taking some time on the other thread.
355        thread.process();
356
357        viewer.eventTraversal();
358        viewer.updateTraversal();
359
360        // Eat up some time on the viewer thread between the update and cull
361        // phases.
362        doSomethingAndTimeIt(viewer, operation2TimeName, 3.0);
363
364        viewer.renderingTraversals();
365    }
366
367    thread.cancel();
368    while (thread.isRunning())
369    {
370        OpenThreads::Thread::YieldCurrentThread();
371    }
372
373}
Note: See TracBrowser for help on using the browser.