mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-05 13:31:38 +02:00
249 lines
8.4 KiB
C++
249 lines
8.4 KiB
C++
//
|
|
// BandwidthMeter.h
|
|
// interface
|
|
//
|
|
// Created by Tobias Schwinger on 6/20/13.
|
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
|
//
|
|
|
|
#include "BandwidthMeter.h"
|
|
#include "InterfaceConfig.h"
|
|
|
|
#include "Util.h"
|
|
|
|
namespace { // .cpp-local
|
|
|
|
int const AREA_WIDTH = -280; // Width of the area used. Aligned to the right when negative.
|
|
int const AREA_HEIGHT = -40; // Height of the area used. Aligned to the bottom when negative.
|
|
int const BORDER_DISTANCE_HORIZ = -10; // Distance to edge of screen (use negative value when width is negative).
|
|
int const BORDER_DISTANCE_VERT = -15; // Distance to edge of screen (use negative value when height is negative).
|
|
|
|
int SPACING_VERT_BARS = 2; // Vertical distance between input and output bar
|
|
int SPACING_RIGHT_CAPTION_IN_OUT = 4; // IN/OUT <--> |######## : |
|
|
int SPACING_LEFT_CAPTION_UNIT = 4; // |######## : | <--> UNIT
|
|
int PADDING_HORIZ_VALUE = 2; // |<-->X.XX<:-># |
|
|
|
|
unsigned const COLOR_TEXT = 0xedededff; // ^ ^ ^ ^ ^ ^
|
|
unsigned const COLOR_FRAME = 0xe0e0e0b0; // | | |
|
|
unsigned const COLOR_INDICATOR = 0xc0c0c0b0; // |
|
|
|
|
char const* CAPTION_IN = "In";
|
|
char const* CAPTION_OUT = "Out";
|
|
char const* CAPTION_UNIT = "Mbps";
|
|
|
|
double const UNIT_SCALE = 8000.0 / (1024.0 * 1024.0); // Bytes/ms -> Mbps
|
|
int const INITIAL_SCALE_MAXIMUM_INDEX = 250; // / 9: exponent, % 9: mantissa - 2, 0 o--o 2 * 10^-10
|
|
int const MIN_METER_SCALE = 10; // 10Mbps
|
|
int const NUMBER_OF_MARKERS = 10;
|
|
}
|
|
|
|
BandwidthMeter::ChannelInfo BandwidthMeter::_CHANNELS[] = {
|
|
{ "Audio" , "Kbps", 8000.0 / 1024.0, 0x33cc99ff },
|
|
{ "Avatars" , "Kbps", 8000.0 / 1024.0, 0xffef40c0 },
|
|
{ "Voxels" , "Kbps", 8000.0 / 1024.0, 0xd0d0d0a0 }
|
|
};
|
|
|
|
BandwidthMeter::BandwidthMeter() :
|
|
_textRenderer(INCONSOLATA_FONT_FAMILY, -1, QFont::Bold, false),
|
|
_scaleMaxIndex(INITIAL_SCALE_MAXIMUM_INDEX) {
|
|
|
|
_channels = static_cast<ChannelInfo*>( malloc(sizeof(_CHANNELS)) );
|
|
memcpy(_channels, _CHANNELS, sizeof(_CHANNELS));
|
|
}
|
|
|
|
BandwidthMeter::~BandwidthMeter() {
|
|
|
|
free(_channels);
|
|
}
|
|
|
|
BandwidthMeter::Stream::Stream(float msToAverage) :
|
|
_value(0.0f),
|
|
_msToAverage(msToAverage) {
|
|
|
|
gettimeofday(& _prevTime, NULL);
|
|
}
|
|
|
|
void BandwidthMeter::Stream::updateValue(double amount) {
|
|
|
|
// Determine elapsed time
|
|
timeval now;
|
|
gettimeofday(& now, NULL);
|
|
double dt = diffclock(& _prevTime, & now);
|
|
|
|
// Ignore this value when timer imprecision yields dt = 0
|
|
if (dt == 0.0) {
|
|
return;
|
|
}
|
|
|
|
memcpy(& _prevTime, & now, sizeof(timeval));
|
|
|
|
// Compute approximate average
|
|
_value = glm::mix(_value, amount / dt,
|
|
glm::clamp(dt / _msToAverage, 0.0, 1.0));
|
|
}
|
|
|
|
void BandwidthMeter::setColorRGBA(unsigned c) {
|
|
|
|
glColor4ub(GLubyte( c >> 24),
|
|
GLubyte((c >> 16) & 0xff),
|
|
GLubyte((c >> 8) & 0xff),
|
|
GLubyte( c & 0xff));
|
|
}
|
|
|
|
void BandwidthMeter::renderBox(int x, int y, int w, int h) {
|
|
|
|
glBegin(GL_QUADS);
|
|
glVertex2i(x, y);
|
|
glVertex2i(x + w, y);
|
|
glVertex2i(x + w, y + h);
|
|
glVertex2i(x, y + h);
|
|
glEnd();
|
|
}
|
|
|
|
void BandwidthMeter::renderVerticalLine(int x, int y, int h) {
|
|
|
|
glBegin(GL_LINES);
|
|
glVertex2i(x, y);
|
|
glVertex2i(x, y + h);
|
|
glEnd();
|
|
}
|
|
|
|
inline int BandwidthMeter::centered(int subject, int object) {
|
|
return (object - subject) / 2;
|
|
}
|
|
|
|
bool BandwidthMeter::isWithinArea(int x, int y, int screenWidth, int screenHeight) {
|
|
|
|
int minX = BORDER_DISTANCE_HORIZ + (AREA_WIDTH >= 0 ? 0 : screenWidth + AREA_WIDTH);
|
|
int minY = BORDER_DISTANCE_VERT + (AREA_HEIGHT >= 0 ? 0 : screenHeight + AREA_HEIGHT);
|
|
|
|
return x >= minX && x < minX + glm::abs(AREA_WIDTH) &&
|
|
y >= minY && y < minY + glm::abs(AREA_HEIGHT);
|
|
}
|
|
|
|
void BandwidthMeter::render(int screenWidth, int screenHeight) {
|
|
|
|
int x = BORDER_DISTANCE_HORIZ + (AREA_WIDTH >= 0 ? 0 : screenWidth + AREA_WIDTH);
|
|
int y = BORDER_DISTANCE_VERT + (AREA_HEIGHT >= 0 ? 0 : screenHeight + AREA_HEIGHT);
|
|
int w = glm::abs(AREA_WIDTH), h = glm::abs(AREA_HEIGHT);
|
|
|
|
// Determine total
|
|
float totalIn = 0.0f, totalOut = 0.0f;
|
|
for (int i = 0; i < N_CHANNELS; ++i) {
|
|
|
|
totalIn += inputStream(ChannelIndex(i)).getValue();
|
|
totalOut += outputStream(ChannelIndex(i)).getValue();
|
|
}
|
|
totalIn *= UNIT_SCALE;
|
|
totalOut *= UNIT_SCALE;
|
|
float totalMax = glm::max(totalIn, totalOut);
|
|
|
|
// Get font / caption metrics
|
|
QFontMetrics const& fontMetrics = _textRenderer.metrics();
|
|
int fontDescent = fontMetrics.descent();
|
|
int labelWidthIn = fontMetrics.width(CAPTION_IN);
|
|
int labelWidthOut = fontMetrics.width(CAPTION_OUT);
|
|
int labelWidthInOut = glm::max(labelWidthIn, labelWidthOut);
|
|
int labelHeight = fontMetrics.ascent() + fontDescent;
|
|
int labelWidthUnit = fontMetrics.width(CAPTION_UNIT);
|
|
int labelsWidth = labelWidthInOut + SPACING_RIGHT_CAPTION_IN_OUT + SPACING_LEFT_CAPTION_UNIT + labelWidthUnit;
|
|
|
|
// Calculate coordinates and dimensions
|
|
int barX = x + labelWidthInOut + SPACING_RIGHT_CAPTION_IN_OUT;
|
|
int barWidth = w - labelsWidth;
|
|
int barHeight = (h - SPACING_VERT_BARS) / 2;
|
|
int textYcenteredLine = h - centered(labelHeight, h) - fontDescent;
|
|
int textYupperLine = barHeight - centered(labelHeight, barHeight) - fontDescent;
|
|
int textYlowerLine = h - centered(labelHeight, barHeight) - fontDescent;
|
|
|
|
// Center of coordinate system -> upper left of bar
|
|
glPushMatrix();
|
|
glTranslatef(float(barX), float(y), 0.0f);
|
|
|
|
// Render captions
|
|
setColorRGBA(COLOR_TEXT);
|
|
_textRenderer.draw(barWidth + SPACING_LEFT_CAPTION_UNIT, textYcenteredLine, CAPTION_UNIT);
|
|
_textRenderer.draw(-labelWidthIn - SPACING_RIGHT_CAPTION_IN_OUT, textYupperLine, CAPTION_IN);
|
|
_textRenderer.draw(-labelWidthOut - SPACING_RIGHT_CAPTION_IN_OUT, textYlowerLine, CAPTION_OUT);
|
|
|
|
// Render vertical lines for the frame
|
|
setColorRGBA(COLOR_FRAME);
|
|
renderVerticalLine(0, 0, h);
|
|
renderVerticalLine(barWidth, 0, h);
|
|
|
|
// Adjust scale
|
|
int steps;
|
|
double step, scaleMax;
|
|
bool commit = false;
|
|
do {
|
|
steps = (_scaleMaxIndex % 9) + 2;
|
|
step = pow(10.0, (_scaleMaxIndex / 9) - 10);
|
|
scaleMax = step * steps;
|
|
if (commit) {
|
|
// printLog("Bandwidth meter scale: %d\n", _scaleMaxIndex);
|
|
break;
|
|
}
|
|
if (totalMax < scaleMax * 0.5) {
|
|
_scaleMaxIndex = glm::max(0, _scaleMaxIndex - 1);
|
|
commit = true;
|
|
} else if (totalMax > scaleMax) {
|
|
_scaleMaxIndex += 1;
|
|
commit = true;
|
|
}
|
|
} while (commit);
|
|
|
|
step = scaleMax / NUMBER_OF_MARKERS;
|
|
if (scaleMax < MIN_METER_SCALE) {
|
|
scaleMax = MIN_METER_SCALE;
|
|
}
|
|
|
|
// Render scale indicators
|
|
setColorRGBA(COLOR_INDICATOR);
|
|
for (int j = NUMBER_OF_MARKERS; --j > 0;) {
|
|
renderVerticalLine(int(barWidth * j / NUMBER_OF_MARKERS), 0, h);
|
|
}
|
|
|
|
// Render bars
|
|
int xIn = 0, xOut = 0;
|
|
for (int i = 0; i < N_CHANNELS; ++i) {
|
|
|
|
ChannelIndex chIdx = ChannelIndex(i);
|
|
int wIn = int(barWidth * inputStream(chIdx).getValue() * UNIT_SCALE / scaleMax);
|
|
int wOut = int(barWidth * outputStream(chIdx).getValue() * UNIT_SCALE / scaleMax);
|
|
|
|
setColorRGBA(channelInfo(chIdx).colorRGBA);
|
|
|
|
if (wIn > 0) {
|
|
renderBox(xIn, 0, wIn, barHeight);
|
|
}
|
|
xIn += wIn;
|
|
|
|
if (wOut > 0) {
|
|
renderBox(xOut, h - barHeight, wOut, barHeight);
|
|
}
|
|
xOut += wOut;
|
|
}
|
|
|
|
// Render numbers
|
|
char fmtBuf[8];
|
|
setColorRGBA(COLOR_TEXT);
|
|
sprintf(fmtBuf, "%0.1f", totalIn);
|
|
_textRenderer.draw(glm::max(xIn - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
|
PADDING_HORIZ_VALUE),
|
|
textYupperLine, fmtBuf);
|
|
sprintf(fmtBuf, "%0.1f", totalOut);
|
|
_textRenderer.draw(glm::max(xOut - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
|
PADDING_HORIZ_VALUE),
|
|
textYlowerLine, fmtBuf);
|
|
|
|
glPopMatrix();
|
|
|
|
// After rendering, indicate that no data has been sent/received since the last feed.
|
|
// This way, the meters fall when not continuously fed.
|
|
for (int i = 0; i < N_CHANNELS; ++i) {
|
|
inputStream(ChannelIndex(i)).updateValue(0);
|
|
outputStream(ChannelIndex(i)).updateValue(0);
|
|
}
|
|
}
|
|
|
|
|