mirror of
https://github.com/yse/easy_profiler.git
synced 2024-12-26 16:11:02 +08:00
(GUI) Added real-time FPS Monitor which shows current max/avg frame time in GUI even if profiler is disabled. You just need to connect to the profiled app. You can close (hide) FPS Monitor and it would not send network requests to the profiled application anymore. You can increase/decrease FPS Monitor requests interval in "Settings -> FPS Monitor -> Request interval, ms". Right click on FPS Monitor window to show context menu in which you can clear contents or hide FPS Monitor.
This commit is contained in:
parent
8b7a68266c
commit
beba74d559
@ -35,6 +35,8 @@ add_executable(${PROJECT_NAME}
|
||||
descriptors_tree_widget.cpp
|
||||
easy_chronometer_item.h
|
||||
easy_chronometer_item.cpp
|
||||
easy_frame_rate_viewer.h
|
||||
easy_frame_rate_viewer.cpp
|
||||
easy_graphics_item.h
|
||||
easy_graphics_item.cpp
|
||||
easy_graphics_scrollbar.h
|
||||
|
297
profiler_gui/easy_frame_rate_viewer.cpp
Normal file
297
profiler_gui/easy_frame_rate_viewer.cpp
Normal file
@ -0,0 +1,297 @@
|
||||
/************************************************************************
|
||||
* file name : easy_frame_rate_viewer.cpp
|
||||
* ----------------- :
|
||||
* creation time : 2017/04/02
|
||||
* author : Victor Zarubkin
|
||||
* email : v.s.zarubkin@gmail.com
|
||||
* ----------------- :
|
||||
* description : This file contains implementation of EasyFrameRateViewer widget.
|
||||
* ----------------- :
|
||||
* change log : * 2017/04/02 Victor Zarubkin: Initial commit.
|
||||
* :
|
||||
* : *
|
||||
* ----------------- :
|
||||
* license : Lightweight profiler library for c++
|
||||
* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin
|
||||
* :
|
||||
* : Licensed under either of
|
||||
* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
|
||||
* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* : at your option.
|
||||
* :
|
||||
* : The MIT License
|
||||
* :
|
||||
* : Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* : of this software and associated documentation files (the "Software"), to deal
|
||||
* : in the Software without restriction, including without limitation the rights
|
||||
* : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* : of the Software, and to permit persons to whom the Software is furnished
|
||||
* : to do so, subject to the following conditions:
|
||||
* :
|
||||
* : The above copyright notice and this permission notice shall be included in all
|
||||
* : copies or substantial portions of the Software.
|
||||
* :
|
||||
* : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* : USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* :
|
||||
* : The Apache License, Version 2.0 (the "License")
|
||||
* :
|
||||
* : You may not use this file except in compliance with the License.
|
||||
* : You may obtain a copy of the License at
|
||||
* :
|
||||
* : http://www.apache.org/licenses/LICENSE-2.0
|
||||
* :
|
||||
* : Unless required by applicable law or agreed to in writing, software
|
||||
* : distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* : See the License for the specific language governing permissions and
|
||||
* : limitations under the License.
|
||||
************************************************************************/
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QResizeEvent>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include "easy_frame_rate_viewer.h"
|
||||
#include "globals.h"
|
||||
|
||||
const int INTERVAL_WIDTH = 20;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EasyFPSGraphicsItem::EasyFPSGraphicsItem() : Parent(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
EasyFPSGraphicsItem::~EasyFPSGraphicsItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
QRectF EasyFPSGraphicsItem::boundingRect() const
|
||||
{
|
||||
return m_boundingRect;
|
||||
}
|
||||
|
||||
void EasyFPSGraphicsItem::setBoundingRect(const QRectF& _boundingRect)
|
||||
{
|
||||
m_boundingRect = _boundingRect;
|
||||
}
|
||||
|
||||
void EasyFPSGraphicsItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h)
|
||||
{
|
||||
m_boundingRect.setRect(x, y, w, h);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EasyFPSGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
|
||||
{
|
||||
if (m_frames.empty())
|
||||
return;
|
||||
|
||||
const int fontHeight = QFontMetrics(scene()->font()).height() + 2;
|
||||
const qreal h = m_boundingRect.height() - (fontHeight << 1) - 4;
|
||||
if (h < 0)
|
||||
return;
|
||||
|
||||
const qreal halfWidth = m_boundingRect.width() * 0.5 - INTERVAL_WIDTH;
|
||||
const int halfMax = static_cast<int>(0.5 + halfWidth / INTERVAL_WIDTH);
|
||||
const int half = static_cast<int>(m_frames.size() / 2);
|
||||
const qreal top = fontHeight, bottom = h + 4 + fontHeight;
|
||||
qreal y;
|
||||
|
||||
_painter->save();
|
||||
|
||||
_painter->drawLine(QPointF(0, top), QPointF(m_boundingRect.width(), top));
|
||||
_painter->drawLine(QPointF(0, bottom), QPointF(m_boundingRect.width(), bottom));
|
||||
|
||||
_painter->setPen(Qt::lightGray);
|
||||
y = m_boundingRect.height() * 0.5;
|
||||
_painter->drawLine(QPointF(0, y), QPointF(m_boundingRect.width(), y));
|
||||
y -= h * 0.25;
|
||||
_painter->drawLine(QPointF(0, y), QPointF(m_boundingRect.width(), y));
|
||||
y += h * 0.5;
|
||||
_painter->drawLine(QPointF(0, y), QPointF(m_boundingRect.width(), y));
|
||||
|
||||
m_points1.reserve(m_frames.size());
|
||||
m_points2.reserve(m_frames.size());
|
||||
int n = 0;
|
||||
qreal x = m_boundingRect.width() * 0.5 + std::min(halfMax, half) * INTERVAL_WIDTH, localMax = 0, localMin = 1e30;
|
||||
const qreal xCurrent = x;
|
||||
for (int i = static_cast<int>(m_frames.size()) - 1; i > -1 && x >= 0; --i, x -= INTERVAL_WIDTH, ++n)
|
||||
{
|
||||
const auto& val = m_frames[i];
|
||||
|
||||
if (val.first > localMax)
|
||||
localMax = val.first;
|
||||
if (val.first < localMin)
|
||||
localMin = val.first;
|
||||
m_points1.emplace_back(x, static_cast<qreal>(val.first) + 1e-3);
|
||||
|
||||
if (val.second > localMax)
|
||||
localMax = val.second;
|
||||
if (val.second < localMin)
|
||||
localMin = val.second;
|
||||
m_points2.emplace_back(x, static_cast<qreal>(val.second) + 1e-3);
|
||||
|
||||
_painter->drawLine(QPointF(x, top + 1), QPointF(x, bottom - 1));
|
||||
}
|
||||
|
||||
const auto delta = std::max(localMax - localMin, 1e-3);
|
||||
_painter->setPen(Qt::black);
|
||||
|
||||
_painter->drawText(5, 0, m_boundingRect.width() - 10, fontHeight, Qt::AlignVCenter | Qt::AlignLeft, QString("Slowest %1 FPS (%2)")
|
||||
.arg(static_cast<quint64>(1e6 / localMax)).arg(::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, localMax, 1)));
|
||||
|
||||
_painter->drawText(5, 0, xCurrent - 5, fontHeight, Qt::AlignVCenter | Qt::AlignRight, QString("Max current %1 FPS (%2)")
|
||||
.arg(static_cast<quint64>(1e6 / m_frames.back().first)).arg(::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, m_frames.back().first, 1)));
|
||||
|
||||
_painter->drawText(5, bottom, xCurrent - 5, fontHeight, Qt::AlignVCenter | Qt::AlignRight, QString("Avg current %1 FPS (%2)")
|
||||
.arg(static_cast<quint64>(1e6 / m_frames.back().second)).arg(::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, m_frames.back().second, 1)));
|
||||
|
||||
_painter->drawText(5, bottom, m_boundingRect.width() - 10, fontHeight, Qt::AlignVCenter | Qt::AlignLeft, QString("Fastest %1 FPS (%2)")
|
||||
.arg(static_cast<quint64>(1e6 / localMin)).arg(::profiler_gui::timeStringReal(EASY_GLOBALS.time_units, localMin, 1)));
|
||||
|
||||
if (localMin < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < localMax)
|
||||
{
|
||||
y = fontHeight + 2 + h * (1. - (EASY_GLOBALS.frame_time - localMin) / delta);
|
||||
_painter->setPen(Qt::DashLine);
|
||||
_painter->drawLine(QPointF(0, y), QPointF(m_boundingRect.width(), y));
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
auto& point1 = m_points1[i];
|
||||
point1.setY(fontHeight + 2 + h * (1. - (point1.y() - localMin) / delta));
|
||||
|
||||
auto& point2 = m_points2[i];
|
||||
point2.setY(fontHeight + 2 + h * (1. - (point2.y() - localMin) / delta));
|
||||
}
|
||||
|
||||
_painter->setPen(QColor::fromRgba(0xa0ff0000));
|
||||
if (n > 1)
|
||||
{
|
||||
_painter->drawPolyline(m_points1.data(), n);
|
||||
|
||||
_painter->setPen(QColor::fromRgba(0xa00000ff));
|
||||
_painter->drawPolyline(m_points2.data(), n);
|
||||
}
|
||||
else
|
||||
{
|
||||
_painter->drawPoint(m_points1.back());
|
||||
|
||||
_painter->setPen(QColor::fromRgba(0xa00000ff));
|
||||
_painter->drawPoint(m_points2.back());
|
||||
}
|
||||
|
||||
_painter->restore();
|
||||
|
||||
m_points1.clear();
|
||||
m_points2.clear();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EasyFPSGraphicsItem::clear()
|
||||
{
|
||||
m_frames.clear();
|
||||
}
|
||||
|
||||
void EasyFPSGraphicsItem::addPoint(uint32_t _maxFrameTime, uint32_t _avgFrameTime)
|
||||
{
|
||||
m_frames.emplace_back(_maxFrameTime, _avgFrameTime);
|
||||
if (m_frames.size() > EASY_GLOBALS.max_fps_history)
|
||||
m_frames.pop_front();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EasyFrameRateViewer::EasyFrameRateViewer(QWidget* _parent) : Parent(_parent), m_fpsItem(nullptr)
|
||||
{
|
||||
setCacheMode(QGraphicsView::CacheNone);
|
||||
//setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
|
||||
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
|
||||
setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
|
||||
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
setScene(new QGraphicsScene(this));
|
||||
scene()->setSceneRect(0, 0, 50, 50);
|
||||
|
||||
m_fpsItem = new EasyFPSGraphicsItem();
|
||||
m_fpsItem->setPos(0, 0);
|
||||
m_fpsItem->setBoundingRect(0, 0, 50, 50);
|
||||
scene()->addItem(m_fpsItem);
|
||||
|
||||
centerOn(0, 0);
|
||||
}
|
||||
|
||||
EasyFrameRateViewer::~EasyFrameRateViewer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void EasyFrameRateViewer::clear()
|
||||
{
|
||||
m_fpsItem->clear();
|
||||
}
|
||||
|
||||
void EasyFrameRateViewer::addPoint(uint32_t _maxFrameTime, uint32_t _avgFrameTime)
|
||||
{
|
||||
m_fpsItem->addPoint(_maxFrameTime, _avgFrameTime);
|
||||
scene()->update();
|
||||
}
|
||||
|
||||
void EasyFrameRateViewer::resizeEvent(QResizeEvent* _event)
|
||||
{
|
||||
Parent::resizeEvent(_event);
|
||||
|
||||
m_fpsItem->setBoundingRect(0, 0, _event->size().width(), _event->size().height());
|
||||
|
||||
scene()->setSceneRect(m_fpsItem->boundingRect());
|
||||
scene()->update();
|
||||
}
|
||||
|
||||
void EasyFrameRateViewer::hideEvent(QHideEvent* _event)
|
||||
{
|
||||
Parent::hideEvent(_event);
|
||||
EASY_GLOBALS.fps_enabled = isVisible();
|
||||
clear();
|
||||
}
|
||||
|
||||
void EasyFrameRateViewer::showEvent(QShowEvent* _event)
|
||||
{
|
||||
Parent::showEvent(_event);
|
||||
EASY_GLOBALS.fps_enabled = isVisible();
|
||||
clear();
|
||||
}
|
||||
|
||||
void EasyFrameRateViewer::contextMenuEvent(QContextMenuEvent* _event)
|
||||
{
|
||||
QMenu menu;
|
||||
QAction* action = nullptr;
|
||||
|
||||
action = menu.addAction("Clear");
|
||||
connect(action, &QAction::triggered, [this](bool){ clear(); });
|
||||
|
||||
action = menu.addAction("Hide");
|
||||
connect(action, &QAction::triggered, [this](bool){ parentWidget()->hide(); });
|
||||
|
||||
menu.exec(QCursor::pos());
|
||||
|
||||
_event->accept();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
125
profiler_gui/easy_frame_rate_viewer.h
Normal file
125
profiler_gui/easy_frame_rate_viewer.h
Normal file
@ -0,0 +1,125 @@
|
||||
/************************************************************************
|
||||
* file name : easy_frame_rate_viewer.cpp
|
||||
* ----------------- :
|
||||
* creation time : 2017/04/02
|
||||
* author : Victor Zarubkin
|
||||
* email : v.s.zarubkin@gmail.com
|
||||
* ----------------- :
|
||||
* description : This file contains declaration of EasyFrameRateViewer widget.
|
||||
* ----------------- :
|
||||
* change log : * 2017/04/02 Victor Zarubkin: Initial commit.
|
||||
* :
|
||||
* : *
|
||||
* ----------------- :
|
||||
* license : Lightweight profiler library for c++
|
||||
* : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin
|
||||
* :
|
||||
* : Licensed under either of
|
||||
* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
|
||||
* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* : at your option.
|
||||
* :
|
||||
* : The MIT License
|
||||
* :
|
||||
* : Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* : of this software and associated documentation files (the "Software"), to deal
|
||||
* : in the Software without restriction, including without limitation the rights
|
||||
* : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* : of the Software, and to permit persons to whom the Software is furnished
|
||||
* : to do so, subject to the following conditions:
|
||||
* :
|
||||
* : The above copyright notice and this permission notice shall be included in all
|
||||
* : copies or substantial portions of the Software.
|
||||
* :
|
||||
* : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* : USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* :
|
||||
* : The Apache License, Version 2.0 (the "License")
|
||||
* :
|
||||
* : You may not use this file except in compliance with the License.
|
||||
* : You may obtain a copy of the License at
|
||||
* :
|
||||
* : http://www.apache.org/licenses/LICENSE-2.0
|
||||
* :
|
||||
* : Unless required by applicable law or agreed to in writing, software
|
||||
* : distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* : See the License for the specific language governing permissions and
|
||||
* : limitations under the License.
|
||||
************************************************************************/
|
||||
|
||||
#ifndef EASY__FRAME_RATE_VIEWER__H
|
||||
#define EASY__FRAME_RATE_VIEWER__H
|
||||
|
||||
#include <QGraphicsView>
|
||||
#include <QGraphicsItem>
|
||||
#include <QTimer>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <easy/profiler.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class EasyFPSGraphicsItem : public QGraphicsItem
|
||||
{
|
||||
typedef QGraphicsItem Parent;
|
||||
typedef EasyFPSGraphicsItem This;
|
||||
typedef std::deque<std::pair<uint32_t, uint32_t> > FrameTimes;
|
||||
|
||||
std::vector<QPointF> m_points1, m_points2;
|
||||
FrameTimes m_frames;
|
||||
QRectF m_boundingRect;
|
||||
|
||||
public:
|
||||
|
||||
explicit EasyFPSGraphicsItem();
|
||||
virtual ~EasyFPSGraphicsItem();
|
||||
|
||||
QRectF boundingRect() const override;
|
||||
void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override;
|
||||
|
||||
void setBoundingRect(const QRectF& _boundingRect);
|
||||
void setBoundingRect(qreal x, qreal y, qreal w, qreal h);
|
||||
|
||||
void clear();
|
||||
void addPoint(uint32_t _maxFrameTime, uint32_t _avgFrameTime);
|
||||
|
||||
}; // END of class EasyFPSGraphicsItem.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class EasyFrameRateViewer : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
typedef QGraphicsView Parent;
|
||||
typedef EasyFrameRateViewer This;
|
||||
|
||||
EasyFPSGraphicsItem* m_fpsItem;
|
||||
|
||||
public:
|
||||
|
||||
explicit EasyFrameRateViewer(QWidget* _parent = nullptr);
|
||||
virtual ~EasyFrameRateViewer();
|
||||
|
||||
void resizeEvent(QResizeEvent* _event) override;
|
||||
void hideEvent(QHideEvent* _event) override;
|
||||
void showEvent(QShowEvent* _event) override;
|
||||
void contextMenuEvent(QContextMenuEvent* _event) override;
|
||||
|
||||
public slots:
|
||||
|
||||
void clear();
|
||||
void addPoint(uint32_t _maxFrameTime, uint32_t _avgFrameTime);
|
||||
|
||||
}; // END of class EasyFrameRateViewer.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // EASY__FRAME_RATE_VIEWER__H
|
@ -76,9 +76,12 @@ namespace profiler_gui {
|
||||
, blocks_spacing(2)
|
||||
, blocks_size_min(2)
|
||||
, blocks_narrow_size(20)
|
||||
, max_fps_history(100)
|
||||
, fps_timer_interval(1000)
|
||||
, chrono_text_position(ChronoTextPosition_Center)
|
||||
, time_units(TimeUnits_ms)
|
||||
, connected(false)
|
||||
, fps_enabled(true)
|
||||
, use_decorated_thread_name(true)
|
||||
, enable_event_markers(true)
|
||||
, enable_statistics(true)
|
||||
|
@ -155,9 +155,12 @@ namespace profiler_gui {
|
||||
int blocks_spacing; ///< Minimum blocks spacing on diagram
|
||||
int blocks_size_min; ///< Minimum blocks size on diagram
|
||||
int blocks_narrow_size; ///< Width indicating narrow blocks
|
||||
int max_fps_history; ///< Max frames history displayed in FPS Monitor
|
||||
int fps_timer_interval; ///< Interval in milliseconds for sending network requests to the profiled application (used by FPS Monitor)
|
||||
ChronometerTextPosition chrono_text_position; ///< Selected interval text position
|
||||
TimeUnits time_units; ///< Units type for time (milliseconds, microseconds, nanoseconds or auto-definition)
|
||||
bool connected; ///< Is connected to source (to be able to capture profiling information)
|
||||
bool fps_enabled; ///< Is FPS Monitor enabled
|
||||
bool use_decorated_thread_name; ///< Add "Thread" to the name of each thread (if there is no one)
|
||||
bool enable_event_markers; ///< Enable event indicators painting (These are narrow rectangles at the bottom of each thread)
|
||||
bool enable_statistics; ///< Enable gathering and using statistics (Disable if you want to consume less memory)
|
||||
|
@ -96,6 +96,7 @@
|
||||
#include "blocks_tree_widget.h"
|
||||
#include "blocks_graphics_view.h"
|
||||
#include "descriptors_tree_widget.h"
|
||||
#include "easy_frame_rate_viewer.h"
|
||||
#include "globals.h"
|
||||
|
||||
#include <easy/easy_net.h>
|
||||
@ -156,8 +157,14 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("localhost"), m_lastP
|
||||
auto treeWidget = new EasyHierarchyWidget(this);
|
||||
m_treeWidget->setWidget(treeWidget);
|
||||
|
||||
m_fpsViewer = new QDockWidget("FPS Monitor", this);
|
||||
m_fpsViewer->setObjectName("ProfilerGUI_FPS");
|
||||
m_fpsViewer->setWidget(new EasyFrameRateViewer(this));
|
||||
m_fpsViewer->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
|
||||
|
||||
addDockWidget(Qt::TopDockWidgetArea, m_graphicsView);
|
||||
addDockWidget(Qt::BottomDockWidgetArea, m_treeWidget);
|
||||
addDockWidget(Qt::TopDockWidgetArea, m_fpsViewer);
|
||||
|
||||
#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0
|
||||
auto descTree = new EasyDescWidget();
|
||||
@ -411,7 +418,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("localhost"), m_lastP
|
||||
l->setContentsMargins(33, 1, 1, 1);
|
||||
l->addWidget(new QLabel("Min blocks spacing, px", w), 0, Qt::AlignLeft);
|
||||
auto spinbox = new QSpinBox(w);
|
||||
spinbox->setMinimum(0);
|
||||
spinbox->setRange(0, 400);
|
||||
spinbox->setValue(EASY_GLOBALS.blocks_spacing);
|
||||
spinbox->setFixedWidth(50);
|
||||
connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onSpacingChange(int)));
|
||||
@ -426,7 +433,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("localhost"), m_lastP
|
||||
l->setContentsMargins(33, 1, 1, 1);
|
||||
l->addWidget(new QLabel("Min blocks size, px", w), 0, Qt::AlignLeft);
|
||||
spinbox = new QSpinBox(w);
|
||||
spinbox->setMinimum(1);
|
||||
spinbox->setRange(1, 400);
|
||||
spinbox->setValue(EASY_GLOBALS.blocks_size_min);
|
||||
spinbox->setFixedWidth(50);
|
||||
connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onMinSizeChange(int)));
|
||||
@ -441,7 +448,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("localhost"), m_lastP
|
||||
l->setContentsMargins(33, 1, 1, 1);
|
||||
l->addWidget(new QLabel("Blocks narrow size, px", w), 0, Qt::AlignLeft);
|
||||
spinbox = new QSpinBox(w);
|
||||
spinbox->setMinimum(1);
|
||||
spinbox->setRange(1, 400);
|
||||
spinbox->setValue(EASY_GLOBALS.blocks_narrow_size);
|
||||
spinbox->setFixedWidth(50);
|
||||
connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onNarrowSizeChange(int)));
|
||||
@ -452,6 +459,42 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("localhost"), m_lastP
|
||||
submenu->addAction(waction);
|
||||
|
||||
|
||||
|
||||
|
||||
submenu = menu->addMenu("FPS Monitor");
|
||||
w = new QWidget(submenu);
|
||||
l = new QHBoxLayout(w);
|
||||
l->setContentsMargins(33, 1, 1, 1);
|
||||
l->addWidget(new QLabel("Request interval, ms", w), 0, Qt::AlignLeft);
|
||||
spinbox = new QSpinBox(w);
|
||||
spinbox->setRange(1, 600000);
|
||||
spinbox->setValue(EASY_GLOBALS.fps_timer_interval);
|
||||
spinbox->setFixedWidth(50);
|
||||
connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onFpsIntervalChange(int)));
|
||||
l->addWidget(spinbox);
|
||||
w->setLayout(l);
|
||||
waction = new QWidgetAction(submenu);
|
||||
waction->setDefaultWidget(w);
|
||||
submenu->addAction(waction);
|
||||
|
||||
w = new QWidget(submenu);
|
||||
l = new QHBoxLayout(w);
|
||||
l->setContentsMargins(33, 1, 1, 1);
|
||||
l->addWidget(new QLabel("Max history size", w), 0, Qt::AlignLeft);
|
||||
spinbox = new QSpinBox(w);
|
||||
spinbox->setRange(2, 200);
|
||||
spinbox->setValue(EASY_GLOBALS.max_fps_history);
|
||||
spinbox->setFixedWidth(50);
|
||||
connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onFpsHistoryChange(int)));
|
||||
l->addWidget(spinbox);
|
||||
w->setLayout(l);
|
||||
waction = new QWidgetAction(submenu);
|
||||
waction->setDefaultWidget(w);
|
||||
submenu->addAction(waction);
|
||||
|
||||
|
||||
|
||||
|
||||
submenu = menu->addMenu("Units");
|
||||
actionGroup = new QActionGroup(this);
|
||||
actionGroup->setExclusive(true);
|
||||
@ -549,6 +592,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("localhost"), m_lastP
|
||||
connect(graphicsView->view(), &EasyGraphicsView::intervalChanged, treeWidget->tree(), &EasyTreeWidget::setTreeBlocks);
|
||||
connect(&m_readerTimer, &QTimer::timeout, this, &This::onFileReaderTimeout);
|
||||
connect(&m_listenerTimer, &QTimer::timeout, this, &This::onListenerTimerTimeout);
|
||||
connect(&m_fpsRequestTimer, &QTimer::timeout, this, &This::onFrameTimeRequestTimeout);
|
||||
|
||||
|
||||
m_progress = new QProgressDialog("Loading file...", "Cancel", 0, 100, this);
|
||||
@ -907,6 +951,24 @@ void EasyMainWindow::onNarrowSizeChange(int _value)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EasyMainWindow::onFpsIntervalChange(int _value)
|
||||
{
|
||||
EASY_GLOBALS.fps_timer_interval = _value;
|
||||
|
||||
if (m_fpsRequestTimer.isActive())
|
||||
m_fpsRequestTimer.stop();
|
||||
|
||||
if (EASY_GLOBALS.connected)
|
||||
m_fpsRequestTimer.start(_value);
|
||||
}
|
||||
|
||||
void EasyMainWindow::onFpsHistoryChange(int _value)
|
||||
{
|
||||
EASY_GLOBALS.max_fps_history = _value;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EasyMainWindow::onEditBlocksClicked(bool)
|
||||
{
|
||||
if (m_descTreeDialog != nullptr)
|
||||
@ -1056,6 +1118,14 @@ void EasyMainWindow::loadSettings()
|
||||
if (!flag.isNull())
|
||||
EASY_GLOBALS.use_decorated_thread_name = flag.toBool();
|
||||
|
||||
flag = settings.value("fps_timer_interval");
|
||||
if (!flag.isNull())
|
||||
EASY_GLOBALS.fps_timer_interval = flag.toInt();
|
||||
|
||||
flag = settings.value("max_fps_history");
|
||||
if (!flag.isNull())
|
||||
EASY_GLOBALS.max_fps_history = flag.toInt();
|
||||
|
||||
flag = settings.value("enable_statistics");
|
||||
if (!flag.isNull())
|
||||
EASY_GLOBALS.enable_statistics = flag.toBool();
|
||||
@ -1115,6 +1185,8 @@ void EasyMainWindow::saveSettingsAndGeometry()
|
||||
settings.setValue("auto_adjust_histogram_height", EASY_GLOBALS.auto_adjust_histogram_height);
|
||||
settings.setValue("use_decorated_thread_name", EASY_GLOBALS.use_decorated_thread_name);
|
||||
settings.setValue("enable_statistics", EASY_GLOBALS.enable_statistics);
|
||||
settings.setValue("fps_timer_interval", EASY_GLOBALS.fps_timer_interval);
|
||||
settings.setValue("max_fps_history", EASY_GLOBALS.max_fps_history);
|
||||
settings.setValue("encoding", QTextCodec::codecForLocale()->name());
|
||||
|
||||
settings.endGroup();
|
||||
@ -1122,6 +1194,9 @@ void EasyMainWindow::saveSettingsAndGeometry()
|
||||
|
||||
void EasyMainWindow::setDisconnected(bool _showMessage)
|
||||
{
|
||||
if (m_fpsRequestTimer.isActive())
|
||||
m_fpsRequestTimer.stop();
|
||||
|
||||
if (_showMessage)
|
||||
QMessageBox::warning(this, "Warning", "Application was disconnected", QMessageBox::Close);
|
||||
|
||||
@ -1138,6 +1213,35 @@ void EasyMainWindow::setDisconnected(bool _showMessage)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EasyMainWindow::onFrameTimeRequestTimeout()
|
||||
{
|
||||
if (EASY_GLOBALS.fps_enabled && EASY_GLOBALS.connected && m_listener.regime() == LISTENER_IDLE)
|
||||
{
|
||||
if (m_listener.requestFrameTime())
|
||||
{
|
||||
QTimer::singleShot(100, this, &This::checkFrameTimeReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EasyMainWindow::checkFrameTimeReady()
|
||||
{
|
||||
if (EASY_GLOBALS.fps_enabled && EASY_GLOBALS.connected && m_listener.regime() == LISTENER_IDLE)
|
||||
{
|
||||
uint32_t maxTime = 0, avgTime = 0;
|
||||
if (m_listener.frameTime(maxTime, avgTime))
|
||||
{
|
||||
static_cast<EasyFrameRateViewer*>(m_fpsViewer->widget())->addPoint(maxTime, avgTime);
|
||||
}
|
||||
else if (m_fpsRequestTimer.isActive())
|
||||
{
|
||||
QTimer::singleShot(100, this, &This::checkFrameTimeReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EasyMainWindow::onListenerTimerTimeout()
|
||||
{
|
||||
if (!m_listener.connected())
|
||||
@ -1579,6 +1683,9 @@ void EasyMainWindow::onConnectClicked(bool)
|
||||
m_captureAction->setEnabled(true);
|
||||
SET_ICON(m_connectAction, ":/Connection-on");
|
||||
|
||||
if (!m_fpsRequestTimer.isActive())
|
||||
m_fpsRequestTimer.start(EASY_GLOBALS.fps_timer_interval);
|
||||
|
||||
disconnect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange);
|
||||
disconnect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange);
|
||||
|
||||
@ -1807,6 +1914,9 @@ EasySocketListener::EasySocketListener() : m_receivedSize(0), m_port(0), m_regim
|
||||
m_bInterrupt = ATOMIC_VAR_INIT(false);
|
||||
m_bConnected = ATOMIC_VAR_INIT(false);
|
||||
m_bStopReceive = ATOMIC_VAR_INIT(false);
|
||||
m_bFrameTimeReady = ATOMIC_VAR_INIT(false);
|
||||
m_frameMax = ATOMIC_VAR_INIT(0);
|
||||
m_frameAvg = ATOMIC_VAR_INIT(0);
|
||||
}
|
||||
|
||||
EasySocketListener::~EasySocketListener()
|
||||
@ -1931,6 +2041,13 @@ bool EasySocketListener::connect(const char* _ipaddress, uint16_t _port, profile
|
||||
|
||||
bool EasySocketListener::startCapture()
|
||||
{
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_bInterrupt.store(true, ::std::memory_order_release);
|
||||
m_thread.join();
|
||||
m_bInterrupt.store(false, ::std::memory_order_release);
|
||||
}
|
||||
|
||||
clearData();
|
||||
|
||||
profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE);
|
||||
@ -1968,6 +2085,13 @@ void EasySocketListener::stopCapture()
|
||||
|
||||
void EasySocketListener::requestBlocksDescription()
|
||||
{
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_bInterrupt.store(true, ::std::memory_order_release);
|
||||
m_thread.join();
|
||||
m_bInterrupt.store(false, ::std::memory_order_release);
|
||||
}
|
||||
|
||||
clearData();
|
||||
|
||||
profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_BLOCKS_DESCRIPTION);
|
||||
@ -1982,6 +2106,47 @@ void EasySocketListener::requestBlocksDescription()
|
||||
m_regime = LISTENER_IDLE;
|
||||
}
|
||||
|
||||
bool EasySocketListener::frameTime(uint32_t& _maxTime, uint32_t& _avgTime)
|
||||
{
|
||||
if (m_bFrameTimeReady.exchange(false, ::std::memory_order_acquire))
|
||||
{
|
||||
_maxTime = m_frameMax.load(::std::memory_order_acquire);
|
||||
_avgTime = m_frameAvg.load(::std::memory_order_acquire);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EasySocketListener::requestFrameTime()
|
||||
{
|
||||
if (m_regime != LISTENER_IDLE)
|
||||
return false;
|
||||
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_bInterrupt.store(true, ::std::memory_order_release);
|
||||
m_thread.join();
|
||||
m_bInterrupt.store(false, ::std::memory_order_release);
|
||||
}
|
||||
|
||||
clearData();
|
||||
|
||||
profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_MAIN_FRAME_TIME_MAX_AVG_US);
|
||||
m_easySocket.send(&request, sizeof(request));
|
||||
|
||||
if (m_easySocket.isDisconnected())
|
||||
{
|
||||
m_bConnected.store(false, ::std::memory_order_release);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_bFrameTimeReady.store(false, ::std::memory_order_release);
|
||||
m_thread = ::std::thread(&EasySocketListener::listenFrameTime, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EasySocketListener::listenCapture()
|
||||
@ -2281,5 +2446,84 @@ void EasySocketListener::listenDescription()
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void EasySocketListener::listenFrameTime()
|
||||
{
|
||||
// TODO: Merge functions listenDescription() and listenCapture()
|
||||
|
||||
static const int buffer_size = sizeof(::profiler::net::TimestampMessage) << 2;
|
||||
char buffer[buffer_size] = {};
|
||||
int seek = 0, bytes = 0;
|
||||
|
||||
bool isListen = true;
|
||||
while (isListen && !m_bInterrupt.load(::std::memory_order_acquire))
|
||||
{
|
||||
if ((bytes - seek) == 0)
|
||||
{
|
||||
bytes = m_easySocket.receive(buffer, buffer_size);
|
||||
|
||||
if (bytes == -1)
|
||||
{
|
||||
if (m_easySocket.isDisconnected())
|
||||
{
|
||||
m_bConnected.store(false, ::std::memory_order_release);
|
||||
isListen = false;
|
||||
}
|
||||
|
||||
seek = 0;
|
||||
bytes = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
seek = 0;
|
||||
}
|
||||
|
||||
if (bytes == 0)
|
||||
{
|
||||
isListen = false;
|
||||
break;
|
||||
}
|
||||
|
||||
char* buf = buffer + seek;
|
||||
|
||||
if (bytes > 0)
|
||||
{
|
||||
auto message = reinterpret_cast<const ::profiler::net::Message*>(buf);
|
||||
if (!message->isEasyNetMessage())
|
||||
continue;
|
||||
|
||||
switch (message->type)
|
||||
{
|
||||
case profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION:
|
||||
{
|
||||
//qInfo() << "Receive MESSAGE_TYPE_ACCEPTED_CONNECTION";
|
||||
seek += sizeof(profiler::net::Message);
|
||||
break;
|
||||
}
|
||||
|
||||
case profiler::net::MESSAGE_TYPE_REPLY_MAIN_FRAME_TIME_MAX_AVG_US:
|
||||
{
|
||||
//qInfo() << "Receive MESSAGE_TYPE_REPLY_MAIN_FRAME_TIME_MAX_AVG_US";
|
||||
|
||||
seek += sizeof(profiler::net::TimestampMessage);
|
||||
if (seek <= buffer_size)
|
||||
{
|
||||
profiler::net::TimestampMessage* timestampMessage = (profiler::net::TimestampMessage*)message;
|
||||
m_frameMax.store(timestampMessage->maxValue, ::std::memory_order_release);
|
||||
m_frameAvg.store(timestampMessage->avgValue, ::std::memory_order_release);
|
||||
m_bFrameTimeReady.store(true, ::std::memory_order_release);
|
||||
}
|
||||
|
||||
isListen = false;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -140,9 +140,12 @@ class EasySocketListener Q_DECL_FINAL
|
||||
::std::thread m_thread; ///<
|
||||
uint64_t m_receivedSize; ///<
|
||||
uint16_t m_port; ///<
|
||||
::std::atomic<uint32_t> m_frameMax; ///<
|
||||
::std::atomic<uint32_t> m_frameAvg; ///<
|
||||
::std::atomic_bool m_bInterrupt; ///<
|
||||
::std::atomic_bool m_bConnected; ///<
|
||||
::std::atomic_bool m_bStopReceive; ///<
|
||||
::std::atomic_bool m_bFrameTimeReady; ///<
|
||||
EasyListenerRegime m_regime; ///<
|
||||
|
||||
public:
|
||||
@ -165,6 +168,9 @@ public:
|
||||
void stopCapture();
|
||||
void requestBlocksDescription();
|
||||
|
||||
bool frameTime(uint32_t& _maxTime, uint32_t& _avgTime);
|
||||
bool requestFrameTime();
|
||||
|
||||
template <class T>
|
||||
inline void send(const T& _message) {
|
||||
m_easySocket.send(&_message, sizeof(T));
|
||||
@ -174,6 +180,7 @@ private:
|
||||
|
||||
void listenCapture();
|
||||
void listenDescription();
|
||||
void listenFrameTime();
|
||||
|
||||
}; // END of class EasySocketListener.
|
||||
|
||||
@ -192,6 +199,7 @@ protected:
|
||||
QString m_lastAddress;
|
||||
QDockWidget* m_treeWidget = nullptr;
|
||||
QDockWidget* m_graphicsView = nullptr;
|
||||
QDockWidget* m_fpsViewer = nullptr;
|
||||
|
||||
#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0
|
||||
QDockWidget* m_descTreeWidget = nullptr;
|
||||
@ -203,6 +211,7 @@ protected:
|
||||
class QMessageBox* m_listenerDialog = nullptr;
|
||||
QTimer m_readerTimer;
|
||||
QTimer m_listenerTimer;
|
||||
QTimer m_fpsRequestTimer;
|
||||
::profiler::SerializedData m_serializedBlocks;
|
||||
::profiler::SerializedData m_serializedDescriptors;
|
||||
EasyFileReader m_reader;
|
||||
@ -257,7 +266,10 @@ protected slots:
|
||||
void onSpacingChange(int _value);
|
||||
void onMinSizeChange(int _value);
|
||||
void onNarrowSizeChange(int _value);
|
||||
void onFpsIntervalChange(int _value);
|
||||
void onFpsHistoryChange(int _value);
|
||||
void onFileReaderTimeout();
|
||||
void onFrameTimeRequestTimeout();
|
||||
void onListenerTimerTimeout();
|
||||
void onFileReaderCancel();
|
||||
void onEditBlocksClicked(bool);
|
||||
@ -273,6 +285,8 @@ protected slots:
|
||||
|
||||
void onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status);
|
||||
|
||||
void checkFrameTimeReady();
|
||||
|
||||
private:
|
||||
|
||||
// Private non-virtual methods
|
||||
|
@ -226,25 +226,28 @@ int main(int argc, char* argv[])
|
||||
cv_m.unlock();
|
||||
cv.notify_all();
|
||||
|
||||
#ifndef SAMPLE_NETWORK_TEST
|
||||
std::atomic_bool stop = ATOMIC_VAR_INIT(false);
|
||||
auto frame_time_printer_thread = std::thread([&stop]()
|
||||
{
|
||||
while (!stop.load(std::memory_order_acquire))
|
||||
{
|
||||
std::cout << "Frame time: " << profiler::main_thread::frameTimeLocalMax() << " us\n";
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
std::cout << "Frame time: max " << profiler::main_thread::frameTimeLocalMax() << " us // avg " << profiler::main_thread::frameTimeLocalAvg() << " us\n";
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
modellingThread();
|
||||
|
||||
#ifndef SAMPLE_NETWORK_TEST
|
||||
stop.store(true, std::memory_order_release);
|
||||
frame_time_printer_thread.join();
|
||||
#endif
|
||||
|
||||
for(auto& t : threads)
|
||||
t.join();
|
||||
|
||||
frame_time_printer_thread.join();
|
||||
|
||||
auto end = std::chrono::system_clock::now();
|
||||
auto elapsed =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
Loading…
x
Reference in New Issue
Block a user