Working on overlays / stats

This commit is contained in:
Brad Davis 2015-06-16 21:00:13 -07:00
parent 02d33f6086
commit 659fa2387c
12 changed files with 671 additions and 638 deletions

View file

@ -0,0 +1,147 @@
import Hifi 1.0 as Hifi
import QtQuick 2.3
import QtQuick.Controls 1.2
Hifi.Stats {
id: root
objectName: "Stats"
implicitHeight: row.height
implicitWidth: row.width
readonly property int sTATS_GENERAL_MIN_WIDTH: 165
readonly property int sTATS_PING_MIN_WIDTH: 190
readonly property int sTATS_GEO_MIN_WIDTH: 240
readonly property int sTATS_OCTREE_MIN_WIDTH: 410
onParentChanged: {
root.x = parent.width - root.width;
}
Row {
z: 100
id: row
spacing: 8
Rectangle {
width: generalCol.width + 8;
height: generalCol.height + 8;
color: "#99333333";
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
}
Column {
id: generalCol
spacing: 4; x: 4; y: 4;
width: sTATS_GENERAL_MIN_WIDTH
Text { color: "white"; text: "Servers: " + root.serverCount }
Text { color: "white"; text: "Avatars: " + root.avatarCount }
Text { color: "white"; text: "Framerate: " + root.framerate }
Text { color: "white"; text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount }
Text { color: "white"; text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2) }
}
}
Rectangle {
width: pingCol.width + 8
height: pingCol.height + 8
color: "#99333333"
visible: root.audioPing != -2
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
}
Column {
id: pingCol
spacing: 4; x: 4; y: 4;
width: sTATS_PING_MIN_WIDTH
Text { color: "white"; text: "Audio ping: " + root.audioPing }
Text { color: "white"; text: "Avatar ping: " + root.avatarPing }
Text { color: "white"; text: "Entities avg ping: " + root.entitiesPing }
Text { color: "white"; text: "Voxel max ping: " + 0; visible: root.expanded; }
}
}
Rectangle {
width: geoCol.width
height: geoCol.height
color: "#99333333"
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
}
Column {
spacing: 4
id: geoCol
width: sTATS_GEO_MIN_WIDTH
Text {
color: "white";
text: "Position: " + root.position.x.toFixed(1) + ", " +
root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1)
}
Text {
color: "white";
text: "Velocity: " + root.velocity.toFixed(1)
}
Text {
color: "white";
text: "Yaw: " + root.yaw.toFixed(1)
}
Text {
color: "white";
visible: root.expanded;
text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " +
root.avatarMixerPps + "pps";
}
Text {
color: "white";
visible: root.expanded;
text: "Downloads: ";
}
}
}
Rectangle {
width: octreeCol.width + 8
height: octreeCol.height + 8
color: "#99333333"
MouseArea {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
}
Column {
id: octreeCol
spacing: 4; x: 4; y: 4;
width: sTATS_OCTREE_MIN_WIDTH
Text {
color: "white";
text: "Triangles: " + root.triangles +
" / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches
}
Text {
color: "white";
visible: root.expanded;
text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque +
" / Translucent: " + root.meshTranslucent;
}
Text {
color: "white";
visible: root.expanded;
text: "\tOpaque considered: " + root.opaqueConsidered +
" / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall;
}
Text {
color: "white";
visible: !root.expanded
text: "Octree Elements Server: ";
}
}
}
}
Connections {
target: root.parent
onWidthChanged: {
root.x = root.parent.width - root.width;
}
}
}

View file

@ -0,0 +1,29 @@
import Hifi 1.0 as Hifi
import QtQuick 2.3 as Original
import "controls"
import "styles"
Hifi.Tooltip {
id: root
HifiConstants { id: hifi }
x: lastMousePosition.x
y: lastMousePosition.y
implicitWidth: border.implicitWidth
implicitHeight: border.implicitHeight
Border {
id: border
anchors.fill: parent
implicitWidth: text.implicitWidth
implicitHeight: Math.max(text.implicitHeight, 64)
Text {
id: text
anchors.fill: parent
anchors.margins: 16
font.pixelSize: hifi.fonts.pixelSize / 2
text: root.text
wrapMode: Original.Text.WordWrap
}
}
}

View file

@ -88,6 +88,7 @@
#include <SettingHandle.h>
#include <SoundCache.h>
#include <TextRenderer.h>
#include <Tooltip.h>
#include <UserActivityLogger.h>
#include <UUID.h>
#include <VrMenu.h>
@ -822,6 +823,7 @@ void Application::initializeUi() {
LoginDialog::registerType();
MessageDialog::registerType();
VrMenu::registerType();
Tooltip::registerType();
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_glWidget->context()->contextHandle());

View file

