1 /*---------------------------------------------------------------------------*\
5 * Copyright (C) 2000-2006 by the OpenSG Forum *
9 * contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de *
11 \*---------------------------------------------------------------------------*/
12 /*---------------------------------------------------------------------------*\
15 * This library is free software; you can redistribute it and/or modify it *
16 * under the terms of the GNU Library General Public License as published *
17 * by the Free Software Foundation, version 2. *
19 * This library is distributed in the hope that it will be useful, but *
20 * WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
22 * Library General Public License for more details. *
24 * You should have received a copy of the GNU Library General Public *
25 * License along with this library; if not, write to the Free Software *
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
28 \*---------------------------------------------------------------------------*/
29 /*---------------------------------------------------------------------------*\
37 \*---------------------------------------------------------------------------*/
39 //---------------------------------------------------------------------------
41 //---------------------------------------------------------------------------
47 #include <boost/format.hpp>
48 #include <boost/assign/list_of.hpp>
50 #include "OSGConfig.h"
51 #include "OSGViewport.h"
53 #include "OSGPerfMonitorForeground.h"
54 #include "OSGPerfMonitor.h"
56 #include "OSGDefaultFont.h"
57 #include "OSGTextTXFFace.h"
58 #include "OSGTextLayoutParam.h"
59 #include "OSGTextLayoutResult.h"
60 #include "OSGTextTXFGlyph.h"
65 // Documentation for this class is emitted in the
66 // OSGPerfMonitorForegroundBase.cpp file.
67 // To modify it, please change the .fcd file (OSGPerfMonitorForeground.fcd) and
68 // regenerate the base file.
70 /***************************************************************************\
72 \***************************************************************************/
74 /***************************************************************************\
76 \***************************************************************************/
78 void PerfMonitorForeground::initMethod(InitPhase ePhase
)
80 Inherited::initMethod(ePhase
);
83 if(ePhase == TypeObject::SystemPost)
90 /***************************************************************************\
92 \***************************************************************************/
94 /*-------------------------------------------------------------------------*\
96 \*-------------------------------------------------------------------------*/
98 /*----------------------- constructors & destructors ----------------------*/
100 PerfMonitorForeground::PerfMonitorForeground(void) :
101 Inherited(), _face(0), _texchunk(NULL
), _texenvchunk(NULL
), mode_names()
103 _texenvchunk
= TextureEnvChunk::create();
104 _texenvchunk
->setEnvMode(GL_MODULATE
);
107 PerfMonitorForeground::PerfMonitorForeground(const PerfMonitorForeground
&source
) :
109 _face (source
._face
),
110 _texchunk (source
._texchunk
),
111 _texenvchunk(source
._texenvchunk
),
112 mode_names (source
.mode_names
)
116 PerfMonitorForeground::~PerfMonitorForeground(void)
120 /*----------------------------- class specific ----------------------------*/
122 void PerfMonitorForeground::cycleMode(int increment
)
124 int next_mode(getMode());
125 next_mode
+= increment
;
126 if (next_mode
>= PerfMonitorForeground::MODE_END
)
132 next_mode
= PerfMonitorForeground::MODE_END
- 1;
135 setMode(static_cast<UInt32
>(next_mode
));
139 void PerfMonitorForeground::changed(ConstFieldMaskArg whichField
,
143 Inherited::changed(whichField
, origin
, details
);
146 void PerfMonitorForeground::dump( UInt32
,
147 const BitVector
) const
149 SLOG
<< "Dump PerfMonitorForeground NI" << std::endl
;
152 /*! Initialize the text used. We may use the default compiled into the library.
154 void PerfMonitorForeground::initText(const std::string
&family
, Real32 size
)
164 param
.size
= static_cast<UInt32
>(size
);
165 _face
= TextTXFFace::create(family
, TextFace::STYLE_PLAIN
, param
);
168 _texchunk
= TextureObjChunk::create();
169 ImageUnrecPtr texture
= _face
->getTexture();
170 _texchunk
->setImage(texture
);
171 _texchunk
->setWrapS(GL_CLAMP
);
172 _texchunk
->setWrapT(GL_CLAMP
);
173 _texchunk
->setMinFilter(GL_NEAREST
);
174 _texchunk
->setMagFilter(GL_NEAREST
);
178 // We failed to create the font - fallback to the default font
181 _face
= DefaultFont::the()->getFace();
182 _texchunk
= DefaultFont::the()->getTexture();
185 // Increment reference counters
189 // Init the text for the chart
190 mode_names
= boost::assign::map_list_of
191 (PerfMonitorForeground::Text
, "Text")
192 (PerfMonitorForeground::PercentLines
, "PercentLines")
193 (PerfMonitorForeground::PercentTotalLines
, "PercentTotalLines")
194 (PerfMonitorForeground::MaxLines
, "MaxLines")
195 (PerfMonitorForeground::ThreadTiming
, "ThreadTiming");
199 /** Draw the foreground on the viewport. */
200 void PerfMonitorForeground::draw(DrawEnv
*pEnv
)
202 // -- Check all early exit and lazy initialization code -- //
203 // If we aren't active, then exit immediately
211 // Find the dimensions of the viewport and any other params needed for rendering
212 Real32 vpWidth = Real32(pPort->getPixelWidth());
213 Real32 vpHeight = Real32(pPort->getPixelHeight());
215 // If we have an empty or NULL vp, then don't do anything
216 if(vpWidth < 1 || vpHeight < 1)
218 Real32 vpAspectRatio = vpWidth/vpHeight;
220 // Lazy init of the font face
223 initText(getFamily(), 10); //getSize());
226 // --- BUILD UP TEXT AND DATA TO DRAW --- //
227 PerfMonitorBase::sample_pair_vector_t samples = PerfMonitor::the()->getFlatSampleTree();
228 unsigned num_samples(samples.size());
229 PerfMonitorForeground::Mode render_mode = PerfMonitorForeground::Mode(getMode());
231 std::vector<std::string> output_lines; // This will be the list of lines of text
233 // Put some basic stats at the top of the output
234 boost::format stats_formatter("FPS: %s");
235 std::string stats_line = boost::str(stats_formatter % PerfMonitor::the()->getFrameRate(10));
236 output_lines.push_back(stats_line);
238 // compute the max length of the prefix strings (" sample_name")
239 unsigned indent_size(2);
240 unsigned max_len (0);
241 for (unsigned i=0; i<num_samples;++i)
243 const unsigned cur_len((samples[i].mDepth*indent_size) + samples[i].mSample->mName.size());
244 if(cur_len > max_len)
245 { max_len = cur_len; }
248 // Now create the lines of text
249 OSG_ASSERT(mode_names.count(render_mode) == 1);
250 std::string mode_name = mode_names[render_mode];
251 std::string header_string = boost::str(
252 boost::format("%s%s %=10s %=10s %=7s") % mode_name
253 % std::string(max_len-mode_name.size(), ' ')
254 % "Avg" % "Max" % "%");
255 boost::format formatter("%s [%8.5f] [%8.5f] [%5.3f]"); // Formatter for each line
257 output_lines.push_back(header_string);
258 for (unsigned i=0; i<num_samples; ++i)
260 unsigned sample_depth = samples[i].mDepth;
261 NestedSampleInfoPtr sample = samples[i].mSample;
263 // Create a correctly spaced "prefix" string with indent, name, and filler
264 std::string indent_str(sample_depth*2, ' ');
265 std::string name_string(indent_str + sample->mName);
266 std::string filler_string(max_len - name_string.size(), ' ');
267 name_string += filler_string;
268 std::string str_out = boost::str(formatter % name_string
271 % sample->mPercentage);
272 output_lines.push_back(str_out);
275 // --- Create chart data to render -- //
276 // - We allocate all buffers but only fill them if we are in that mode.
277 // Chart data vectors that is used when rendering normalized data (percentages, etc)
278 // Note: all buffers *must* have max_samples entries to be valid
279 std::vector< std::vector<float> > normalized_chart_data;
280 unsigned max_samples = PerfMonitorBase::max_samples;
282 if (PerfMonitorForeground::PercentLines == render_mode)
284 for(unsigned i=0;i<num_samples;++i)
286 std::vector<float> percent_samples = samples[i].mSample->getPercentageSamples();
287 percent_samples.resize(max_samples, 0.0f);
288 normalized_chart_data.push_back(percent_samples);
291 // Create new data that has percentage of each sample out of total time.
292 else if (PerfMonitorForeground::PercentTotalLines == render_mode)
294 std::vector<float> total_values = samples[0].mSample->getSamples();
295 total_values.resize(max_samples, 0.0f);
297 for(unsigned i=0;i<num_samples;++i)
299 std::vector<float> values = samples[i].mSample->getSamples();
300 values.resize(max_samples, 0.0f);
301 for (unsigned j=0;j<max_samples;j++)
303 float total_value(total_values[j]);
304 if (total_value == 0.0)
307 { values[j] = values[j] / total_value; }
309 normalized_chart_data.push_back(values);
312 // List of samples normalized by the max value in each set
313 else if (PerfMonitorForeground::MaxLines == render_mode)
315 for (unsigned i=0;i<num_samples;++i)
317 std::vector<float> values = samples[i].mSample->getSamples();
318 float max_value=0.0f;
320 for (unsigned j=0;j<values.size();j++)
321 { max_value = OSG::osgMax(values[j], max_value); }
322 // normalize the values based on max value
323 if (max_value != 0.0f)
325 for(unsigned j=0;j<values.size();j++)
326 { values[j] = values[j]/max_value; }
328 values.resize(max_samples, 0.0f);
329 normalized_chart_data.push_back(values);
333 // --- LAYOUT THE TEXT and DATA --- //
334 // Now layout the text to draw it on the screen
335 TextLayoutParam layoutParam;
336 layoutParam.spacing = 1.1;
337 layoutParam.majorAlignment = TextLayoutParam::ALIGN_BEGIN;
338 layoutParam.minorAlignment = TextLayoutParam::ALIGN_BEGIN;
340 TextLayoutResult layoutResult;
341 _face->layout(output_lines, layoutParam, layoutResult);
343 unsigned num_lines(output_lines.size());
344 Real32 est_height(float(num_lines)*1.1f);
346 // layoutResult.textBounds is almost the number of lines and characters in size
347 // - It takes into account spacing and stuff like that
348 Real32 scale = 1.0f / _face->getScale(); // Scale to get to pixels or num pixels in size ??
349 Real32 size = _face->getParam().size; // Size of the face in pixels?
350 Real32 textBlockWidth = layoutResult.textBounds.x() * scale; // Num base font pixels in size ??
351 Real32 textBlockHeight = layoutResult.textBounds.y() * scale; // Num base font pixels in size ??
352 Real32 chartBlockWidth(0.0), chartBlockHeight(0.0);
353 if (PerfMonitorForeground::Text != render_mode)
355 chartBlockWidth = textBlockWidth * 2.0; // Hack for now, should be based on window size
356 chartBlockHeight = textBlockHeight;
358 // Panel is larger then just text, we add on size/2 spacing and two margins
359 Real32 panelWidth = textBlockWidth + chartBlockWidth + size + (getTextMargin().x() * 2.0f);
360 Real32 panelHeight = textBlockHeight + + size + (getTextMargin().y() * 2.0f);
362 // Scale the size of the virtual surfaced based on what can fit
363 float surf_mult(1.0);
364 if (panelHeight > vpHeight)
365 { surf_mult = (panelHeight/vpHeight); }
367 Real32 surfaceWidth = vpWidth * surf_mult;
368 Real32 surfaceHeight = vpHeight * surf_mult;
370 // ---- SETUP THE VIRTUAL VIEWPORT ---- //
371 // Now setup the GL state for "drawing" the text
372 // Note on ortho: We want to map one unit to one pixel on the
373 // screen. Some sources in the internet say that we should
374 // add an offset of -0.375 to prevent rounding errors. Don't
375 // know if that is true, but it seems to work.
376 glPushAttrib(GL_LIGHTING_BIT | GL_POLYGON_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
378 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
379 glDisable(GL_DEPTH_TEST);
380 glDisable(GL_COLOR_MATERIAL);
381 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
384 glMatrixMode(GL_MODELVIEW);
388 glMatrixMode(GL_PROJECTION);
392 //glOrtho(0 - 0.375, (panelHeight*vpAspectRatio) - 0.375, 0 - 0.375, panelHeight - 0.375, 0, 1);
393 glOrtho(0 - 0.375, surfaceWidth - 0.375, 0 - 0.375, surfaceHeight - 0.375, 0, 1);
395 // --- DRAW EVERYTHING --- //
396 // Let's do some simple form of layout management
397 // - just determining the place to put the "text box"
398 Real32 startX(0), startY(surfaceHeight);
399 glTranslatef(startX, startY, 0.0);
402 glColor4fv((GLfloat*)getBgColor().getValuesRGBA());
404 glVertex2f(0, -panelHeight); // LL
405 glVertex2f(panelWidth, -panelHeight); // LR
406 glVertex2f(panelWidth, 0); // UR
407 glVertex2f(0, 0); // UL
411 if(getBorderColor().alpha() >= 0.0f)
413 glColor4fv((GLfloat*)getBorderColor().getValuesRGBA());
414 glBegin(GL_LINE_LOOP);
415 Real32 left(getBorderOffset().x()), bottom(-panelHeight+1+getBorderOffset().y()),
416 right(panelWidth -1 -getBorderOffset().x()), top(-1 - getBorderOffset().y());
417 glVertex2f(left, bottom); // LL
418 glVertex2f(right, bottom); // LR
419 glVertex2f(right, top); // UR
420 glVertex2f(left, top); // UL
424 // Create some colors for the chart bars (alternative color for better visibility)
425 std::vector<OSG::Color4f> chart_colors;
426 OSG::Color4f bg_color = getBgColor();
427 OSG::Color4f chart_color1 = OSG::Color4f(1.0,1.0,1.0,0.0);
428 OSG::Color4f chart_color2 = (bg_color * 0.2f);
429 chart_color2[3] = 0.4;
430 chart_colors.push_back(chart_color1);
431 chart_colors.push_back(chart_color2);
432 unsigned next_base_color_idx(0);
434 // Now render chart if needed
435 if ( (PerfMonitorForeground::PercentLines == render_mode) ||
436 (PerfMonitorForeground::PercentTotalLines == render_mode) ||
437 (PerfMonitorForeground::MaxLines == render_mode) )
440 // Go to upper left of chart area
441 glTranslatef(( 0.5 * size) + getTextMargin().x() + textBlockWidth,
442 (-0.5 * size) - getTextMargin().y(), 0.0);
444 float right(chartBlockWidth);
445 float sample_height(1.1f * size); // XXX: ???
446 float value_width(chartBlockWidth/float(max_samples)); // Amount to move right for each chart value
448 // Skip the first line since it is the header and skip another to get to bottom of sample line
449 unsigned num_header_lines(2);
450 for (unsigned i=0;i<num_header_lines;i++)
451 { glTranslatef(0.0, -sample_height, 0.0); }
452 glTranslatef(0.0, -sample_height, 0.0);
454 // Alternate colors for the base color
455 for(unsigned i=0; i<num_samples;++i)
458 glColor4fv(chart_colors[next_base_color_idx].getValuesRGBA());
459 next_base_color_idx += 1;
460 if (next_base_color_idx >= chart_colors.size())
461 { next_base_color_idx = 0; }
464 glVertex2f(-textBlockWidth, 0.0); // LL
465 glVertex2f(right, 0.0); // LR
466 glVertex2f(right, sample_height); // UR
467 glVertex2f(-textBlockWidth, sample_height); // UL
471 OSG::Color3f base_color(1.0, 1.0, 0.0);
472 OSG::Color3f high_color(1.0, 0.4, 0.0);
473 OSG::Color3f cur_color;
474 std::vector<float>& norm_data(normalized_chart_data[i]);
475 OSG_ASSERT(norm_data.size() == max_samples);
477 glBegin(GL_LINE_STRIP);
478 for (unsigned x=0;x<max_samples;++x)
480 float cur_val(norm_data[x]);
481 cur_color = (base_color*(1.0-cur_val)) + (high_color*cur_val);
482 glColor3fv(cur_color.getValuesRGB());
483 glVertex2f(float(x)*value_width, norm_data[x]*size);
488 glTranslatef(0.0, -sample_height, 0.0);
494 // Now render the text
495 // - first offset by the margins into the text area
497 glTranslatef(( 0.5 * size) + getTextMargin().x(),
498 (-0.5 * size) - getTextMargin().y(), 0.0);
500 _texchunk ->activate(pEnv); // Active the texture for the text
501 _texenvchunk->activate(pEnv);
504 glColor4fv((GLfloat *) getColor().getValuesRGBA());
506 glScalef(scale, scale, 1);
507 _face->drawCharacters(layoutResult);
510 _texchunk ->deactivate(pEnv);
511 _texenvchunk->deactivate(pEnv);
514 // Go back to the initial state
515 glMatrixMode(GL_PROJECTION);
518 glMatrixMode(GL_MODELVIEW);