Commit 5aa7a48f authored by Arjen Hiemstra's avatar Arjen Hiemstra Committed by Vlad Zahorodnii
Browse files

Replace "Show FPS" effect with a QML version

This removes a bunch of custom rendering, replacing it with a simple QML
file that makes use of KQuickCharts for chart rendering. Functionally,
it should be mostly the same except that I removed all configuration
options as I don't see why we have those.
parent fa5be54a
Pipeline #219007 passed with stage
in 33 minutes and 20 seconds
#######################################
# Effect
# SPDX-FileCopyrightText: 2022 Arjen Hiemstra <ahiemstra@heimr.nl>
#
# SPDX-License-Identifier: BSD-3-Clause
set(showfps_SOURCES
main.cpp
showfps.cpp
)
kconfig_add_kcfg_files(showfps_SOURCES
showfpsconfig.kcfgc
showfpseffect.cpp
)
kwin4_add_effect_module(kwin4_effect_showfps ${showfps_SOURCES})
target_link_libraries(kwin4_effect_showfps PRIVATE
kwineffects
kwinglutils
KF5::ConfigGui
KF5::I18n
)
#######################################
# Config
if (KWIN_BUILD_KCMS)
set(kwin_showfps_config_SRCS showfps_config.cpp)
ki18n_wrap_ui(kwin_showfps_config_SRCS showfps_config.ui)
kconfig_add_kcfg_files(kwin_showfps_config_SRCS showfpsconfig.kcfgc)
kwin_add_effect_config(kwin_showfps_config ${kwin_showfps_config_SRCS})
target_link_libraries(kwin_showfps_config
KF5::ConfigWidgets
KF5::CoreAddons
KF5::I18n
Qt::DBus
KWinEffectsInterface
Qt::Quick
)
endif()
install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/showfps)
......@@ -4,13 +4,14 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "showfps.h"
#include "showfpseffect.h"
namespace KWin
{
KWIN_EFFECT_FACTORY(ShowFpsEffect,
"metadata.json.stripped")
KWIN_EFFECT_FACTORY_SUPPORTED(ShowFpsEffect,
"metadata.json.stripped",
return ShowFpsEffect::supported();)
} // namespace KWin
......
......@@ -72,6 +72,5 @@
"Name[x-test]": "xxShow FPSxx",
"Name[zh_CN]": "显示每秒帧数",
"Name[zh_TW]": "顯示 FPS"
},
"X-KDE-ConfigModule": "kwin_showfps_config"
}
}
/*
SPDX-FileCopyrightText: 2022 Arjen Hiemstra <ahiemstra@heimr.nl>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.18 as Kirigami
import org.kde.quickcharts 1.0 as Charts
import org.kde.quickcharts.controls 1.0 as ChartControls
Rectangle {
id: root
required property QtObject effect
readonly property color gridColor: Qt.rgba(Kirigami.Theme.backgroundColor.r,
Kirigami.Theme.backgroundColor.g,
Kirigami.Theme.backgroundColor.b,
0.25)
color: Qt.rgba(1.0, 1.0, 1.0, 0.5)
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Charts.BarChart {
id: fpsChart
Layout.preferredWidth: Kirigami.Units.gridUnit
Layout.fillHeight: true
yRange.minimum: root.effect.maximumFps + 10
yRange.increment: 10
valueSources: Charts.SingleValueSource { value: root.effect.fps }
colorSource: Charts.SingleValueSource { value: Kirigami.Theme.highlightColor }
Charts.GridLines {
anchors.fill: parent
z: -1
chart: fpsChart
direction: Charts.GridLines.Vertical;
major.visible: false
minor.frequency: 10
minor.lineWidth: 1
minor.color: root.gridColor
}
}
Charts.BarChart {
Layout.fillWidth: true
Layout.fillHeight: true
yRange.minimum: 100
yRange.increment: 10
xRange.from: 0
xRange.to: 50
xRange.automatic: false
indexingMode: Charts.Chart.IndexSourceValues
valueSources: Charts.HistoryProxySource {
source: Charts.SingleValueSource {
value: root.effect.paintDuration
}
maximumHistory: 100
fillMode: Charts.HistoryProxySource.FillFromStart
}
colorSource: Charts.HistoryProxySource {
source: Charts.SingleValueSource {
value: root.effect.paintColor
}
maximumHistory: 100
fillMode: Charts.HistoryProxySource.FillFromStart
}
Charts.GridLines {
anchors.fill: parent
z: -1
chart: parent
direction: Charts.GridLines.Vertical;
major.visible: false
minor.frequency: 10
minor.lineWidth: 1
minor.color: root.gridColor
}
Label {
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
text: i18nc("@label", "Paint Duration")
font: Kirigami.Theme.smallFont
}
}
Charts.BarChart {
Layout.fillWidth: true
Layout.fillHeight: true
yRange.minimum: Qt.application.screens[0].width * Qt.application.screens[0].height
yRange.increment: 500000
xRange.from: 0
xRange.to: 50
xRange.automatic: false
indexingMode: Charts.Chart.IndexSourceValues
valueSources: Charts.HistoryProxySource {
source: Charts.SingleValueSource {
value: root.effect.paintAmount
}
maximumHistory: 100
fillMode: Charts.HistoryProxySource.FillFromStart
}
colorSource: Charts.HistoryProxySource {
source: Charts.SingleValueSource {
value: root.effect.paintColor
}
maximumHistory: 100
fillMode: Charts.HistoryProxySource.FillFromStart
}
Charts.GridLines {
anchors.fill: parent
z: -1
chart: parent
direction: Charts.GridLines.Vertical;
major.visible: false
minor.frequency: 100000
minor.lineWidth: 1
minor.color: root.gridColor
}
Label {
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
text: i18nc("@label", "Paint Amount")
font: Kirigami.Theme.smallFont
}
}
}
RowLayout {
Layout.fillWidth: true
ChartControls.LegendDelegate {
Layout.fillWidth: true
Layout.preferredWidth: 0
name: i18nc("@label", "Current FPS")
value: root.effect.fps
color: Kirigami.Theme.highlightColor
}
ChartControls.LegendDelegate {
Layout.fillWidth: true
Layout.preferredWidth: 0
name: i18nc("@label", "Maximum FPS")
value: root.effect.maximumFps
color: Kirigami.Theme.neutralTextColor
}
}
Label {
Layout.fillWidth: true
text: i18nc("@label", "This effect is not a benchmark")
}
}
}
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "showfps.h"
// KConfigSkeleton
#include "showfpsconfig.h"
#include <kwinconfig.h>
#include <kwinglutils.h>
#include <KLocalizedString>
#include <QPainter>
#include <QPalette>
#include <QVector2D>
#include <cmath>
namespace KWin
{
const int FPS_WIDTH = 10;
const int MAX_TIME = 100;
ShowFpsEffect::ShowFpsEffect()
: paints_pos(0)
, frames_pos(0)
, m_noBenchmark(effects->effectFrame(EffectFrameUnstyled, false))
{
initConfig<ShowFpsConfig>();
for (int i = 0; i < NUM_PAINTS; i++) {
paints[i] = 0;
paint_size[i] = 0;
}
for (int i = 0; i < MAX_FPS; i++) {
frames[i] = 0;
}
if (m_showNoBenchmark) {
m_noBenchmark->setAlignment(Qt::AlignTop | Qt::AlignRight);
m_noBenchmark->setText(i18n("This effect is not a benchmark"));
}
reconfigure(ReconfigureAll);
}
ShowFpsEffect::~ShowFpsEffect() = default;
void ShowFpsEffect::reconfigure(ReconfigureFlags)
{
ShowFpsConfig::self()->read();
alpha = ShowFpsConfig::alpha();
x = ShowFpsConfig::x();
y = ShowFpsConfig::y();
m_showNoBenchmark = ShowFpsConfig::showNoBenchmark();
m_showGraph = ShowFpsConfig::showGraph();
m_colorizeText = ShowFpsConfig::colorizeText();
const QSize screenSize = effects->virtualScreenSize();
if (x == -10000) { // there's no -0 :(
x = screenSize.width() - 2 * NUM_PAINTS - FPS_WIDTH;
} else if (x < 0) {
x = screenSize.width() - 2 * NUM_PAINTS - FPS_WIDTH - x;
}
if (y == -10000) {
y = screenSize.height() - MAX_TIME;
} else if (y < 0) {
y = screenSize.height() - MAX_TIME - y;
}
fps_rect = QRect(x, y, FPS_WIDTH + 2 * NUM_PAINTS, MAX_TIME);
m_noBenchmark->setPosition(fps_rect.bottomRight() + QPoint(-6, 6));
int textPosition = ShowFpsConfig::textPosition();
textFont = ShowFpsConfig::textFont();
textColor = ShowFpsConfig::textColor();
double textAlpha = ShowFpsConfig::textAlpha();
if (!textColor.isValid()) {
textColor = QPalette().color(QPalette::Active, QPalette::WindowText);
}
textColor.setAlphaF(textAlpha);
switch (textPosition) {
case TOP_LEFT:
fpsTextRect = QRect(0, 0, 100, 100);
textAlign = Qt::AlignTop | Qt::AlignLeft;
break;
case TOP_RIGHT:
fpsTextRect = QRect(screenSize.width() - 100, 0, 100, 100);
textAlign = Qt::AlignTop | Qt::AlignRight;
break;
case BOTTOM_LEFT:
fpsTextRect = QRect(0, screenSize.height() - 100, 100, 100);
textAlign = Qt::AlignBottom | Qt::AlignLeft;
break;
case BOTTOM_RIGHT:
fpsTextRect = QRect(screenSize.width() - 100, screenSize.height() - 100, 100, 100);
textAlign = Qt::AlignBottom | Qt::AlignRight;
break;
case NOWHERE:
fpsTextRect = QRect();
break;
case INSIDE_GRAPH:
default:
fpsTextRect = QRect(x, y, FPS_WIDTH + NUM_PAINTS, MAX_TIME);
textAlign = Qt::AlignTop | Qt::AlignRight;
break;
}
}
void ShowFpsEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
{
frames[frames_pos] = QDateTime::currentMSecsSinceEpoch();
if (++frames_pos == MAX_FPS) {
frames_pos = 0;
}
effects->prePaintScreen(data, presentTime);
data.paint += fps_rect;
paint_size[paints_pos] = 0;
t.restart();
// detect highest monitor refresh rate
int num_screens = effects->screens().size();
detectedMaxFps = 0;
for (int i = 0; i < num_screens; ++i) {
detectedMaxFps = std::max(effects->screens().at(i)->refreshRate(), detectedMaxFps);
}
detectedMaxFps /= 1000; // convert mHz to Hz (see kwineffects.h: EffectScreen)
}
void ShowFpsEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
{
effects->paintWindow(w, mask, region, data);
// Take intersection of region and actual window's rect, minus the fps area
// (since we keep repainting it) and count the pixels.
QRegion r2 = region & QRect(w->x(), w->y(), w->width(), w->height());
r2 -= fps_rect;
int winsize = 0;
for (const QRect &r : r2) {
winsize += r.width() * r.height();
}
paint_size[paints_pos] += winsize;
}
void ShowFpsEffect::paintScreen(int mask, const QRegion &region, ScreenPaintData &data)
{
effects->paintScreen(mask, region, data);
int lastFrame = frames_pos - 1;
if (lastFrame < 0) {
lastFrame = MAX_FPS - 1;
}
const qint64 lastTimestamp = frames[lastFrame];
int fps = 0;
for (int i = 0; i < MAX_FPS; i++) {
if (abs(lastTimestamp - frames[i]) < 1000) {
++fps; // count all frames in the last second
}
}
if (effects->isOpenGLCompositing()) {
paintGL(fps, data.projectionMatrix());
glFinish(); // make sure all rendering is done
} else if (effects->compositingType() == QPainterCompositing) {
paintQPainter(fps);
}
if (m_showNoBenchmark) {
m_noBenchmark->render(infiniteRegion(), 1.0, alpha);
}
}
void ShowFpsEffect::paintGL(int fps, const QMatrix4x4 &projectionMatrix)
{
int x = this->x;
int y = this->y;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// TODO painting first the background white and then the contents
// means that the contents also blend with the background, I guess
ShaderBinder binder(ShaderTrait::UniformColor);
binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, projectionMatrix);
if (m_showGraph) {
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
vbo->reset();
QColor color(255, 255, 255);
color.setAlphaF(alpha);
vbo->setColor(color);
QVector<float> verts;
verts.reserve(12);
verts << x + 2 * NUM_PAINTS + FPS_WIDTH << y;
verts << x << y;
verts << x << y + MAX_TIME;
verts << x << y + MAX_TIME;
verts << x + 2 * NUM_PAINTS + FPS_WIDTH << y + MAX_TIME;
verts << x + 2 * NUM_PAINTS + FPS_WIDTH << y;
vbo->setData(6, 2, verts.constData(), nullptr);
vbo->render(GL_TRIANGLES);
y += MAX_TIME; // paint up from the bottom
color.setRed(0);
color.setGreen(0);
vbo->setColor(color);
verts.clear();
verts << x + FPS_WIDTH << y - fps;
verts << x << y - fps;
verts << x << y;
verts << x << y;
verts << x + FPS_WIDTH << y;
verts << x + FPS_WIDTH << y - fps;
vbo->setData(6, 2, verts.constData(), nullptr);
vbo->render(GL_TRIANGLES);
color.setBlue(0);
vbo->setColor(color);
QVector<float> vertices;
for (int i = 10; i < MAX_TIME; i += 10) {
vertices << x << y - i;
vertices << x + FPS_WIDTH << y - i;
}
vbo->setData(vertices.size() / 2, 2, vertices.constData(), nullptr);
vbo->render(GL_LINES);
x += FPS_WIDTH;
// Paint FPS graph
paintFPSGraph(x, y);
x += NUM_PAINTS;
// Paint amount of rendered pixels graph
paintDrawSizeGraph(x, y);
}
// Paint FPS numerical value
if (fpsTextRect.isValid()) {
fpsText.reset(new GLTexture(fpsTextImage(fps)));
fpsText->bind();
ShaderBinder binder(ShaderTrait::MapTexture);
QMatrix4x4 mvp = projectionMatrix;
mvp.translate(fpsTextRect.x(), fpsTextRect.y());
binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
fpsText->render(fpsTextRect);
fpsText->unbind();
effects->addRepaint(fpsTextRect);
}
// Paint paint sizes
glDisable(GL_BLEND);
}
void ShowFpsEffect::paintQPainter(int fps)
{
QPainter *painter = effects->scenePainter();
painter->save();
if (m_showGraph) {
QColor color(255, 255, 255);
color.setAlphaF(alpha);
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
painter->fillRect(x, y, 2 * NUM_PAINTS + FPS_WIDTH, MAX_TIME, color);
color.setRed(0);
color.setGreen(0);
painter->fillRect(x, y + MAX_TIME - fps, FPS_WIDTH, fps, color);
color.setBlue(0);
for (int i = 10; i < MAX_TIME; i += 10) {
painter->setPen(color);
painter->drawLine(x, y + MAX_TIME - i, x + FPS_WIDTH, y + MAX_TIME - i);
}
// Paint FPS graph
paintFPSGraph(x + FPS_WIDTH, y + MAX_TIME - 1);
// Paint amount of rendered pixels graph
paintDrawSizeGraph(x + FPS_WIDTH + NUM_PAINTS, y + MAX_TIME - 1);
}
// Paint FPS numerical value
painter->setPen(Qt::black);
painter->drawText(fpsTextRect, textAlign, QString::number(fps));
painter->restore();
}
void ShowFpsEffect::paintFPSGraph(int x, int y)
{
// Paint FPS graph
QList<int> lines;
lines << 10 << 20 << 50;
QList<int> values;
for (int i = 0;
i < NUM_PAINTS;
++i) {
values.append(paints[(i + paints_pos) % NUM_PAINTS]);
}
paintGraph(x, y, values, lines, true);
}
void ShowFpsEffect::paintDrawSizeGraph(int x, int y)
{
int max_drawsize = 0;
for (int i = 0; i < NUM_PAINTS; i++) {
max_drawsize = qMax(max_drawsize, paint_size[i]);
}
// Log of min/max values shown on graph
const float max_pixels_log = 7.2f;
const float min_pixels_log = 2.0f;
const int minh = 5; // Minimum height of the bar when value > 0
float drawscale = (MAX_TIME - minh) / (max_pixels_log - min_pixels_log);
QList<int> drawlines;
for (int logh = (int)min_pixels_log; logh <= max_pixels_log; logh++) {
drawlines.append((int)((logh - min_pixels_log) * drawscale) + minh);
}
QList<int> drawvalues;
for (int i = 0;
i < NUM_PAINTS;
++i) {
int value = paint_size[(i + paints_pos) % NUM_PAINTS];
int h = 0;
if (value > 0) {
h = (int)((log10((double)value) - min_pixels_log) * drawscale);
h = qMin(qMax(0, h) + minh, MAX_TIME);
}
drawvalues.append(h);
}
paintGraph(x, y, drawvalues, drawlines, false);
}
void ShowFpsEffect::paintGraph(int x, int y, QList<int> values, QList<int> lines, bool colorize)