@ -22,6 +22,7 @@
#include <OffscreenUi.h>
#include <CursorManager.h>
#include <PerfStat.h>
#include <Tooltip.h>
#include "AudioClient.h"
@ -41,7 +42,7 @@
static const float MAG_SPEED = 0.08f;
static const quint64 MSECS_TO_USECS = 1000ULL;
static const quint64 TOOLTIP_DELAY = 2000000ULL;
static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };
@ -59,7 +60,7 @@ static gpu::BufferPointer _hemiVertices;
static gpu::BufferPointer _hemiIndices;
static int _hemiIndexCount{ 0 };
EntityItemID ApplicationCompositor::_noItemId;
static QString _tooltipId;
// Return a point's cartesian coordinates on a sphere from pitch and yaw
glm::vec3 getPoint(float yaw, float pitch) {
@ -159,6 +160,11 @@ ApplicationCompositor::ApplicationCompositor() {
_hoverItemHref.clear();
auto cursor = Cursor::Manager::instance().getCursor();
cursor->setIcon(Cursor::Icon::DEFAULT);
if (!_tooltipId.isEmpty()) {
qDebug() << "Closing tooltip " << _tooltipId;
Tooltip::closeTip(_tooltipId);
_tooltipId.clear();
}
}
});
}
@ -166,6 +172,7 @@ ApplicationCompositor::ApplicationCompositor() {
ApplicationCompositor::~ApplicationCompositor() {
}
void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) {
auto& cursorManager = Cursor::Manager::instance();
auto cursor = cursorManager.getCursor(cursorIndex);
@ -184,17 +191,10 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
return;
}
updateTooltips();
vec2 canvasSize = qApp->getCanvasSize();
_textureAspectRatio = aspect(canvasSize);
if (_hoverItemId != _noItemId) {
quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;
if (!_hoverItemHref.isEmpty() && hoverDuration > TOOLTIP_DELAY) {
// TODO Enable and position the tooltip
}
}
//Handle fading and deactivation/activation of UI
gpu::Batch batch;
@ -262,6 +262,11 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
return;
}
vec2 canvasSize = qApp->getCanvasSize();
_textureAspectRatio = aspect(canvasSize);
updateTooltips();
renderArgs->_context->syncCache();
auto geometryCache = DependencyManager::get<GeometryCache>();
@ -392,13 +397,6 @@ bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& positi
//Renders optional pointers
void ApplicationCompositor::renderPointers(gpu::Batch& batch) {
//glEnable(GL_TEXTURE_2D);
//glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glActiveTexture(GL_TEXTURE0);
//bindCursorTexture();
if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) {
//If we are in oculus, render reticle later
if (_lastMouseMove == 0) {
@ -442,8 +440,6 @@ void ApplicationCompositor::renderPointers(gpu::Batch& batch) {
_magActive[MOUSE] = false;
renderControllerPointers(batch);
}
//glBindTexture(GL_TEXTURE_2D, 0);
//glDisable(GL_TEXTURE_2D);
}
@ -718,7 +714,6 @@ void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) {
batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount);
}
glm::vec2 ApplicationCompositor::directionToSpherical(const glm::vec3& direction) {
glm::vec2 result;
// Compute yaw
@ -788,127 +783,13 @@ glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) co
return sphericalToScreen(overlayToSpherical(overlayPos));
}
#if 0
gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() {
if (!_standardDrawPipeline) {
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)));
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag)));
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
gpu::Shader::makeProgram((*program));
auto state = gpu::StatePointer(new gpu::State());
// enable decal blend
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
_standardDrawPipeline.reset(gpu::Pipeline::create(program, state));
}
return _standardDrawPipeline;
}
// Draws the FBO texture for the screen
void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
if (_alpha == 0.0f) {
return;
}
renderArgs->_context->syncCache();
gpu::Batch batch;
Transform model;
//DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
batch.setPipeline(getDrawPipeline());
batch.setModelTransform(Transform());
batch.setProjectionTransform(mat4());
batch.setViewTransform(model);
batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
DependencyManager::get<GeometryCache>()->renderUnitQuad(batch, vec4(vec3(1), _alpha));
//draw the mouse pointer
glm::vec2 canvasSize = qApp->getCanvasSize();
// Get the mouse coordinates and convert to NDC [-1, 1]
vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY());
mousePosition /= canvasSize;
mousePosition *= 2.0f;
mousePosition -= 1.0f;
mousePosition.y *= -1.0f;
model.setTranslation(vec3(mousePosition, 0));
glm::vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
model.setScale(vec3(mouseSize, 1.0f));
batch.setModelTransform(model);
bindCursorTexture(batch);
glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
DependencyManager::get<GeometryCache>()->renderUnitQuad(batch, vec4(1));
renderArgs->_context->render(batch);
}
// Draws the FBO texture for Oculus rift.
void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) {
if (_alpha == 0.0f) {
return;
}
renderArgs->_context->syncCache();
gpu::Batch batch;
batch.setPipeline(getDrawPipeline());
batch._glDisable(GL_DEPTH_TEST);
batch._glDisable(GL_CULL_FACE);
batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
batch.setProjectionTransform(whichCamera.getProjection());
batch.setViewTransform(Transform());
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const quat& avatarOrientation = myAvatar->getOrientation();
quat hmdOrientation = qApp->getCamera()->getHmdRotation();
vec3 hmdPosition = glm::inverse(avatarOrientation) * qApp->getCamera()->getHmdPosition();
mat4 overlayXfm = glm::mat4_cast(glm::inverse(hmdOrientation)) * glm::translate(mat4(), -hmdPosition);
batch.setModelTransform(Transform(overlayXfm));
drawSphereSection(batch);
bindCursorTexture(batch);
auto geometryCache = DependencyManager::get<GeometryCache>();
vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize);
//Controller Pointers
for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
PalmData& palm = myAvatar->getHand()->getPalms()[i];
if (palm.isActive()) {
glm::vec2 polar = getPolarCoordinates(palm);
// Convert to quaternion
mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
mat4 reticleXfm = overlayXfm * pointerXfm;
reticleXfm = glm::scale(reticleXfm, reticleScale);
batch.setModelTransform(reticleXfm);
// Render reticle at location
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
void ApplicationCompositor::updateTooltips() {
if (_hoverItemId != _noItemId) {
quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;
if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemHref.isEmpty() && hoverDuration > TOOLTIP_DELAY) {
// TODO Enable and position the tooltip
_hoverItemEnterUsecs = UINT64_MAX;
_tooltipId = Tooltip::showTip("URL: " + _hoverItemHref);
}
}
//Mouse Pointer
if (_reticleActive[MOUSE]) {
glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(),
_reticlePosition[MOUSE].y()));
mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
mat4 reticleXfm = overlayXfm * pointerXfm;
reticleXfm = glm::scale(reticleXfm, reticleScale);
batch.setModelTransform(reticleXfm);
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
}
renderArgs->_context->render(batch);
}
#endif

View file

@ -74,6 +74,7 @@ private:
void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks);
void drawSphereSection(gpu::Batch& batch);
void updateTooltips();
void renderPointers(gpu::Batch& batch);
void renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder);

View file

