Revamp perf test

Fixes: QTBUG-121316
Change-Id: Ia2c77726e45accee59aa06ea1dc82db26ddac0c5
Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
This commit is contained in:
Sakaria Pouke 2024-01-24 13:56:21 +02:00
parent 08a0d6f7ba
commit 2542cd0616
6 changed files with 1182 additions and 192 deletions

View File

@ -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"

View File

@ -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();
for (uint i = 0; i < count; i++) {
ptrToDataArray->setPosition(QVector3D(QRandomGenerator::global()->generateDouble(),
QRandomGenerator::global()->generateDouble(),
QRandomGenerator::global()->generateDouble()));
ptrToDataArray++;
if (m_surfaceResetArray) {
for (auto row : *m_surfaceResetArray)
row->clear();
m_surfaceResetArray->clear();
}
series->dataProxy()->resetArray(dataArray);
}
void DataGenerator::add(QScatter3DSeries *series, uint count)
{
QScatterDataArray appendArray;
appendArray.resize(count);
m_surfaceResetArray = new QSurfaceDataArray;
m_surfaceResetArray->reserve(count);
for (uint i = 0; i < count; i++) {
appendArray[i].setPosition(QVector3D(QRandomGenerator::global()->generateDouble(),
QRandomGenerator::global()->generateDouble(),
QRandomGenerator::global()->generateDouble()));
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()->addItems(appendArray);
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::writeLine(int itemCount, float fps)
void DataGenerator::generateScatterData(QScatter3DSeries *series, uint 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++) {
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()->resetArray(m_surfaceResetArray);
index++;
if (index >= m_cacheCount)
index = 0;
}
void DataGenerator::updateScatterData(QScatter3DSeries *series)
{
if (!series || series->dataProxy()->array()->count() == 0)
return;
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);
out << fpsString;
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;
}
}
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);
}
}
}
}

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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()
}
}
}
}

View File

@ -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"
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";
}
}
}
ColumnLayout {
id: buttonPanel
anchors.top: parent.top
width: parent.width
spacing: 10
visible: autoTest.finished
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);
}
}
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));
}
}
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);
}
theme: Theme3D {
type: Theme3D.ThemeRetro
colorStyle: Theme3D.ColorStyleRangeGradient
baseGradients: customGradient
ColorGradient {
id: customGradient
ColorGradientStop { position: 1.0; color: "red" }
ColorGradientStop { position: 0.0; color: "blue" }
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)
}
}
Scatter3DSeries {
id: scatterSeries
mesh: Abstract3DSeries.MeshSphere
}
Component.onCompleted: dataGenerator.generateData(scatterSeries, itemCount);
}
states: [
State {
name: "meshsphere"
StateChangeScript {
name: "doSphere"
script: {
console.log("Do the sphere");
scatterSeries.mesh = Abstract3DSeries.MeshSphere;
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)
}
},
State {
name: "meshcube"
StateChangeScript {
name: "doCube"
script: {
console.log("Do the cube");
scatterSeries.mesh = Abstract3DSeries.MeshCube;
}
}
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])
}
},
State {
name: "meshpyramid"
StateChangeScript {
name: "doPyramid"
script: {
console.log("Do the pyramid");
scatterSeries.mesh = Abstract3DSeries.MeshPyramid;
}
}
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"
}
}
},
State {
name: "meshpoint"
StateChangeScript {
name: "doPoint"
script: {
console.log("Do the point");
scatterSeries.mesh = Abstract3DSeries.MeshPoint;
}
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
}
}
}
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()
}
}
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
}
}
}
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.ThemeQt
colorStyle: Theme3D.ColorStyleRangeGradient
baseGradients: surfaceGradient
ColorGradient {
id: surfaceGradient
ColorGradientStop { position: 1.0; color: "red" }
ColorGradientStop { position: 0.0; color: "blue" }
}
}
Surface3DSeries {
id: surfaceSeries
dataProxy.onArrayReset: tests.dataPoints = dataProxy.columnCount * dataProxy.rowCount
}
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
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" }
}
}
Scatter3DSeries {
id: scatterSeries
dataProxy.onArrayReset: tests.dataPoints = dataProxy.itemCount
itemSize: 0.1
}
onCurrentFpsChanged: {
tests.currentFps = currentFps
}
}
}
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
}
}
}
}
}