diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/manual/qmlperf/CMakeLists.txt | 3 | ||||
-rw-r--r-- | tests/manual/qmlperf/datagenerator.cpp | 325 | ||||
-rw-r--r-- | tests/manual/qmlperf/datagenerator.h | 44 | ||||
-rw-r--r-- | tests/manual/qmlperf/qml/qmlperf/AutoTest.qml | 219 | ||||
-rw-r--r-- | tests/manual/qmlperf/qml/qmlperf/Tests.qml | 184 | ||||
-rw-r--r-- | tests/manual/qmlperf/qml/qmlperf/main.qml | 559 |
6 files changed, 1162 insertions, 172 deletions
diff --git a/tests/manual/qmlperf/CMakeLists.txt b/tests/manual/qmlperf/CMakeLists.txt index 31235e32..b2a90d5f 100644 --- a/tests/manual/qmlperf/CMakeLists.txt +++ b/tests/manual/qmlperf/CMakeLists.txt @@ -9,6 +9,7 @@ qt_internal_add_manual_test(qmlperf datagenerator.cpp datagenerator.h main.cpp ) + target_link_libraries(qmlperf PUBLIC Qt::Gui Qt::Qml @@ -18,6 +19,8 @@ target_link_libraries(qmlperf PUBLIC set(qmlperf_resource_files "qml/qmlperf/main.qml" + "qml/qmlperf/Tests.qml" + "qml/qmlperf/AutoTest.qml" ) qt_internal_add_resource(qmlperf "qmlperf" diff --git a/tests/manual/qmlperf/datagenerator.cpp b/tests/manual/qmlperf/datagenerator.cpp index df00d483..a092e749 100644 --- a/tests/manual/qmlperf/datagenerator.cpp +++ b/tests/manual/qmlperf/datagenerator.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "datagenerator.h" @@ -7,62 +7,329 @@ Q_DECLARE_METATYPE(QScatter3DSeries *) -DataGenerator::DataGenerator(QObject *parent) : - QObject(parent) +DataGenerator::DataGenerator(QObject *parent) + : QObject(parent) { qRegisterMetaType<QScatter3DSeries *>(); - - m_file = new QFile("results.txt"); - if (!m_file->open(QIODevice::WriteOnly | QIODevice::Text)) { - delete m_file; - m_file = 0; - } + qRegisterMetaType<QSurface3DSeries *>(); + qRegisterMetaType<QBar3DSeries *>(); + setFilePath(QUrl()); } DataGenerator::~DataGenerator() { m_file->close(); + m_csv->close(); delete m_file; + delete m_csv; } -void DataGenerator::generateData(QScatter3DSeries *series, uint count) +void DataGenerator::generateSurfaceData(QSurface3DSeries *series, uint count) { - QScatterDataArray *dataArray = new QScatterDataArray; - dataArray->resize(count); - QScatterDataItem *ptrToDataArray = &dataArray->first(); + if (m_surfaceResetArray) { + for (auto row : *m_surfaceResetArray) + row->clear(); + m_surfaceResetArray->clear(); + } + m_surfaceResetArray = new QSurfaceDataArray; + m_surfaceResetArray->reserve(count); for (uint i = 0; i < count; i++) { - ptrToDataArray->setPosition(QVector3D(QRandomGenerator::global()->generateDouble(), - QRandomGenerator::global()->generateDouble(), - QRandomGenerator::global()->generateDouble())); - ptrToDataArray++; + m_surfaceResetArray->append(new QSurfaceDataRow()); + QSurfaceDataRow *row = m_surfaceResetArray->at(i); + row->reserve(count); + for (uint j = 0; j < count; j++) { + float x = float(j) / float(count); + float z = float(i) / float(count); + row->append( + QSurfaceDataItem(QVector3D(x, QRandomGenerator::global()->generateDouble(), z))); + } } - series->dataProxy()->resetArray(dataArray); + writeLine(QString("Surface Graph: setting %1 points").arg(count * count)); + + m_timer.start(); + series->dataProxy()->resetArray(m_surfaceResetArray); + long long nsecs = m_timer.nsecsElapsed(); + + writeLine(QString("Took %1 nanoseconds").arg(nsecs)); + + populateSurfaceCaches(count); } -void DataGenerator::add(QScatter3DSeries *series, uint count) +void DataGenerator::generateScatterData(QScatter3DSeries *series, uint count) { - QScatterDataArray appendArray; - appendArray.resize(count); + if (m_scatterResetArray) { + m_scatterResetArray->clear(); + } + m_scatterResetArray = new QScatterDataArray; + m_scatterResetArray->reserve(count * count); + for (uint i = 0; i < count * count; i++) { + m_scatterResetArray->append( + QScatterDataItem(QVector3D(QRandomGenerator::global()->generateDouble() * 2 - 1, + QRandomGenerator::global()->generateDouble() * 2 - 1, + QRandomGenerator::global()->generateDouble() * 2 - 1))); + } + writeLine(QString("Scatter Graph: setting %1 points").arg(count * count)); + + m_timer.start(); + series->dataProxy()->resetArray(m_scatterResetArray); + long long nsecs = m_timer.nsecsElapsed(); + + writeLine(QString("Took %1 nanoseconds").arg(nsecs)); + + populateScatterCaches(count); +} + +void DataGenerator::generateBarData(QBar3DSeries *series, uint count) +{ + if (m_barResetArray) { + for (auto row : *m_barResetArray) + row->clear(); + m_barResetArray->clear(); + } + + m_barResetArray = new QBarDataArray; + m_barResetArray->reserve(count); for (uint i = 0; i < count; i++) { - appendArray[i].setPosition(QVector3D(QRandomGenerator::global()->generateDouble(), - QRandomGenerator::global()->generateDouble(), - QRandomGenerator::global()->generateDouble())); + m_barResetArray->append(new QBarDataRow()); + QBarDataRow *row = m_barResetArray->at(i); + row->reserve(count); + for (uint j = 0; j < count; j++) { + row->append(QBarDataItem(QRandomGenerator::global()->generateDouble())); + } + } + + writeLine(QString("Bar Graph: setting %1 points").arg(count * count)); + + m_timer.start(); + series->dataProxy()->resetArray(m_barResetArray); + long long nsecs = m_timer.nsecsElapsed(); + + writeLine(QString("Took %1 nanoseconds").arg(nsecs)); + + populateBarChaches(count); +} + +void DataGenerator::updateSurfaceData(QSurface3DSeries *series) +{ + if (!series || series->dataProxy()->columnCount() == 0 || series->dataProxy()->rowCount() == 0) + return; + + static int index = 0; + const QSurfaceDataArray &cache = m_surfaceCaches.at(index); + const int rows = cache.count(); + for (int i = 0; i < rows; i++) { + const QSurfaceDataRow &sourceRow = *(cache.at(i)); + QSurfaceDataRow &row = *(*m_surfaceResetArray)[i]; + std::copy(sourceRow.cbegin(), sourceRow.cend(), row.begin()); } - series->dataProxy()->addItems(appendArray); + series->dataProxy()->resetArray(m_surfaceResetArray); + + index++; + if (index >= m_cacheCount) + index = 0; } +void DataGenerator::updateScatterData(QScatter3DSeries *series) +{ + if (!series || series->dataProxy()->array()->count() == 0) + return; -void DataGenerator::writeLine(int itemCount, float fps) + static int index = 0; + const QScatterDataArray &cache = m_scatterCaches.at(index); + + const int count = cache.count(); + for (int i = 0; i < count; i++) { + (*m_scatterResetArray)[i].setPosition(cache.at(i).position()); + } + + series->dataProxy()->resetArray(m_scatterResetArray); + index++; + if (index >= m_cacheCount) + index = 0; +} +void DataGenerator::updateBarData(QBar3DSeries *series) +{ + static int index = 0; + const int rows = series->dataProxy()->rowCount(); + + const QBarDataArray &cache = m_barCaches.at(index); + for (int i = 0; i < rows; i++) { + const QBarDataRow &sourceRow = *(cache.at(i)); + QBarDataRow &row = *(*m_barResetArray)[i]; + std::copy(sourceRow.cbegin(), sourceRow.cend(), row.begin()); + } + + series->dataProxy()->resetArray(m_barResetArray); + index++; + if (index >= m_cacheCount) + index = 0; +} + +void DataGenerator::setFilePath(const QUrl &path) +{ + if (m_file) { + m_file->close(); + delete m_file; + } + if (m_csv) { + m_csv->close(); + delete m_csv; + } + + QString pathString = path.toLocalFile(); + if (!pathString.isEmpty()) { + pathString += "/"; + qDebug() << "Set path to : " << pathString; + emit onMessage("Set path to " + pathString); + } + + m_file = new QFile(pathString + "results.txt"); + if (!m_file->open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << m_file->errorString(); + delete m_file; + m_file = 0; + } + m_csv = new QFile(pathString + "measurements.csv"); + if (!m_csv->open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << m_file->errorString(); + delete m_csv; + m_csv = 0; + } else { + QTextStream out(m_csv); + QString headers = QString("Graph type,Number of points,Optimization,MSAA " + "Samples,Shadow Quality,Init Time,Average FPS"); + out << headers << Qt::endl; + } +} + +void DataGenerator::writeLine(const QString &line) { if (m_file) { QTextStream out(m_file); - QString fpsFormatString(QStringLiteral("%1 %2\n")); - QString fpsString = fpsFormatString.arg(itemCount).arg(fps); + qDebug() << line << Qt::endl; + out << line << Qt::endl; + emit onMessage(line); + } +} + +void DataGenerator::writeCSV(const QString &line) +{ + if (m_csv) { + qDebug() << line << Qt::endl; + QTextStream out(m_csv); + out << line << Qt::endl; + } +} - out << fpsString; +void DataGenerator::populateSurfaceCaches(int sideLength) +{ + for (int i = 0; i < m_surfaceCaches.size(); i++) { + QSurfaceDataArray &array = m_surfaceCaches[i]; + array.clear(); + } + m_surfaceCaches.clear(); + + // Re-create the cache array + m_surfaceCaches.resize(m_cacheCount); + for (int i = 0; i < m_cacheCount; i++) { + QSurfaceDataArray &array = m_surfaceCaches[i]; + array.reserve(sideLength); + for (int j = 0; j < sideLength; j++) { + array.append(new QSurfaceDataRow(sideLength)); + } + } + + //Populate caches + for (int i = 0; i < m_cacheCount; i++) { + QSurfaceDataArray &cache = m_surfaceCaches[i]; + float timeStep = float(i) / float(m_cacheCount); + for (int j = 0; j < sideLength; j++) { + QSurfaceDataRow &row = *(cache[j]); + for (int k = 0; k < sideLength; k++) { + float x = float(k) / float(sideLength); + float z = float(j) / float(sideLength); + float y = qSin(2 * M_PI * (x + z + (timeStep))) * 0.5 + 0.5; + row[k] = QSurfaceDataItem(QVector3D(x, y, z)); + } + } + } +} + +void DataGenerator::populateScatterCaches(int sideLength) +{ + for (int i = 0; i < m_scatterCaches.size(); i++) { + QScatterDataArray &array = m_scatterCaches[i]; + array.clear(); + } + m_scatterCaches.clear(); + + // Re-create the cache array + const int count = sideLength * sideLength; + m_scatterCaches.resize(m_cacheCount); + for (int i = 0; i < m_cacheCount; i++) { + QScatterDataArray &array = m_scatterCaches[i]; + array.reserve(count); + for (int j = 0; j < count; j++) { + array.append(QScatterDataItem()); + } + } + + //Populate caches + for (int i = 0; i < m_cacheCount; i++) { + // time loops from 0 to 4 + float t = (float(i) * 4) / float(m_cacheCount); + QScatterDataArray &cache = m_scatterCaches[i]; + for (int j = 0; j < sideLength; j++) { + for (int k = 0; k < sideLength; k++) { + float u = (float(j) / float(sideLength)) * 2 - 1; + float v = (float(k) / float(sideLength)) * 2 - 1; + + //create a torus + float r1 = 0.7f + 0.1f * qSin(M_PI * (6.0f * u + 0.5f * t)); + float r2 = 0.15f + 0.05f * qSin(M_PI * (8.0f * u + 4.0f * v + 2.0f * t)); + float s = r1 + r2 * qCos(M_PI * v); + + float x = s * qSin(M_PI * u); + float y = r2 * qSin(M_PI * v); + float z = s * qCos(M_PI * u); + cache[sideLength * j + k].setPosition(QVector3D(x, y, z)); + } + } + } +} + +void DataGenerator::populateBarChaches(int sideLength) +{ + for (int i = 0; i < m_barCaches.size(); i++) { + QBarDataArray &array = m_barCaches[i]; + array.clear(); + } + m_barCaches.clear(); + + // Re-create the cache array + m_barCaches.resize(m_cacheCount); + for (int i = 0; i < m_cacheCount; i++) { + QBarDataArray &array = m_barCaches[i]; + array.reserve(sideLength); + for (int j = 0; j < sideLength; j++) { + array.append(new QBarDataRow(sideLength)); + } + } + for (int i = 0; i < m_cacheCount; i++) { + QBarDataArray &cache = m_barCaches[i]; + float timeStep = float(i) / float(m_cacheCount); + for (int j = 0; j < sideLength; j++) { + QBarDataRow &row = *(cache[j]); + for (int k = 0; k < sideLength; k++) { + float x = float(j) / float(sideLength); + float z = float(k) / float(sideLength); + float y = qSin(2 * M_PI * (x + z + (timeStep))) * 0.5 + 0.5; + row[k] = QBarDataItem(y); + } + } } } diff --git a/tests/manual/qmlperf/datagenerator.h b/tests/manual/qmlperf/datagenerator.h index 99a9dbf7..5e8fe959 100644 --- a/tests/manual/qmlperf/datagenerator.h +++ b/tests/manual/qmlperf/datagenerator.h @@ -1,27 +1,53 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef DATAGENERATOR_H #define DATAGENERATOR_H -#include <QtDataVisualization/QScatter3DSeries> #include <QtCore/QFile> +#include <QtDataVisualization> -class DataGenerator : public QObject +class DataGenerator : public QObject { Q_OBJECT public: DataGenerator(QObject *parent = 0); - virtual ~DataGenerator(); + ~DataGenerator() override; public Q_SLOTS: - void generateData(QScatter3DSeries *series, uint count); - void add(QScatter3DSeries *series, uint count); - void writeLine(int itemCount, float fps); + void generateSurfaceData(QSurface3DSeries *series, uint count); + void generateScatterData(QScatter3DSeries *series, uint count); + void generateBarData(QBar3DSeries *series, uint count); + + void updateScatterData(QScatter3DSeries *series); + void updateSurfaceData(QSurface3DSeries *series); + void updateBarData(QBar3DSeries *series); + + void setFilePath(const QUrl &path); + void writeLine(const QString &line); + void writeCSV(const QString &line); + +Q_SIGNALS: + void onMessage(const QString &message); + void onCaptureInit(long long nanoseconds); private: - QScatter3DSeries m_series; - QFile *m_file; + QFile *m_file = nullptr; + QFile *m_csv = nullptr; + QElapsedTimer m_timer; + int m_cacheCount = 60; + + QList<QSurfaceDataArray> m_surfaceCaches; + QList<QScatterDataArray> m_scatterCaches; + QList<QBarDataArray> m_barCaches; + + QSurfaceDataArray *m_surfaceResetArray = nullptr; + QScatterDataArray *m_scatterResetArray = nullptr; + QBarDataArray *m_barResetArray = nullptr; + + void populateSurfaceCaches(int sideLength); + void populateScatterCaches(int sideLength); + void populateBarChaches(int sideLength); }; #endif // DATAGENERATOR_H diff --git a/tests/manual/qmlperf/qml/qmlperf/AutoTest.qml b/tests/manual/qmlperf/qml/qmlperf/AutoTest.qml new file mode 100644 index 00000000..d860b99a --- /dev/null +++ b/tests/manual/qmlperf/qml/qmlperf/AutoTest.qml @@ -0,0 +1,219 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtDataVisualization +import "." + +Item { + + height: 150 + + property list<string> graphTypes: ["Surface", "Scatter", "Bars"] + property int currentGraph: 0 + + property list<int>dataPoints: [10, 100, 300, 500] + property int currentPoints: 0 + + property list<string> optimization: ["Default", "Static"] + property int currentOptimization: 0 + + property list<int> sampleCounts: [0,2,4,8] + property int currentSamples: 0 + + + property int shadowQuality + + property bool finished: true + + property int initTime: 0 + + function initAutoTest() { + //go through each graph iterating over all the variables + liveDataCB.checked = true + rotateCB.checked = true + + tests.onTestFinished.connect(test) + dataGenerator.onCaptureInit.connect(setInitTime) + + finished = false + + currentGraph = 0 + currentPoints = 0 + currentOptimization = 0 + currentSamples= 0 + shadowQuality = 0 + + setParameters() + tests.startTest() + } + + function test() { + //write previous test results + var averageFps = tests.averageFps + // graph type, num points, optimization, msaa, init time, averagefps + var csvLine = graphTypes[currentGraph] + "," + + (dataPoints[currentPoints] * dataPoints[currentPoints])+ "," + + optimization[currentOptimization] + "," + + sampleCounts[currentSamples] + "," + + shadowQuality + "," + + initTime + "," + + averageFps + + dataGenerator.writeCSV(csvLine) + increment() + setParameters() + if (!finished) { + tests.startTest() + } else { + tests.onTestFinished.disconnect(test) + dataGenerator.onCaptureInit.disconnect(setInitTime) + } + } + + function increment() { + if (varyShadow.checked) { + if (shadowQuality < 6) { + shadowQuality++ + return + } + shadowQuality = 0 + } + + if (varySamples.checked) { + if (currentSamples < sampleCounts.length -1) { + currentSamples ++ + return + } + currentSamples = 0 + } + + if (varyOptimization.checked) { + if (currentOptimization < optimization.length -1 + && tabBar.currentIndex !== 0) { + currentOptimization++ + return + } + currentOptimization = 0 + } + + if (varyPoints.checked) { + if (currentPoints < dataPoints.length -1) { + currentPoints ++ + return + } + currentPoints = 0 + } + + if (varyGraphs.checked) { + if (currentGraph < graphTypes.length - 1) { + currentGraph++ + console.log("Switching to " + graphTypes[currentGraph]) + return + } + currentGraph = 0 + } + + dataGenerator.writeLine("Finished all tests!") + finished = true + } + + + function setParameters() { + if (varyShadow.checked) { + surfaceGraph.shadowQuality = shadowQuality + scatterGraph.shadowQuality = shadowQuality + barGraph.shadowQuality = shadowQuality + } + + if (varySamples.checked) { + surfaceGraph.msaaSamples = sampleCounts[currentSamples] + scatterGraph.msaaSamples = sampleCounts[currentSamples] + barGraph.msaaSamples = sampleCounts[currentSamples] + } + + if (varyOptimization.checked) { + if (optimization[currentOptimization] === "Legacy") { + scatterGraph.optimizationHint = AbstractGraph3D.OptimizationHint.Default + barGraph.optimizationHint = AbstractGraph3D.OptimizationHint.Default + } else { + scatterGraph.optimizationHint = AbstractGraph3D.OptimizationHint.Legacy + barGraph.optimizationHint = AbstractGraph3D.OptimizationHint.Legacy + } + } + + if (varyGraphs.checked) + tabBar.setCurrentIndex(currentGraph) + + if (varyPoints.checked) { + if (tabBar.currentIndex === 0) + dataGenerator.generateSurfaceData(surfaceSeries, dataPoints[currentPoints]) + else if (tabBar.currentIndex === 1) + dataGenerator.generateScatterData(scatterSeries, dataPoints[currentPoints]) + else + dataGenerator.generateBarData(barSeries, dataPoints[currentPoints]) + } + } + + function setInitTime(nsecs) { + initTime = nsecs + } + + Button { + id: autoButton + text: finished? "Auto Test" : "End test" + width: parent.width - 20 + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + height: 50 + onClicked: { + if (finished) { + dataGenerator.writeLine("Testing configurations...") + initAutoTest() + } else { + finished = true + } + + } + } + + GridLayout { + id: autoTestParams + anchors.top: autoButton.bottom + anchors.topMargin: 10 + width: parent.width - 50 + enabled: finished + anchors.horizontalCenter: parent.Center + height: 50 + columns: 2 + CheckBox { + id: varyGraphs + text: qsTr("Vary graphs") + Layout.alignment: Qt.AlignCenter + checked: true + } + CheckBox { + id: varyPoints + text: qsTr("Vary points") + Layout.alignment: Qt.AlignCenter + checked: true + } + CheckBox { + id: varyOptimization + text: qsTr("Vary optimization") + Layout.alignment: Qt.AlignCenter + } + CheckBox { + id: varySamples + text: qsTr("Vary MSAA ") + Layout.alignment: Qt.AlignCenter + } + CheckBox { + id: varyShadow + text: qsTr("Vary Shadow ") + Layout.alignment: Qt.AlignCenter + } + } +} diff --git a/tests/manual/qmlperf/qml/qmlperf/Tests.qml b/tests/manual/qmlperf/qml/qmlperf/Tests.qml new file mode 100644 index 00000000..dc533641 --- /dev/null +++ b/tests/manual/qmlperf/qml/qmlperf/Tests.qml @@ -0,0 +1,184 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtDataVisualization +import "." + +Item { + id: tests + + property alias currentFps: fpsText.fps + property alias dataPoints: dataPointText.dataPoints + property alias buttonVisible: testButton.visible + property real averageFps: 0 + + signal onTestFinished + + property list<real> fpsCounts: [] + Component.onCompleted: { + dataGenerator.onMessage.connect(addLine) + } + + function addLine(line) { + logModel.append({'logText':line}); + } + + + function startTest() { + // logModel.clear() + fpsCounts = [] + dataGenerator.writeLine(" ") + switch (tabBar.currentIndex) { + case 0: + dataGenerator.writeLine("Started surface test with configuration:") + break + case 1: + dataGenerator.writeLine("Started scatter test with configuration:") + break + case 2: + dataGenerator.writeLine("Started bars test with configuration:") + break + default: + break + } + + if (tabBar.currentIndex === 0) { + dataGenerator.writeLine("Shadow Quality: " + surfaceGraph.shadowQuality) + dataGenerator.writeLine("MSAA samples: " + + surfaceGraph.msaaSamples) + } else if (tabBar.currentIndex === 1) { + dataGenerator.writeLine("Shadow Quality: " + scatterGraph.shadowQuality) + var optimizationString = scatterGraph.optimizationHint? "Static" : "Default" + dataGenerator.writeLine("Optimization: " + optimizationString) + dataGenerator.writeLine("MSAA samples: " + + scatterGraph.msaaSamples) + } else { + dataGenerator.writeLine("Shadow Quality: " + scatterGraph.shadowQuality) + optimizationString = barGraph.optimizationHint? "Static" : "Default" + dataGenerator.writeLine("Optimization: " + optimizationString) + dataGenerator.writeLine("MSAA samples: " + + barGraph.msaaSamples) + + } + + testTimer.start() + } + + Button { + id: testButton + text: "Test current" + onClicked: startTest() + height: 50 + width: parent.width - 20 + anchors.horizontalCenter: parent.horizontalCenter + } + + + ColumnLayout { + id: statsPanel + anchors.top: testButton.bottom + anchors.topMargin: 20 + width: parent.width + Text { + id: statsBanner + text: "Statistics" + font.bold: true + font.pixelSize: 16 + Layout.fillWidth: true + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + Text { + id: fpsText + property real fps: 0 + text: qsTr("FPS: %1").arg(fps) + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + + } + + Text { + id: dataPointText + property int dataPoints: 0 + text : qsTr("Data Points: %1").arg(dataPoints) + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + } + + Text { + id: logBanner + text: "Log" + font.bold: true + font.pixelSize: 16 + Layout.fillWidth: true + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + + Rectangle { + id: logBackground + Layout.fillWidth: true + Layout.preferredHeight: 170 + Layout.margins: 10 + color: "forestgreen" + ListView { + id: logView + anchors.fill: parent + highlightFollowsCurrentItem: true + clip: true + delegate: Text { + text: logText + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + width: logView.width + wrapMode: Text.Wrap + } + + model: ListModel { + id: logModel + } + + onCountChanged: { + logView.currentIndex = count - 1 + } + } + } + } + + Timer { + id: testTimer + interval: 1000 + repeat: true + onTriggered: { + var fps = 0 + if (tabBar.currentIndex === 0) + fps = surfaceGraph.currentFps + else if (tabBar.currentIndex === 1) + fps = scatterGraph.currentFps + else + fps = barGraph.currentFps + + if (fps != -1) { + fpsCounts.push(fps) + dataGenerator.writeLine("FPS: " + fps) + } + else { + dataGenerator.writeLine("Invalid fps reading") + } + + if (fpsCounts.length >= 5) { + var sum = 0 + fpsCounts.forEach((element) => sum+=element); + averageFps = sum / fpsCounts.length + dataGenerator.writeLine("Average FPS: " + averageFps) + testTimer.stop() + onTestFinished() + } + } + } +} diff --git a/tests/manual/qmlperf/qml/qmlperf/main.qml b/tests/manual/qmlperf/qml/qmlperf/main.qml index 4562e538..ce282e87 100644 --- a/tests/manual/qmlperf/qml/qmlperf/main.qml +++ b/tests/manual/qmlperf/qml/qmlperf/main.qml @@ -1,176 +1,467 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +import QtCore import QtQuick import QtQuick.Layouts import QtQuick.Controls -import QtDataVisualization 1.2 +import QtQuick3D.Helpers +import QtQuick.Dialogs +import QtDataVisualization import "." Item { - id: mainview + id: mainView width: 1280 height: 1024 - property var itemCount: 1000.0 - property var addItems: 500.0 - - Button { - id: changeButton - width: parent.width / 7 - height: 50 + TabBar { + id: tabBar + anchors.top: parent.top + anchors.right: panels.left anchors.left: parent.left - enabled: true - text: "Change" - onClicked: { - console.log("changeButton clicked"); - if (graphView.state == "meshsphere") { - graphView.state = "meshcube" - } else if (graphView.state == "meshcube") { - graphView.state = "meshpyramid" - } else if (graphView.state == "meshpyramid") { - graphView.state = "meshpoint" - } else if (graphView.state == "meshpoint") { - graphView.state = "meshsphere" - } + contentHeight: 50 + TabButton { + text: qsTr("Surface") + } + TabButton { + text: qsTr("Scatter") + } + TabButton { + text: qsTr("Bars") } } - Text { - id: fpsText - text: "Reading" - width: (parent.width / 7) * 3 - height: 50 - anchors.left: changeButton.right - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } + Rectangle { + id: panels + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + width: parent.width / 5 + color: "green" + + ColumnLayout { + id: buttonPanel + anchors.top: parent.top + width: parent.width + spacing: 10 + visible: autoTest.finished - Button { - id: optimization - width: parent.width / 7 - height: 50 - anchors.left: fpsText.right - enabled: true - text: scatterPlot.optimizationHints === AbstractGraph3D.OptimizationDefault ? "To Static" : "To Default" - onClicked: { - console.log("Optimization"); - if (scatterPlot.optimizationHints === AbstractGraph3D.OptimizationDefault) { - scatterPlot.optimizationHints = AbstractGraph3D.OptimizationStatic; - optimization.text = "To Default"; - } else { - scatterPlot.optimizationHints = AbstractGraph3D.OptimizationDefault; - optimization.text = "To Static"; + Button { + id: shadowToggle + property int shadowQuality: 0 + property string qualityName: "None" + text: qsTr("Shadow Quality : %1").arg(qualityName) + Layout.fillWidth: true + Layout.preferredHeight: 50 + Layout.margins: 10 + Layout.alignment: Qt.AlignCenter + onClicked: { + var nextQuality = (shadowQuality + 1) % 7 + surfaceGraph.shadowQuality = nextQuality + scatterGraph.shadowQuality = nextQuality + barGraph.shadowQuality = nextQuality + shadowQuality = nextQuality + qualityName = barGraph.shadowQuality.toString() + console.log("Set shadow quality to " + qualityName) + } + } + + Button { + id: optimizationToggle + visible: tabBar.currentIndex > 0 + property string optimization: "Default" + text: qsTr("Optimization: %1").arg(optimization) + Layout.fillWidth: true + Layout.preferredHeight: 50 + Layout.margins: 10 + Layout.alignment: Qt.AlignCenter + onClicked: { + if (optimization === "Static") { + scatterGraph.optimizationHints = AbstractGraph3D.OptimizationDefault + barGraph.optimizationHints = AbstractGraph3D.OptimizationDefault + optimization= "Default" + } else { + scatterGraph.optimizationHints = AbstractGraph3D.OptimizationStatic + barGraph.optimizationHints = AbstractGraph3D.OptimizationStatic + optimization = "Static" + } + console.log("Set optimization to " + optimization) + } + } + + Button { + id: samplesButton + property list<int> samples: [0,2,4,8] + property int index: 0 + text: qsTr("MSAA samples: %1").arg(samples[index]) + + Layout.fillWidth: true + Layout.preferredHeight: 50 + Layout.margins: 10 + Layout.alignment: Qt.AlignCenter + onClicked: { + index = (index + 1) % 4 + surfaceGraph.msaaSamples = samples[index] + scatterGraph.msaaSamples = samples[index] + barGraph.msaaSamples = samples[index] + console.log("Set msaa samples to " + samples[index]) + } + } + + Button { + id: scatterMesh + visible: tabBar.currentIndex === 1 + property string mesh: "Sphere" + text: qsTr("Mesh: %1").arg(mesh) + Layout.fillWidth: true + Layout.preferredHeight: 50 + Layout.margins: 10 + Layout.alignment: Qt.AlignCenter + onClicked: { + if (mesh === "Sphere") { + scatterSeries.mesh = Abstract3DSeries.MeshCube + mesh = "Cube" + } else if (mesh === "Cube") { + scatterSeries.mesh = Abstract3DSeries.MeshPyramid + mesh = "Pyramid" + } else if (mesh === "Pyramid") { + scatterSeries.mesh = Abstract3DSeries.MeshPoint + mesh = "Point" + } else { + scatterSeries.mesh = Abstract3DSeries.MeshSphere + mesh = "Sphere" + } + } + } + + + Button { + id: surfaceShadingToggle + visible: tabBar.currentIndex <= 2 + text: qsTr("Flat shading: %1").arg(surfaceSeries.flatShadingEnabled.toString()) + Layout.fillWidth: true + Layout.preferredHeight: 50 + Layout.margins: 10 + Layout.alignment: Qt.AlignCenter + onClicked: { + surfaceSeries.flatShadingEnabled = + !surfaceSeries.flatShadingEnabled + scatterSeries.meshSmooth = !scatterSeries.meshSmooth + } + } + + Button { + id: gridToggle + visible: tabBar.currentIndex === 0 + property bool gridEnabled + text: qsTr("Show grid: %1").arg(gridEnabled.toString()) + Layout.fillWidth: true + Layout.preferredHeight: 50 + Layout.margins: 10 + Layout.alignment: Qt.AlignCenter + onClicked: { + if (surfaceSeries.drawMode & Surface3DSeries.DrawWireframe) + surfaceSeries.drawMode &= ~Surface3DSeries.DrawWireframe; + else + surfaceSeries.drawMode |= Surface3DSeries.DrawWireframe; + + gridEnabled = surfaceSeries.drawMode & Surface3DSeries.DrawWireframe + } + + } + + ColumnLayout { + id: pointSetContainer + Layout.fillWidth: true + Layout.preferredHeight: 50 + Layout.margins: 10 + Layout.alignment: Qt.AlignCenter + + + Text { + id: spinboxTitle + text: "Side length" + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + } + + SpinBox { + id: sizeField + from: 2 + to: 500 + stepSize: 20 + value: 10 + editable: true + Layout.preferredWidth: parent.width + } + + Button { + id: pointSetButton + text: qsTr("Place points"); + Layout.preferredWidth: parent.width + Layout.bottomMargin: 10 + onClicked: { + switch (tabBar.currentIndex) { + case 0: + dataGenerator.generateSurfaceData(surfaceSeries, sizeField.value) + break; + case 1: + dataGenerator.generateScatterData(scatterSeries, sizeField.value) + break; + case 2: + dataGenerator.generateBarData(barSeries, sizeField.value) + break; + default: + break; + } + } + } + } + } + + GridLayout { + id: checkBoxPanel + anchors.top: buttonPanel.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 10 + visible: autoTest.finished + columns: 2 + + CheckBox { + id: liveDataCB + text: qsTr("Live Data") + Layout.fillWidth: true + Layout.margins: 10 + Layout.alignment: Qt.AlignCenter + } + CheckBox { + id: rotateCB + text: qsTr("Rotation") + Layout.fillWidth: true + Layout.margins: 10 + Layout.alignment: Qt.AlignCenter + } + + ColumnLayout { + id: freqContainer + Layout.columnSpan: 2 + Text { + text: qsTr("Frequency: %1").arg(frequencySlider.value) + Layout.fillWidth: true + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + Slider { + id: frequencySlider + from: 1 + to: 60 + stepSize: 1 + value: 30 + snapMode: Slider.SnapAlways + Layout.alignment: Qt.AlignCenter + } } } - } - Button { - id: itemAdd - width: parent.width / 7 - height: 50 - anchors.left: optimization.right - enabled: true - text: "Add" - onClicked: { - itemCount = itemCount + addItems; - dataGenerator.add(scatterSeries, addItems); + AutoTest { + id: autoTest + width: parent.width + anchors.top: checkBoxPanel.bottom + anchors.left: parent.left + anchors.topMargin: 10 + } + + Tests { + id: tests + width: parent.width + anchors.top: autoTest.bottom + anchors.left: parent.left + buttonVisible: autoTest.finished + } + + Button { + id: pathButton + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + text: "Set logfile path" + anchors.margins: 10 + onClicked: folderDialog.open() } } - Button { - id: writeLine - width: parent.width / 7 - height: 50 - anchors.left: itemAdd.right - enabled: true - text: "Write" - onClicked: { - dataGenerator.writeLine(itemCount, scatterPlot.currentFps.toFixed(1)); + FolderDialog { + id: folderDialog + currentFolder: StandardPaths.standardLocations(StandardPaths.DocumentsLocation)[0] + onAccepted: dataGenerator.setFilePath(currentFolder) + } + + Timer { + id: rotationTimer + interval: 15 + running: rotateCB.checked + repeat: true + onTriggered: { + switch (tabBar.currentIndex) { + case 0: + if (++surfaceGraph.scene.activeCamera.xRotation == 360) + surfaceGraph.cameraXRotation = 0; + break + case 1: + if (++scatterGraph.scene.activeCamera.xRotation == 360) + scatterGraph.cameraXRotation = 0; + break + case 2: + if (++barGraph.scene.activeCamera.xRotation == 360) + barGraph.cameraXRotation = 0; + break + } } } - Item { - id: graphView - width: mainview.width - height: mainview.height - anchors.top: changeButton.bottom - anchors.left: mainview.left - state: "meshsphere" - - Scatter3D { - id: scatterPlot - width: graphView.width - height: graphView.height - shadowQuality: AbstractGraph3D.ShadowQualityNone - optimizationHints: AbstractGraph3D.OptimizationDefault - scene.activeCamera.yRotation: 45.0 - measureFps: true - onCurrentFpsChanged: { - fpsText.text = itemCount + " : " + scatterPlot.currentFps.toFixed(1); + Timer { + id: updateTimer + interval: 1000 / frequencySlider.value + running: liveDataCB.checked + repeat: true + onTriggered: { + switch (tabBar.currentIndex) { + case 0: + dataGenerator.updateSurfaceData(surfaceSeries) + break + case 1: + dataGenerator.updateScatterData(scatterSeries) + break + case 2: + dataGenerator.updateBarData(barSeries) + break } + } + } + + + StackLayout { + anchors.top: tabBar.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: panels.left + currentIndex: tabBar.currentIndex + + Item { + id: surfaceTab + Surface3D { + id: surfaceGraph + anchors.fill: parent + shadowQuality: AbstractGraph3D.ShadowQualityNone + scene.activeCamera.yRotation: 45.0 + measureFps: true + + axisX.min: 0 + axisX.max: 1 + axisY.min: 0 + axisY.max: 1 + horizontalAspectRatio: 1.0 + - theme: Theme3D { - type: Theme3D.ThemeRetro - colorStyle: Theme3D.ColorStyleRangeGradient - baseGradients: customGradient + theme : Theme3D { + type: Theme3D.ThemeQt + colorStyle: Theme3D.ColorStyleRangeGradient + baseGradients: surfaceGradient - ColorGradient { - id: customGradient - ColorGradientStop { position: 1.0; color: "red" } - ColorGradientStop { position: 0.0; color: "blue" } + ColorGradient { + id: surfaceGradient + ColorGradientStop { position: 1.0; color: "red" } + ColorGradientStop { position: 0.0; color: "blue" } + } } - } - Scatter3DSeries { - id: scatterSeries - mesh: Abstract3DSeries.MeshSphere - } + Surface3DSeries { + id: surfaceSeries + dataProxy.onArrayReset: tests.dataPoints = dataProxy.columnCount * dataProxy.rowCount + } - Component.onCompleted: dataGenerator.generateData(scatterSeries, itemCount); + onCurrentFpsChanged: { + tests.currentFps = currentFps + } + } } + Item { + id: scatterTab + Scatter3D { + id: scatterGraph + anchors.fill: parent + shadowQuality: AbstractGraph3D.ShadowQualityNone + scene.activeCamera.yRotation: 45.0 + aspectRatio: 1.0 + horizontalAspectRatio: 1.0 + + axisY.min: -1 + axisY.max: 1 + axisX.min: -1 + axisX.max: 1 + axisZ.min: -1 + axisZ.max: 1 + + measureFps: true + theme : Theme3D { + type: Theme3D.ThemeQt + colorStyle: Theme3D.ColorStyleRangeGradient + baseGradients: scatterGradient - states: [ - State { - name: "meshsphere" - StateChangeScript { - name: "doSphere" - script: { - console.log("Do the sphere"); - scatterSeries.mesh = Abstract3DSeries.MeshSphere; + ColorGradient { + id: scatterGradient + ColorGradientStop { position: 1.0; color: "yellow" } + ColorGradientStop { position: 0.6; color: "red" } + ColorGradientStop { position: 0.4; color: "blue" } + ColorGradientStop { position: 0.0; color: "green" } } } - }, - State { - name: "meshcube" - StateChangeScript { - name: "doCube" - script: { - console.log("Do the cube"); - scatterSeries.mesh = Abstract3DSeries.MeshCube; - } + Scatter3DSeries { + id: scatterSeries + dataProxy.onArrayReset: tests.dataPoints = dataProxy.itemCount + itemSize: 0.1 } - }, - State { - name: "meshpyramid" - StateChangeScript { - name: "doPyramid" - script: { - console.log("Do the pyramid"); - scatterSeries.mesh = Abstract3DSeries.MeshPyramid; - } + onCurrentFpsChanged: { + tests.currentFps = currentFps } - }, - State { - name: "meshpoint" - StateChangeScript { - name: "doPoint" - script: { - console.log("Do the point"); - scatterSeries.mesh = Abstract3DSeries.MeshPoint; + } + } + Item { + id: barTab + Bars3D { + id: barGraph + anchors.fill: parent + shadowQuality: AbstractGraph3D.ShadowQualityNone + scene.activeCamera.yRotation: 45.0 + measureFps: true + valueAxis.min: 0 + valueAxis.max: 1 + + theme : Theme3D { + type: Theme3D.ThemeQt + colorStyle: Theme3D.ColorStyleRangeGradient + baseGradients: barGradient + + ColorGradient{ + id: barGradient + ColorGradientStop { position: 1.0; color: "red" } + ColorGradientStop { position: 0.0; color: "blue" } } } + + Bar3DSeries { + id: barSeries + dataProxy.onArrayReset: tests.dataPoints + = dataProxy.colCount * dataProxy.rowCount + + } + + onCurrentFpsChanged: { + tests.currentFps = currentFps + } } - ] + } } } |