@ -50,6 +50,8 @@ static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f;
static const float MIRROR_REARVIEW_DISTANCE = 0.722f;
static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f;
static const float MIRROR_FIELD_OF_VIEW = 30.0f;
static const float ORTHO_NEAR_CLIP = -10000;
static const float ORTHO_FAR_CLIP = 10000;
ApplicationOverlay::ApplicationOverlay() :
@ -85,6 +87,7 @@ ApplicationOverlay::~ApplicationOverlay() {
void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");
Stats::getInstance()->updateStats();
glm::vec2 size = qApp->getCanvasSize();
// TODO Handle fading and deactivation/activation of UI
@ -98,41 +101,38 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
glViewport(0, 0, size.x, size.y);
// Now render the overlay components together into a single texture
gpu::Batch batch;
auto geometryCache = DependencyManager::get<GeometryCache>();
geometryCache->useSimpleDrawPipeline(batch);
batch._glDisable(GL_DEPTH);
batch._glDisable(GL_LIGHTING);
batch._glEnable(GL_BLEND);
//renderOverlays(renderArgs);
//renderAudioMeter(renderArgs);
//renderCameraToggle(renderArgs);
//renderStatsAndLogs(renderArgs);
renderOverlays(batch, renderArgs);
renderAudioMeter(batch);
renderCameraToggle(batch);
renderStatsAndLogs(batch);
renderDomainConnectionStatusBorder(batch);
renderQmlUi(batch);
renderDomainConnectionStatusBorder(renderArgs);
renderQmlUi(renderArgs);
renderArgs->_context->syncCache();
renderArgs->_context->render(batch);
_overlayFramebuffer->release();
}
void ApplicationOverlay::renderQmlUi(gpu::Batch& batch) {
void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
if (_uiTexture) {
gpu::Batch batch;
auto geometryCache = DependencyManager::get<GeometryCache>();
geometryCache->useSimpleDrawPipeline(batch);
batch._glDisable(GL_DEPTH_TEST);
batch._glDisable(GL_LIGHTING);
batch._glEnable(GL_BLEND);
batch.setProjectionTransform(mat4());
batch.setModelTransform(mat4());
batch._glBindTexture(GL_TEXTURE_2D, _uiTexture);
auto geometryCache = DependencyManager::get<GeometryCache>();
geometryCache->renderUnitQuad(batch, glm::vec4(1));
renderArgs->_context->syncCache();
renderArgs->_context->render(batch);
}
}
void ApplicationOverlay::renderOverlays(gpu::Batch& batch, RenderArgs* renderArgs) {
static const float NEAR_CLIP = -10000;
static const float FAR_CLIP = 10000;
void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
glm::vec2 size = qApp->getCanvasSize();
mat4 legacyProjection = glm::ortho<float>(0, size.x, size.y, 0, NEAR_CLIP, FAR_CLIP);
mat4 legacyProjection = glm::ortho<float>(0, size.x, size.y, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadMatrixf(glm::value_ptr(legacyProjection));
@ -152,8 +152,7 @@ void ApplicationOverlay::renderOverlays(gpu::Batch& batch, RenderArgs* renderArg
glMatrixMode(GL_MODELVIEW);
}
void ApplicationOverlay::renderCameraToggle(gpu::Batch& batch) {
/*
void ApplicationOverlay::renderCameraToggle(RenderArgs* renderArgs) {
if (Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) {
return;
}
@ -169,11 +168,9 @@ void ApplicationOverlay::renderCameraToggle(gpu::Batch& batch) {
}
DependencyManager::get<CameraToolBox>()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed);
*/
}
void ApplicationOverlay::renderAudioMeter(gpu::Batch& batch) {
/*
void ApplicationOverlay::renderAudioMeter(RenderArgs* renderArgs) {
auto audio = DependencyManager::get<AudioClient>();
// Audio VU Meter and Mute Icon
@ -287,10 +284,9 @@ void ApplicationOverlay::renderAudioMeter(gpu::Batch& batch) {
audioLevel, AUDIO_METER_HEIGHT, quadColor,
_audioBlueQuad);
}
*/
}
void ApplicationOverlay::renderRearView(gpu::Batch& batch) {
void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
// // Grab current viewport to reset it at the end
// int viewport[4];
// glGetIntegerv(GL_VIEWPORT, viewport);
@ -369,37 +365,18 @@ void ApplicationOverlay::renderRearView(gpu::Batch& batch) {
//}
}
void ApplicationOverlay::renderStatsAndLogs(gpu::Batch& batch) {
/*
Application* application = Application::getInstance();
QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor();
NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay();
void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) {
// Display stats and log text onscreen
batch._glLineWidth(1.0f);
glPointSize(1.0f);
// Determine whether to compute timing details
bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
Menu::getInstance()->isOptionChecked(MenuOption::Stats) &&
Stats::getInstance()->isExpanded();
Menu::getInstance()->isOptionChecked(MenuOption::Stats); //&&
// Stats::getInstance()->isExpanded();
if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) {
PerformanceTimer::setActive(shouldDisplayTimingDetail);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
// let's set horizontal offset to give stats some margin to mirror
int voxelPacketsToProcess = octreePacketProcessor.packetsToProcessCount();
// Onscreen text about position, servers, etc
Stats::getInstance()->display(WHITE_TEXT, STATS_HORIZONTAL_OFFSET, application->getFps(),
bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond(),
bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond(),
bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond(),
bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond(),
voxelPacketsToProcess);
}
Stats::getInstance()->updateStats();
// Show on-screen msec timer
if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) {
@ -412,16 +389,26 @@ void ApplicationOverlay::renderStatsAndLogs(gpu::Batch& batch) {
drawText(canvasSize.x - 100, canvasSize.y - timerBottom,
0.30f, 0.0f, 0, frameTimer.toUtf8().constData(), WHITE_TEXT);
}
glPointSize(1.0f);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
NodeBounds& nodeBoundsDisplay = qApp->getNodeBoundsDisplay();
nodeBoundsDisplay.drawOverlay();
*/
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
}
void ApplicationOverlay::renderDomainConnectionStatusBorder(gpu::Batch& batch) {
void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderArgs) {
auto geometryCache = DependencyManager::get<GeometryCache>();
std::once_flag once;
std::call_once(once, [&] {
QVector<vec2> points;
static const float B = 0.99;
static const float B = 0.99f;
points.push_back(vec2(-B));
points.push_back(vec2(B, -B));
points.push_back(vec2(B));
@ -430,9 +417,13 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(gpu::Batch& batch) {
geometryCache->updateVertices(_domainStatusBorder, points, CONNECTION_STATUS_BORDER_COLOR);
});
auto nodeList = DependencyManager::get<NodeList>();
if (nodeList && !nodeList->getDomainHandler().isConnected()) {
gpu::Batch batch;
auto geometryCache = DependencyManager::get<GeometryCache>();
geometryCache->useSimpleDrawPipeline(batch);
batch._glDisable(GL_DEPTH);
batch._glDisable(GL_LIGHTING);
batch._glEnable(GL_BLEND);
batch.setProjectionTransform(mat4());
batch.setModelTransform(mat4());
batch.setUniformTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
@ -445,6 +436,8 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(gpu::Batch& batch) {
//batch.setModelTransform(glm::scale(mat4(), vec3(scaleAmount)));
geometryCache->renderVertices(batch, gpu::LINE_STRIP, _domainStatusBorder);
renderArgs->_context->syncCache();
renderArgs->_context->render(batch);
}
}

View file

@ -29,13 +29,13 @@ public:
private:
void renderAudioMeter(gpu::Batch& batch);
void renderCameraToggle(gpu::Batch& batch);
void renderStatsAndLogs(gpu::Batch& batch);
void renderDomainConnectionStatusBorder(gpu::Batch& batch);
void renderRearView(gpu::Batch& batch);
void renderQmlUi(gpu::Batch& batch);
void renderOverlays(gpu::Batch& batch, RenderArgs* renderArgs);
void renderAudioMeter(RenderArgs* renderArgs);
void renderCameraToggle(RenderArgs* renderArgs);
void renderStatsAndLogs(RenderArgs* renderArgs);
void renderDomainConnectionStatusBorder(RenderArgs* renderArgs);
void renderRearView(RenderArgs* renderArgs);
void renderQmlUi(RenderArgs* renderArgs);
void renderOverlays(RenderArgs* renderArgs);
void buildFramebufferObject();
float _alpha{ 1.0f };

View file

@ -9,9 +9,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <sstream>
#include <gpu/GPUConfig.h>
#include <stdlib.h>
#include "Stats.h"
#include <glm/glm.hpp>
#include <glm/gtx/component_wise.hpp>
@ -25,147 +25,30 @@
#include <LODManager.h>
#include <PerfStat.h>
#include "Stats.h"
#include "BandwidthRecorder.h"
#include "InterfaceConfig.h"
#include "Menu.h"
#include "Util.h"
#include "SequenceNumberStats.h"
HIFI_QML_DEF(Stats)
using namespace std;
const int STATS_PELS_PER_LINE = 16;
const int STATS_PELS_INITIALOFFSET = 12;
const int STATS_GENERAL_MIN_WIDTH = 165;
const int STATS_PING_MIN_WIDTH = 190;
const int STATS_GEO_MIN_WIDTH = 240;
const int STATS_OCTREE_MIN_WIDTH = 410;
static Stats* INSTANCE{ nullptr };
Stats* Stats::getInstance() {
static Stats stats;
return &stats;
if (!INSTANCE) {
Stats::registerType();
Stats::show();
//Stats::toggle();
Q_ASSERT(INSTANCE);
}
return INSTANCE;
}
Stats::Stats():
_expanded(false),
_recentMaxPackets(0),
_resetRecentMaxPacketsSoon(true),
_generalStatsWidth(STATS_GENERAL_MIN_WIDTH),
_pingStatsWidth(STATS_PING_MIN_WIDTH),
_geoStatsWidth(STATS_GEO_MIN_WIDTH),
_octreeStatsWidth(STATS_OCTREE_MIN_WIDTH),
_lastHorizontalOffset(0)
{
auto canvasSize = Application::getInstance()->getCanvasSize();
resetWidth(canvasSize.x, 0);
}
void Stats::toggleExpanded() {
_expanded = !_expanded;
}
// called on mouse click release
// check for clicks over stats in order to expand or contract them
void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset) {
auto canvasSize = Application::getInstance()->getCanvasSize();
if (0 != glm::compMax(glm::abs(glm::ivec2(mouseX - mouseDragStartedX, mouseY - mouseDragStartedY)))) {
// not worried about dragging on stats
return;
}
int statsHeight = 0,
statsWidth = 0,
statsX = 0,
statsY = 0,
lines = 0;
statsX = horizontalOffset;
// top-left stats click
lines = _expanded ? 5 : 3;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
if (mouseX > statsX && mouseX < statsX + _generalStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
toggleExpanded();
return;
}
statsX += _generalStatsWidth;
// ping stats click
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
lines = _expanded ? 4 : 3;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
if (mouseX > statsX && mouseX < statsX + _pingStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
toggleExpanded();
return;
}
statsX += _pingStatsWidth;
}
// geo stats panel click
lines = _expanded ? 4 : 3;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
if (mouseX > statsX && mouseX < statsX + _geoStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
toggleExpanded();
return;
}
statsX += _geoStatsWidth;
// top-right stats click
lines = _expanded ? 11 : 3;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
statsWidth = canvasSize.x - statsX;
if (mouseX > statsX && mouseX < statsX + statsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
toggleExpanded();
return;
}
}
void Stats::resetWidth(int width, int horizontalOffset) {
auto canvasSize = Application::getInstance()->getCanvasSize();
int extraSpace = canvasSize.x - horizontalOffset - 2
- STATS_GENERAL_MIN_WIDTH
- (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0)
- STATS_GEO_MIN_WIDTH
- STATS_OCTREE_MIN_WIDTH;
int panels = 4;
_generalStatsWidth = STATS_GENERAL_MIN_WIDTH;
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
_pingStatsWidth = STATS_PING_MIN_WIDTH;
} else {
_pingStatsWidth = 0;
panels = 3;
}
_geoStatsWidth = STATS_GEO_MIN_WIDTH;
_octreeStatsWidth = STATS_OCTREE_MIN_WIDTH;
if (extraSpace > panels) {
_generalStatsWidth += (int) extraSpace / panels;
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
_pingStatsWidth += (int) extraSpace / panels;
}
_geoStatsWidth += (int) extraSpace / panels;
_octreeStatsWidth += canvasSize.x -
(_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3);
}
}
// translucent background box that makes stats more readable
void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int height) {
glm::vec4 color(((rgba >> 24) & 0xff) / 255.0f,
((rgba >> 16) & 0xff) / 255.0f,
((rgba >> 8) & 0xff) / 255.0f,
(rgba & 0xff) / 255.0f);
// FIX ME: is this correct? It seems to work to fix textures bleeding into us...
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
DependencyManager::get<GeometryCache>()->renderQuad(x, y, width, height, color);
Stats::Stats(QQuickItem* parent) : QQuickItem(parent) {
INSTANCE = this;
}
bool Stats::includeTimingRecord(const QString& name) {
@ -190,161 +73,63 @@ bool Stats::includeTimingRecord(const QString& name) {
return false;
}
// display expanded or contracted stats
void Stats::display(
const float* color,
int horizontalOffset,
float fps,
int inPacketsPerSecond,
int outPacketsPerSecond,
int inKbitsPerSecond,
int outKbitsPerSecond,
int voxelPacketsToProcess)
{
auto canvasSize = Application::getInstance()->getCanvasSize();
unsigned int backgroundColor = 0x33333399;
int verticalOffset = 0, lines = 0;
float scale = 0.10f;
float rotation = 0.0f;
int font = 2;
QLocale locale(QLocale::English);
std::stringstream octreeStats;
QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
if (_lastHorizontalOffset != horizontalOffset) {
resetWidth(canvasSize.x, horizontalOffset);
_lastHorizontalOffset = horizontalOffset;
#define STAT_UPDATE(name, src) \
{ \
auto val = src; \
if (_##name != val) { \
_##name = val; \
emit name##Changed(); \
} \
}
glPointSize(1.0f);
#define STAT_UPDATE_FLOAT(name, src, epsilon) \
{ \
float val = src; \
if (abs(_##name - val) >= epsilon) { \
_##name = val; \
emit name##Changed(); \
} \
}
void Stats::updateStats() {
if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
if (isVisible()) {
setVisible(false);
}
return;
} else {
if (!isVisible()) {
setVisible(true);
}
}
auto nodeList = DependencyManager::get<NodeList>();
auto avatarManager = DependencyManager::get<AvatarManager>();
// we need to take one avatar out so we don't include ourselves
int totalAvatars = DependencyManager::get<AvatarManager>()->size() - 1;
int totalServers = DependencyManager::get<NodeList>()->size();
STAT_UPDATE(avatarCount, avatarManager->size() - 1);
STAT_UPDATE(serverCount, nodeList->size());
STAT_UPDATE(framerate, (int)qApp->getFps());
lines = 5;
int columnOneWidth = _generalStatsWidth;
bool performanceTimerIsActive = PerformanceTimer::isActive();
bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);
if (displayPerf && performanceTimerIsActive) {
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide...
// we will also include room for 1 line per timing record and a header of 4 lines
lines += 4;
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
int statsLines = 0;
while (i.hasNext()) {
i.next();
if (includeTimingRecord(i.key())) {
lines++;
statsLines++;
if (onlyDisplayTopTen && statsLines == 10) {
break;
}
}
}
}
drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, (lines + 1) * STATS_PELS_PER_LINE);
horizontalOffset += 5;
int columnOneHorizontalOffset = horizontalOffset;
QString serverNodes = QString("Servers: %1").arg(totalServers);
QString avatarNodes = QString("Avatars: %1").arg(totalAvatars);
QString framesPerSecond = QString("Framerate: %1 FPS").arg(fps, 3, 'f', 0);
verticalOffset = STATS_PELS_INITIALOFFSET; // first one is offset by less than a line
drawText(horizontalOffset, verticalOffset, scale, rotation, font, serverNodes.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, framesPerSecond.toUtf8().constData(), color);
QString packetsPerSecondString = QString("Packets In/Out: %1/%2").arg(inPacketsPerSecond).arg(outPacketsPerSecond);
QString averageMegabitsPerSecond = QString("Mbps In/Out: %1/%2").
arg((float)inKbitsPerSecond * 1.0f / 1000.0f).
arg((float)outKbitsPerSecond * 1.0f / 1000.0f);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond.toUtf8().constData(), color);
// TODO: the display of these timing details should all be moved to JavaScript
if (displayPerf && performanceTimerIsActive) {
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
// Timing details...
verticalOffset += STATS_PELS_PER_LINE * 4; // skip 3 lines to be under the other columns
drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font,
"-------------------------------------------------------- Function "
"------------------------------------------------------- --msecs- -calls--", color);
// First iterate all the records, and for the ones that should be included, insert them into
// a new Map sorted by average time...
QMap<float, QString> sortedRecords;
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
while (i.hasNext()) {
i.next();
if (includeTimingRecord(i.key())) {
float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC;
sortedRecords.insertMulti(averageTime, i.key());
}
}
int linesDisplayed = 0;
QMapIterator<float, QString> j(sortedRecords);
j.toBack();
while (j.hasPrevious()) {
j.previous();
QChar noBreakingSpace = QChar::Nbsp;
QString functionName = j.value();
const PerformanceTimerRecord& record = allRecords.value(functionName);
QString perfLine = QString("%1: %2 [%3]").
arg(QString(qPrintable(functionName)), 120, noBreakingSpace).
arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace).
arg((int)record.getCount(), 6, 10, noBreakingSpace);
verticalOffset += STATS_PELS_PER_LINE;
drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine.toUtf8().constData(), color);
linesDisplayed++;
if (onlyDisplayTopTen && linesDisplayed == 10) {
break;
}
}
}
verticalOffset = STATS_PELS_INITIALOFFSET;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + 1;
auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
STAT_UPDATE(packetOutCount, bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond());
STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f);
STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f);
// Second column: ping
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
int pingAudio = -1, pingAvatar = -1, pingVoxel = -1, pingOctreeMax = -1;
auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1);
STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1);
pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : -1;
pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : -1;
// Now handle voxel servers, since there could be more than one, we average their ping times
//// Now handle voxel servers, since there could be more than one, we average their ping times
unsigned long totalPingOctree = 0;
int octreeServerCount = 0;
nodeList->eachNode([&totalPingOctree, &pingOctreeMax, &octreeServerCount](const SharedNodePointer& node){
int pingOctreeMax = 0;
int pingVoxel;
nodeList->eachNode([&](const SharedNodePointer& node) {
// TODO: this should also support entities
if (node->getType() == NodeType::EntityServer) {
totalPingOctree += node->getPingMs();
@ -359,146 +144,101 @@ void Stats::display(
pingVoxel = totalPingOctree / octreeServerCount;
}
lines = _expanded ? 4 : 3;
// only draw our background if column one didn't draw a wide background
if (columnOneWidth == _generalStatsWidth) {
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, (lines + 1) * STATS_PELS_PER_LINE);
}
horizontalOffset += 5;
QString audioPing;
if (pingAudio >= 0) {
audioPing = QString("Audio ping: %1").arg(pingAudio);
} else {
audioPing = QString("Audio ping: --");
}
QString avatarPing;
if (pingAvatar >= 0) {
avatarPing = QString("Avatar ping: %1").arg(pingAvatar);
} else {
avatarPing = QString("Avatar ping: --");
}
QString voxelAvgPing;
if (pingVoxel >= 0) {
voxelAvgPing = QString("Entities avg ping: %1").arg(pingVoxel);
} else {
voxelAvgPing = QString("Entities avg ping: --");
}
drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioPing.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPing.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelAvgPing.toUtf8().constData(), color);
if (_expanded) {
QString voxelMaxPing;
if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid.
voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax);
} else {
voxelMaxPing = QString("Voxel max ping: --");
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing.toUtf8().constData(), color);
}
verticalOffset = STATS_PELS_INITIALOFFSET;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2;
STAT_UPDATE(entitiesPing, pingVoxel);
//if (_expanded) {
// QString voxelMaxPing;
// if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid.
// voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax);
// } else {
// voxelMaxPing = QString("Voxel max ping: --");
// }
} else {
// -2 causes the QML to hide the ping column
STAT_UPDATE(audioPing, -2);
}
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
// Third column, avatar stats
MyAvatar* myAvatar = avatarManager->getMyAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();
lines = _expanded ? 7 : 3;
if (columnOneWidth == _generalStatsWidth) {
drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, (lines + 1) * STATS_PELS_PER_LINE);
}
horizontalOffset += 5;
QString avatarPosition = QString("Position: %1, %2, %3").
arg(avatarPos.x, -1, 'f', 1).
arg(avatarPos.y, -1, 'f', 1).
arg(avatarPos.z, -1, 'f', 1);
QString avatarVelocity = QString("Velocity: %1").arg(glm::length(myAvatar->getVelocity()), -1, 'f', 1);
QString avatarBodyYaw = QString("Yaw: %1").arg(myAvatar->getBodyYaw(), -1, 'f', 1);
QString avatarMixerStats;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPosition.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarVelocity.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarBodyYaw.toUtf8().constData(), color);
STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z));
STAT_UPDATE_FLOAT(velocity, glm::length(myAvatar->getVelocity()), 0.1f);
STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f);
if (_expanded) {
SharedNodePointer avatarMixer = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AvatarMixer);
SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);
if (avatarMixer) {
avatarMixerStats = QString("Avatar Mixer: %1 kbps, %2 pps").
arg(roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))).
arg(roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) +
bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(avatarMixerKbps, roundf(
bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer) +
bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(avatarMixerPps, roundf(
bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer) +
bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer)));
} else {
avatarMixerStats = QString("No Avatar Mixer");
STAT_UPDATE(avatarMixerKbps, -1);
STAT_UPDATE(avatarMixerPps, -1);
}
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
if (audioMixerNode) {
STAT_UPDATE(audioMixerKbps, roundf(
bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerPps, roundf(
bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) +
bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
} else {
STAT_UPDATE(audioMixerKbps, -1);
STAT_UPDATE(audioMixerPps, -1);
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarMixerStats.toUtf8().constData(), color);
stringstream downloads;
downloads << "Downloads: ";
foreach (Resource* resource, ResourceCache::getLoadingRequests()) {
downloads << (int)(resource->getProgress() * 100.0f) << "% ";
}
downloads << "(" << ResourceCache::getPendingRequestCount() << " pending)";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color);
}
STAT_UPDATE(downloads, ResourceCache::getLoadingRequests().size());
STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount());
// TODO fix to match original behavior
//stringstream downloads;
//downloads << "Downloads: ";
//foreach(Resource* resource, ) {
// downloads << (int)(resource->getProgress() * 100.0f) << "% ";
//}
//downloads << "(" << << " pending)";
} // expanded avatar column
verticalOffset = STATS_PELS_INITIALOFFSET;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3;
lines = _expanded ? 10 : 3;
drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset,
(lines + 1) * STATS_PELS_PER_LINE);
horizontalOffset += 5;
// Model/Entity render details
octreeStats.str("");
octreeStats << "Triangles: " << _renderDetails._trianglesRendered
<< " / Quads:" << _renderDetails._quadsRendered
<< " / Material Switches:" << _renderDetails._materialSwitches;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
// Fourth column, octree stats
}
void Stats::setRenderDetails(const RenderDetails& details) {
//STATS_PROPERTY(int, triangles, 0)
//STATS_PROPERTY(int, quads, 0)
//STATS_PROPERTY(int, materialSwitches, 0)
//STATS_PROPERTY(int, meshOpaque, 0)
//STATS_PROPERTY(int, meshTranslucent, 0)
//STATS_PROPERTY(int, opaqueConsidered, 0)
//STATS_PROPERTY(int, opaqueOutOfView, 0)
//STATS_PROPERTY(int, opaqueTooSmall, 0)
//STATS_PROPERTY(int, translucentConsidered, 0)
//STATS_PROPERTY(int, translucentOutOfView, 0)
//STATS_PROPERTY(int, translucentTooSmall, 0)
//STATS_PROPERTY(int, octreeElementsServer, 0)
//STATS_PROPERTY(int, octreeElementsLocal, 0)
STAT_UPDATE(triangles, details._trianglesRendered);
STAT_UPDATE(quads, details._quadsRendered);
STAT_UPDATE(materialSwitches, details._materialSwitches);
if (_expanded) {
octreeStats.str("");
octreeStats << " Mesh Parts Rendered Opaque: " << _renderDetails._opaque._rendered
<< " / Translucent:" << _renderDetails._translucent._rendered;
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
octreeStats.str("");
octreeStats << " Opaque considered: " << _renderDetails._opaque._considered
<< " / Out of view:" << _renderDetails._opaque._outOfView
<< " / Too small:" << _renderDetails._opaque._tooSmall;
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
octreeStats.str("");
octreeStats << " Translucent considered: " << _renderDetails._translucent._considered
<< " / Out of view:" << _renderDetails._translucent._outOfView
<< " / Too small:" << _renderDetails._translucent._tooSmall;
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
STAT_UPDATE(meshOpaque, details._opaque._rendered);
STAT_UPDATE(meshTranslucent, details._opaque._rendered);
STAT_UPDATE(opaqueConsidered, details._opaque._considered);
STAT_UPDATE(opaqueOutOfView, details._opaque._outOfView);
STAT_UPDATE(opaqueTooSmall, details._opaque._tooSmall);
STAT_UPDATE(translucentConsidered, details._translucent._considered);
STAT_UPDATE(translucentOutOfView, details._translucent._outOfView);
STAT_UPDATE(translucentTooSmall, details._translucent._tooSmall);
}
}
/*
// display expanded or contracted stats
void Stats::display(
int voxelPacketsToProcess)
{
// iterate all the current voxel stats, and list their sending modes, and total voxel counts
std::stringstream sendingMode("");
sendingMode << "Octree Sending Mode: [";
@ -619,3 +359,81 @@ void Stats::display(
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
}
//// Performance timer
bool performanceTimerIsActive = PerformanceTimer::isActive();
bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);
if (displayPerf && performanceTimerIsActive) {
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide...
// we will also include room for 1 line per timing record and a header of 4 lines
lines += 4;
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
int statsLines = 0;
while (i.hasNext()) {
i.next();
if (includeTimingRecord(i.key())) {
lines++;
statsLines++;
if (onlyDisplayTopTen && statsLines == 10) {
break;
}
}
}
}
// TODO: the display of these timing details should all be moved to JavaScript
if (displayPerf && performanceTimerIsActive) {
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
// Timing details...
verticalOffset += STATS_PELS_PER_LINE * 4; // skip 3 lines to be under the other columns
drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font,
"-------------------------------------------------------- Function "
"------------------------------------------------------- --msecs- -calls--", color);
// First iterate all the records, and for the ones that should be included, insert them into
// a new Map sorted by average time...
QMap<float, QString> sortedRecords;
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
while (i.hasNext()) {
i.next();
if (includeTimingRecord(i.key())) {
float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC;
sortedRecords.insertMulti(averageTime, i.key());
}
}
int linesDisplayed = 0;
QMapIterator<float, QString> j(sortedRecords);
j.toBack();
while (j.hasPrevious()) {
j.previous();
QChar noBreakingSpace = QChar::Nbsp;
QString functionName = j.value();
const PerformanceTimerRecord& record = allRecords.value(functionName);
QString perfLine = QString("%1: %2 [%3]").
arg(QString(qPrintable(functionName)), 120, noBreakingSpace).
arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace).
arg((int)record.getCount(), 6, 10, noBreakingSpace);
verticalOffset += STATS_PELS_PER_LINE;
drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine.toUtf8().constData(), color);
linesDisplayed++;
if (onlyDisplayTopTen && linesDisplayed == 10) {
break;
}
}
}
*/

View file

@ -13,47 +13,113 @@
#define hifi_Stats_h
#include <QObject>
#include <QQuickItem>
#include <QVector3D>
#include <OffscreenUi.h>
#include <RenderArgs.h>
class Stats: public QObject {
#define STATS_PROPERTY(type, name, initialValue) \
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
public: \
type name() { return _##name; }; \
private: \
type _##name{ initialValue };
class Stats : public QQuickItem {
Q_OBJECT
HIFI_QML_DECL
Q_PROPERTY(bool expanded READ isExpanded NOTIFY expandedChanged)
STATS_PROPERTY(int, serverCount, 0)
STATS_PROPERTY(int, framerate, 0)
STATS_PROPERTY(int, avatarCount, 0)
STATS_PROPERTY(int, packetInCount, 0)
STATS_PROPERTY(int, packetOutCount, 0)
STATS_PROPERTY(float, mbpsIn, 0)
STATS_PROPERTY(float, mbpsOut, 0)
STATS_PROPERTY(int, audioPing, 0)
STATS_PROPERTY(int, avatarPing, 0)
STATS_PROPERTY(int, entitiesPing, 0)
STATS_PROPERTY(QVector3D, position, QVector3D(0, 0, 0) )
STATS_PROPERTY(float, velocity, 0)
STATS_PROPERTY(float, yaw, 0)
STATS_PROPERTY(int, avatarMixerKbps, 0)
STATS_PROPERTY(int, avatarMixerPps, 0)
STATS_PROPERTY(int, audioMixerKbps, 0)
STATS_PROPERTY(int, audioMixerPps, 0)
STATS_PROPERTY(int, downloads, 0)
STATS_PROPERTY(int, downloadsPending, 0)
STATS_PROPERTY(int, triangles, 0)
STATS_PROPERTY(int, quads, 0)
STATS_PROPERTY(int, materialSwitches, 0)
STATS_PROPERTY(int, meshOpaque, 0)
STATS_PROPERTY(int, meshTranslucent, 0)
STATS_PROPERTY(int, opaqueConsidered, 0)
STATS_PROPERTY(int, opaqueOutOfView, 0)
STATS_PROPERTY(int, opaqueTooSmall, 0)
STATS_PROPERTY(int, translucentConsidered, 0)
STATS_PROPERTY(int, translucentOutOfView, 0)
STATS_PROPERTY(int, translucentTooSmall, 0)
STATS_PROPERTY(int, octreeElementsServer, 0)
STATS_PROPERTY(int, octreeElementsLocal, 0)
public:
static Stats* getInstance();
Stats();
static void drawBackground(unsigned int rgba, int x, int y, int width, int height);
void toggleExpanded();
bool isExpanded() { return _expanded; }
void checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset);
void resetWidth(int width, int horizontalOffset);
void display(const float* color, int horizontalOffset, float fps, int inPacketsPerSecond, int outPacketsPerSecond,
int inKbitsPerSecond, int outKbitsPerSecond, int voxelPacketsToProcess);
Stats(QQuickItem* parent = nullptr);
bool includeTimingRecord(const QString& name);
void setRenderDetails(const RenderDetails& details) { _renderDetails = details; }
void setRenderDetails(const RenderDetails& details);
void updateStats();
bool isExpanded() { return _expanded; }
void setExpanded(bool expanded) {
if (expanded != _expanded) {
_expanded = expanded;
}
}
signals:
void expandedChanged();
void serverCountChanged();
void framerateChanged();
void avatarCountChanged();
void packetInCountChanged();
void packetOutCountChanged();
void mbpsInChanged();
void mbpsOutChanged();
void audioPingChanged();
void avatarPingChanged();
void entitiesPingChanged();
void positionChanged();
void velocityChanged();
void yawChanged();
void avatarMixerKbpsChanged();
void avatarMixerPpsChanged();
void audioMixerKbpsChanged();
void audioMixerPpsChanged();
void downloadsChanged();
void downloadsPendingChanged();
void trianglesChanged();
void quadsChanged();
void materialSwitchesChanged();
void meshOpaqueChanged();
void meshTranslucentChanged();
void opaqueConsideredChanged();
void opaqueOutOfViewChanged();
void opaqueTooSmallChanged();
void translucentConsideredChanged();
void translucentOutOfViewChanged();
void translucentTooSmallChanged();
void octreeElementsServerChanged();
void octreeElementsLocalChanged();
private:
static Stats* _sharedInstance;
bool _expanded;
int _recentMaxPackets; // recent max incoming voxel packets to process
bool _resetRecentMaxPacketsSoon;
int _generalStatsWidth;
int _bandwidthStatsWidth;
int _pingStatsWidth;
int _geoStatsWidth;
int _octreeStatsWidth;
int _lastHorizontalOffset;
RenderDetails _renderDetails;
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
bool _resetRecentMaxPacketsSoon{ true };
bool _expanded{ false };
};
#endif // hifi_Stats_h

View file

@ -91,6 +91,7 @@ void OffscreenQmlSurface::resize(const QSize& newSize) {
// Qt bug in 5.4 forces this check of pixel ratio,
// even though we're rendering offscreen.
qreal pixelRatio = 1.0;
_qmlEngine->rootContext()->setContextProperty("surfaceSize", newSize);
if (_renderControl && _renderControl->_renderWindow) {
pixelRatio = _renderControl->_renderWindow->devicePixelRatio();
} else {
@ -111,7 +112,6 @@ void OffscreenQmlSurface::resize(const QSize& newSize) {
_quickWindow->contentItem()->setSize(newSize);
}
// Update our members
if (_rootItem) {
_rootItem->setSize(newSize);
@ -376,4 +376,4 @@ void OffscreenQmlSurface::setProxyWindow(QWindow* window) {
QQuickWindow* OffscreenQmlSurface::getWindow() {
return _quickWindow;
}
}

View file

@ -0,0 +1,51 @@
//
// Tooltip.cpp
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Tooltip.h"
#include <QUuid>
HIFI_QML_DEF(Tooltip)
Tooltip::Tooltip(QQuickItem* parent) : QQuickItem(parent) {
}
Tooltip::~Tooltip() {
}
QString Tooltip::text() const {
return _text;
}
void Tooltip::setText(const QString& arg) {
if (arg != _text) {
_text = arg;
emit textChanged();
}
}
void Tooltip::setVisible(bool visible) {
QQuickItem::setVisible(visible);
}
QString Tooltip::showTip(const QString& text) {
const QString newTipId = QUuid().createUuid().toString();
Tooltip::show([&](QQmlContext*, QObject* object) {
object->setObjectName(newTipId);
object->setProperty("text", text);
});
return newTipId;
}
void Tooltip::closeTip(const QString& tipId) {
auto rootItem = DependencyManager::get<OffscreenUi>()->getRootItem();
QQuickItem* that = rootItem->findChild<QQuickItem*>(tipId);
if (that) {
that->deleteLater();
}
}

View file

@ -0,0 +1,45 @@
//
// Tooltip.h
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_Tooltip_h
#define hifi_Tooltip_h
#include "OffscreenQmlDialog.h"
class Tooltip : public QQuickItem
{
Q_OBJECT
HIFI_QML_DECL
private:
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
Tooltip(QQuickItem* parent = 0);
virtual ~Tooltip();
QString text() const;
static QString showTip(const QString& text);
static void closeTip(const QString& tipId);
public slots:
virtual void setVisible(bool v);
void setText(const QString& arg);
signals:
void textChanged();
private:
QString _text;
};
#endif // hifi_Tooltip_h