diff --git a/src/plugins/qmlprofiler/CMakeLists.txt b/src/plugins/qmlprofiler/CMakeLists.txt index f0a13b63f0a..937134b7a39 100644 --- a/src/plugins/qmlprofiler/CMakeLists.txt +++ b/src/plugins/qmlprofiler/CMakeLists.txt @@ -69,6 +69,8 @@ set(QMLPROFILER_CPP_SOURCES qmltypedevent.cpp qmltypedevent.h scenegraphtimelinemodel.cpp scenegraphtimelinemodel.h quick3dmodel.cpp quick3dmodel.h + quick3dframeview.cpp quick3dframeview.h + quick3dframemodel.cpp quick3dframemodel.h ) find_package(Qt6 COMPONENTS ShaderTools QUIET) diff --git a/src/plugins/qmlprofiler/qmlprofiler.qbs b/src/plugins/qmlprofiler/qmlprofiler.qbs index 8183b15e678..5cc3604a07b 100644 --- a/src/plugins/qmlprofiler/qmlprofiler.qbs +++ b/src/plugins/qmlprofiler/qmlprofiler.qbs @@ -60,6 +60,8 @@ QtcPlugin { "qmlprofilerviewmanager.cpp", "qmlprofilerviewmanager.h", "qmltypedevent.cpp", "qmltypedevent.h", "quick3dmodel.cpp", "quick3dmodel.h", + "quick3dframeview.cpp", "quick3dframeview.h", + "quick3dframemodel.cpp", "quick3dframemodel.h", "scenegraphtimelinemodel.cpp", "scenegraphtimelinemodel.h", ] } diff --git a/src/plugins/qmlprofiler/qmlprofilereventtypes.h b/src/plugins/qmlprofiler/qmlprofilereventtypes.h index 8ad7e2ab270..4dea9aacc1b 100644 --- a/src/plugins/qmlprofiler/qmlprofilereventtypes.h +++ b/src/plugins/qmlprofiler/qmlprofilereventtypes.h @@ -47,9 +47,10 @@ enum Quick3DEventType { Quick3DParticleUpdate, Quick3DGenerateShader, Quick3DLoadShader, + Quick3DRenderCall, + Quick3DRenderPass, + Quick3DEventData, MaximumQuick3DFrameType, - NumQuick3DRenderThreadFrameTypes = Quick3DParticleUpdate, - NumQuick3DGUIThreadFrameTypes = MaximumQuick3DFrameType - NumQuick3DRenderThreadFrameTypes, }; enum RangeType { diff --git a/src/plugins/qmlprofiler/qmlprofilertraceclient.cpp b/src/plugins/qmlprofiler/qmlprofilertraceclient.cpp index d6560533ba2..833ed319961 100644 --- a/src/plugins/qmlprofiler/qmlprofilertraceclient.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertraceclient.cpp @@ -16,7 +16,7 @@ namespace QmlProfiler { inline auto qHash(const QmlEventType &type) { - return qHash(type.location()) + return qHash(type.location()) ^ qHash(type.data()) ^ (((type.message() << 12) & 0xf000) // 4 bits of message | ((type.rangeType() << 24) & 0xf000000) // 4 bits of rangeType | ((type.detailType() << 28) & 0xf0000000)); // 4 bits of detailType @@ -25,7 +25,8 @@ inline auto qHash(const QmlEventType &type) inline bool operator==(const QmlEventType &type1, const QmlEventType &type2) { return type1.message() == type2.message() && type1.rangeType() == type2.rangeType() - && type1.detailType() == type2.detailType() && type1.location() == type2.location(); + && type1.detailType() == type2.detailType() && type1.location() == type2.location() + && type1.data() == type2.data(); } inline bool operator!=(const QmlEventType &type1, const QmlEventType &type2) diff --git a/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp b/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp index f4b3492d10f..52c0660f26a 100644 --- a/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp @@ -66,15 +66,19 @@ void QmlProfilerViewManager::createViews() prepareEventsView(m_statisticsView); m_flameGraphView = new FlameGraphView(m_profilerModelManager); prepareEventsView(m_flameGraphView); + m_quick3dView = new Quick3DFrameView(m_profilerModelManager); + prepareEventsView(m_quick3dView); QWidget *anchorDock = nullptr; if (m_traceView->isUsable()) { anchorDock = m_traceView; m_perspective->addWindow(m_traceView, Perspective::SplitVertical, nullptr); m_perspective->addWindow(m_flameGraphView, Perspective::AddToTab, m_traceView); + m_perspective->addWindow(m_quick3dView, Perspective::AddToTab, m_flameGraphView); } else { anchorDock = m_flameGraphView; m_perspective->addWindow(m_flameGraphView, Perspective::SplitVertical, nullptr); + m_perspective->addWindow(m_quick3dView, Perspective::AddToTab, m_flameGraphView); } m_perspective->addWindow(m_statisticsView, Perspective::AddToTab, anchorDock); m_perspective->addWindow(anchorDock, Perspective::Raise, nullptr); @@ -87,6 +91,7 @@ QmlProfilerViewManager::~QmlProfilerViewManager() delete m_traceView; delete m_flameGraphView; delete m_statisticsView; + delete m_quick3dView; delete m_perspective; } diff --git a/src/plugins/qmlprofiler/qmlprofilerviewmanager.h b/src/plugins/qmlprofiler/qmlprofilerviewmanager.h index 443a7f11646..1cfbc99c2e8 100644 --- a/src/plugins/qmlprofiler/qmlprofilerviewmanager.h +++ b/src/plugins/qmlprofiler/qmlprofilerviewmanager.h @@ -5,6 +5,7 @@ #include "qmlprofilerstatisticsview.h" #include "qmlprofilertraceview.h" +#include "quick3dframeview.h" #include "flamegraphview.h" namespace Utils { class Perspective; } @@ -25,6 +26,7 @@ public: QmlProfilerTraceView *traceView() const { return m_traceView; } QmlProfilerStatisticsView *statisticsView() const { return m_statisticsView; } FlameGraphView *flameGraphView() const { return m_flameGraphView; } + Quick3DFrameView *quick3dView() const { return m_quick3dView; } Utils::Perspective *perspective() const { return m_perspective; } void clear(); @@ -40,6 +42,7 @@ private: QmlProfilerTraceView *m_traceView = nullptr; QmlProfilerStatisticsView *m_statisticsView = nullptr; FlameGraphView *m_flameGraphView = nullptr; + Quick3DFrameView *m_quick3dView = nullptr; QmlProfilerStateManager *m_profilerState = nullptr; QmlProfilerModelManager *m_profilerModelManager = nullptr; Utils::Perspective *m_perspective = nullptr; diff --git a/src/plugins/qmlprofiler/qmltypedevent.cpp b/src/plugins/qmlprofiler/qmltypedevent.cpp index 88e7b5d64c2..cc25b7afb34 100644 --- a/src/plugins/qmlprofiler/qmltypedevent.cpp +++ b/src/plugins/qmlprofiler/qmltypedevent.cpp @@ -166,13 +166,19 @@ QDataStream &operator>>(QDataStream &stream, QmlTypedEvent &event) case Quick3DEvent: { QVarLengthArray params; - qint64 param; - - while (!stream.atEnd()) { - stream >> param; - params.push_back(param); + qint64 param = 0; + QByteArray str; + if (subtype == Quick3DEventData) { + stream >> str; + } else { + while (!stream.atEnd()) { + stream >> param; + params.push_back(param); + } } + event.type = QmlEventType(static_cast(messageType), UndefinedRangeType, subtype); + event.type.setData(QString::fromUtf8(str)); event.event.setNumbers, qint64>(params); break; } diff --git a/src/plugins/qmlprofiler/quick3dframemodel.cpp b/src/plugins/qmlprofiler/quick3dframemodel.cpp new file mode 100644 index 00000000000..3271f87c45e --- /dev/null +++ b/src/plugins/qmlprofiler/quick3dframemodel.cpp @@ -0,0 +1,528 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include +#include "quick3dframemodel.h" +#include "quick3dmodel.h" +#include "qmlprofilermodelmanager.h" +#include "qmlprofilerconstants.h" + + +namespace QmlProfiler { +namespace Internal { + +Quick3DFrameModel::Quick3DFrameModel(QmlProfilerModelManager *modelManager) + : m_modelManager(modelManager) +{ + m_acceptedDetailTypes << RenderFrame << SynchronizeFrame << PrepareFrame << RenderCall << RenderPass << EventData << TextureLoad << MeshLoad << CustomMeshLoad; + modelManager->registerFeatures(1ULL << ProfileQuick3D, + std::bind(&Quick3DFrameModel::loadEvent, this, + std::placeholders::_1, std::placeholders::_2), + std::bind(&Quick3DFrameModel::beginResetModel, this), + std::bind(&Quick3DFrameModel::finalize, this), + std::bind(&Quick3DFrameModel::clear, this)); +} + +void Quick3DFrameModel::clear() +{ + beginResetModel(); + m_data.clear(); + m_stackBottom = {}; + m_frameTimes.clear(); + m_eventData.clear(); + m_oldEvents = false; + endResetModel(); +} + +int Quick3DFrameModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) + return m_stackBottom.children.size(); + int index = parent.internalId(); + if (index >= 0) { + const Item &i = m_data[index]; + return i.children.size(); + } else { + return m_stackBottom.children.size(); + } +} + +int Quick3DFrameModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return MaxColumnType; +} + +QVariant Quick3DFrameModel::data(const QModelIndex &index, int role) const +{ + const Item &item = m_data[index.internalId()]; + QString strData; + if (m_eventData.contains(item.eventData)) + strData = m_modelManager->eventType(m_eventData[item.eventData]).data(); + QVariant result; + switch (role) { + case Qt::DisplayRole: { + switch (index.column()) { + case Frame: { + switch (item.additionalType) { + case SynchronizeFrame: { + QString data = "Synchronize Frame: "; + if (item.data) { + quint32 w = item.data & 0xffffffff; + quint32 h = item.data >> 32; + data += ", Render target size: " + QString::number(w) + "x" + QString::number(h); + } + result = QVariant::fromValue(data); + } break; + case RenderFrame: { + QString data = "Render Frame: "; + if (item.data) { + quint32 calls = item.data & 0xffffffff; + quint32 passes = item.data >> 32; + data += "Render Calls: " + QString::number(calls) + ", Render Passes: " + QString::number(passes); + } + result = QVariant::fromValue(data); + } break; + case PrepareFrame: { + QString data = "Prepare Frame: "; + if (item.data) { + quint32 w = item.data & 0xffffffff; + quint32 h = item.data >> 32; + data += ", Render target size: " + QString::number(w) + "x" + QString::number(h); + } + result = QVariant::fromValue(data); + } break; + case RenderCall: { + QString data = "Render Call: "; + if (item.data) { + quint32 primitives = item.data & 0xffffffff; + quint32 instances = item.data >> 32; + data += "Primitives: " + QString::number(primitives) + ", Instances: " + QString::number(instances); + } + result = QVariant::fromValue(data); + } break; + case RenderPass: { + QString data = "Render Pass: " + strData; + if (item.data) { + quint32 w = item.data & 0xffffffff; + quint32 h = item.data >> 32; + data += ", Render target size: " + QString::number(w) + "x" + QString::number(h); + } + result = QVariant::fromValue(data); + } break; + case FrameGroup: { + QString data = "Frame " + QString::number(item.data); + result = QVariant::fromValue(data); + } break; + case TextureLoad: { + QString data = "Texture Load " + strData; + result = QVariant::fromValue(data); + } break; + case MeshLoad: { + QString data = "Mesh Load " + strData; + result = QVariant::fromValue(data); + } break; + case CustomMeshLoad: { + QString data = "Custom Mesh Load " + strData; + result = QVariant::fromValue(data); + } break; + case SubData: { + if (strData.contains(QLatin1String("Material"))) + strData = QLatin1String("Material: ") + strData; + else if (strData.contains(QLatin1String("Model"))) + strData = QLatin1String("Model: ") + strData; + else if (strData.contains(QLatin1String("Particle"))) + strData = QLatin1String("Particle: ") + strData; + else + strData = QLatin1String("Object: ") + strData; + result = QVariant::fromValue(strData); + } break; + } + } break; + case Timestamp: { + QString data = Timeline::formatTime(item.begin); + result = QVariant::fromValue(data); + } break; + case Duration: { + QString data = Timeline::formatTime(item.end - item.begin); + result = QVariant::fromValue(data); + } break; + case FrameDelta: { + if (item.frameDelta) { + QString data = Timeline::formatTime(item.frameDelta); + result = QVariant::fromValue(data); + } + } break; + case View3D: { + // Don't show view3d object anywhere but FrameGroup + const bool isView3D = m_frameTimes.contains(item.eventData); + if (isView3D && item.additionalType == FrameGroup) + result = QVariant::fromValue(strData); + } break; + } + } break; + case SortRole: { + switch (index.column()) { + case Frame: + if (item.additionalType == FrameGroup) + result = QVariant::fromValue(item.data); + else + result = QVariant::fromValue(item.index); + break; + case Timestamp: + result = QVariant::fromValue(item.begin); + break; + case Duration: + result = QVariant::fromValue(item.end - item.begin); + break; + case FrameDelta: + if (item.frameDelta) + result = QVariant::fromValue(item.frameDelta); + break; + default: + break; + } + } break; + case IndexRole: { + result = QVariant::fromValue(item.index); + } break; + case FilterRole: { + const Item *group = findFrameGroup(&item); + if (group) + result = QVariant::fromValue(m_modelManager->eventType(m_eventData[group->eventData]).data()); + } break; + case CompareRole: { + const Item *group = findFrameGroup(&item); + if (group && int(group->data) == m_filterFrame && (group->eventData == m_filterView3D || m_filterView3D == -1)) + result = QVariant::fromValue(QString("+")); + else + result = QVariant::fromValue(QString("-")); + } break; + } + + return result; +} + +QVariant Quick3DFrameModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + QVariant result; + if (orientation != Qt::Horizontal) + return QAbstractItemModel::headerData(section, orientation, role); + switch (role) { + case Qt::DisplayRole: + switch (section) { + case Frame: + result = QVariant::fromValue(tr("Frame")); + break; + case Duration: + result = QVariant::fromValue(tr("Duration")); + break; + case Timestamp: + result = QVariant::fromValue(tr("Timestamp")); + break; + case FrameDelta: + result = QVariant::fromValue(tr("Frame Delta")); + break; + case View3D: + result = QVariant::fromValue(tr("View3D")); + break; + } + break; + } + return result; +} + +QString Quick3DFrameModel::location(int index) const +{ + if (index < 0) + return {}; + const Item &item = m_data[index]; + if (item.eventData == -1) + return {}; + return m_modelManager->eventType(m_eventData[item.eventData]).data(); +} + +QModelIndex Quick3DFrameModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + if (parent.isValid()) { + int parentIndex = parent.internalId(); + if (parentIndex >= m_data.size()) + return QModelIndex(); + const Item *parentData = &m_stackBottom; + if (parentIndex >= 0) + parentData = &m_data[parentIndex]; + return createIndex(row, column, parentData->children[row]); + } else { + return createIndex(row, column, row >= 0 ? m_stackBottom.children[row] : -1); + } +} + +int Quick3DFrameModel::parentRow(int index) const +{ + const Item *item = &m_data[index]; + const Item *parent = item->parent == -1 ? &m_stackBottom : &m_data[item->parent]; + return parent->children.indexOf(index); +} + +QModelIndex Quick3DFrameModel::parent(const QModelIndex &index) const +{ + if (index.isValid()) { + int childIndex = index.internalId(); + const Item *childData = &m_data[childIndex]; + return childData->parent == -1 ? QModelIndex() : createIndex(parentRow(childData->parent), 0, childData->parent); + } else { + return QModelIndex(); + } +} + +static bool isParentOf(const Quick3DFrameModel::Item &child, const Quick3DFrameModel::Item &parent) { + return child.begin >= parent.begin && child.begin < parent.end; +} + +const Quick3DFrameModel::Item *Quick3DFrameModel::findFrameGroup(const Quick3DFrameModel::Item *item) const +{ + while (item) { + if (item->additionalType == FrameGroup) + return item; + item = item->parent >= 0 ? &m_data[item->parent] : nullptr; + } + return nullptr; +} + +Quick3DFrameModel::Item *Quick3DFrameModel::findParent(int child) +{ + const Item &ci = m_data[child]; + if (ci.parent != -1) + return &m_data[ci.parent]; + for (auto iter = m_data.begin(); iter != m_data.end(); iter++) { + if (ci.index == iter->index || iter->additionalType == SubData) + continue; + if (isParentOf(ci, *iter)) { + const Item *j = &m_data[iter->index]; + bool allChecked = false; + while (!allChecked && j->hasChildren()) { + allChecked = true; + for (int i = 0; i < iter->children.size(); i++) { + const Item &k = m_data[iter->children[i]]; + if (isParentOf(ci, k) && iter->additionalType != SubData) { + j = &k; + allChecked = false; + break; + } + } + } + return &m_data[j->index]; + } + } + return nullptr; +} + +void Quick3DFrameModel::loadEvent(const QmlEvent &event, const QmlEventType &type) +{ + int detailType = type.detailType(); + if (!m_acceptedDetailTypes.contains(detailType) || m_oldEvents) + return; + + if (detailType == EventData){ + m_eventData.insert(m_eventData.size() + 1, event.typeIndex()); + return; + } + + QList numbers = event.numbers>(); + if (numbers.size() == 0) + return; + qint64 eventDuration = numbers[0]; + qint64 eventTime = event.timestamp() - eventDuration; + quint64 data = numbers.size() > 1 ? numbers[1] : 0; + QList eventData; + // The rest are pairs of event data id's + for (int i = 2; i < numbers.size(); i++) { + qint32 h = Quick3DModel::eventDataId(numbers[i] >> 32); + qint32 l = Quick3DModel::eventDataId(numbers[i] & 0xffffffff); + if (m_eventData.contains(h)) + eventData.push_back(h); + if (m_eventData.contains(l)) + eventData.push_back(l); + } + if (eventData.isEmpty() && detailType == SynchronizeFrame) { + m_oldEvents = true; + return; + } + int index = m_data.size(); + Item item = Item(index, -1, eventTime, event.timestamp(), detailType, data); + m_data.push_back(item); + if (detailType == RenderCall) { + for (int i = 0; i < eventData.size(); i++) { + Item child = Item(index + i + 1, item.index, eventTime, event.timestamp(), SubData); + child.eventData = eventData[i]; + m_data.push_back(child); + m_data[index].children.push_back(child.index); + } + } else if (eventData.size()) { + m_data[index].eventData = eventData[0]; + } + + if (!(detailType >= RenderFrame && detailType <= PrepareFrame)) + return; + + int v3d = eventData[0]; + if (!m_frameTimes.contains(v3d)) + m_frameTimes[v3d] = {}; + + switch (detailType) { + case SynchronizeFrame: + if (m_frameTimes[v3d].inFrame) + qWarning () << "Previous frame was not ended correctly"; + m_frameTimes[v3d].begin = eventTime - 1; + m_frameTimes[v3d].inFrame = true; + break; + case PrepareFrame: + if (!m_frameTimes[v3d].inFrame) { + qWarning () << "Synchronize frame missing"; + m_frameTimes[v3d].begin = eventTime - 1; + m_frameTimes[v3d].inFrame = true; + } + m_frameTimes[v3d].prepareReceived = true; + break; + case RenderFrame: + int index = m_data.size(); + if (!m_frameTimes[v3d].inFrame) { + qWarning () << "Render message received without Synchronize and Prepare messages"; + } else { + if (!m_frameTimes[v3d].prepareReceived) + qWarning () << "Frame received without Prepare message"; + item = Item(index, -1, m_frameTimes[v3d].begin, event.timestamp() + 1, FrameGroup, m_frameTimes[v3d].frameCount); + if (m_frameTimes[v3d].frameCount) + item.frameDelta = item.end - m_frameTimes[v3d].end; + item.eventData = v3d; + m_frameTimes[v3d].frameCount++; + m_frameTimes[v3d].end = item.end; + m_frameTimes[v3d].inFrame = false; + m_frameTimes[v3d].prepareReceived = false; + m_data.push_back(item); + } + break; + } +} + +QStringList Quick3DFrameModel::view3DNames() const +{ + QStringList ret; + for (auto value : m_frameTimes.keys()) + ret << m_modelManager->eventType(m_eventData[value]).data(); + return ret; +} + +QList Quick3DFrameModel::frameIndices(const QString &view3DFilter) const +{ + QList ret; + int key = -1; + if (view3DFilter != tr("All")) { + for (int v3d : m_frameTimes.keys()) { + if (m_modelManager->eventType(m_eventData[v3d]).data() == view3DFilter) { + key = v3d; + break; + } + } + } + + for (auto child : m_stackBottom.children) { + if (key == -1 || key == m_data[child].eventData) + ret << child; + } + return ret; +} + +QStringList Quick3DFrameModel::frameNames(const QString &view3D) const +{ + auto indices = frameIndices(view3D); + QStringList ret; + for (auto index : indices) { + const Item &item = m_data[index]; + ret << QString(tr("Frame") + QLatin1Char(' ') + QString::number(item.data)); + } + return ret; +} + +void Quick3DFrameModel::setFilterFrame(const QString &frame) +{ + if (frame == tr("None")) { + m_filterFrame = -1; + } else { + QString title = tr("Frame"); + QString number = frame.right(frame.length() - title.length()); + m_filterFrame = number.toInt(); + } +} + +void Quick3DFrameModel::setFilterView3D(const QString &view3D) +{ + int key = -1; + if (view3D != tr("All")) { + for (int v3d : m_frameTimes.keys()) { + if (m_modelManager->eventType(m_eventData[v3d]).data() == view3D) { + key = v3d; + break; + } + } + } + m_filterView3D = key; +} + +void Quick3DFrameModel::finalize() +{ + if (m_oldEvents) { + m_data.clear(); + endResetModel(); + return; + } + for (auto &item : m_data) { + Item *parent = findParent(item.index); + if (parent) { + if (item.parent == -1) { + for (int i = 0; i < parent->children.size();) { + Item &j = m_data[parent->children[i]]; + if (isParentOf(j, item) && j.additionalType != SubData) { + parent->children.removeOne(j.index); + item.children.push_back(j.index); + j.parent = item.index; + } else { + i++; + } + } + parent->children.push_back(item.index); + item.parent = parent->index; + } + } else { + m_stackBottom.children.push_back(item.index); + } + } + endResetModel(); +} + +} // namespace Internal +} // namespace QmlProfiler diff --git a/src/plugins/qmlprofiler/quick3dframemodel.h b/src/plugins/qmlprofiler/quick3dframemodel.h new file mode 100644 index 00000000000..c58015b8b4e --- /dev/null +++ b/src/plugins/qmlprofiler/quick3dframemodel.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "qmlprofilereventtypes.h" +#include "qmlevent.h" +#include "qmleventtype.h" + +#include + +#include +#include +#include +#include +#include + +namespace QmlProfiler { +class QmlProfilerModelManager; + +namespace Internal { + +class Quick3DFrameModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + struct Item { + Item(int index = -1, int parent = -1, qint64 begin = 0, qint64 end = 0, int additionalType = 0, quint64 data = 0) : + index(index), parent(parent), additionalType(additionalType), begin(begin), end(end), data(data) {} + int index; + int parent; + int additionalType; + qint64 begin; + qint64 end; + quint64 data; + quint64 frameDelta = 0; + int eventData = -1; + QList children; + bool hasChildren() const + { + return !children.isEmpty(); + } + }; + struct FrameTime + { + qint64 begin = 0; + qint64 end = 0; + int frameCount = 0; + bool inFrame = false; + bool prepareReceived = false; + }; + + enum MessageType + { + RenderFrame, + SynchronizeFrame, + PrepareFrame, + MeshLoad, + CustomMeshLoad, + TextureLoad, + GenerateShader, + LoadShader, + ParticleUpdate, + RenderCall, + RenderPass, + EventData, + // additional types + MeshMemoryConsumption, + TextureMemoryConsumption, + FrameGroup, + SubData + }; + + enum ColumnType + { + Frame, + Duration, + FrameDelta, + Timestamp, + View3D, + MaxColumnType + }; + + enum ItemRole { + SortRole = Qt::UserRole + 1, // Sort by data, not by displayed string + FilterRole, + CompareRole, + IndexRole, + }; + + Quick3DFrameModel(QmlProfilerModelManager *manager); + + void clear(); + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; + QString location(int index) const; + + QStringList view3DNames() const; + QStringList frameNames(const QString &view3D) const; + void setFilterView3D(const QString &view3D); + void setFilterFrame(const QString &frame); + +private: + QList frameIndices(const QString &view3DFilter) const; + void loadEvent(const QmlEvent &event, const QmlEventType &type); + Item *findParent(int child); + const Item *findFrameGroup(const Item *item) const; + void finalize(); + int parentRow(int index) const; + + bool m_oldEvents = false; + QList m_data; + Item m_stackBottom; + QHash m_frameTimes; + QHash m_eventData; + QList m_acceptedDetailTypes; + QPointer m_modelManager; + int m_filterView3D = -1; + int m_filterFrame = -1; +}; + +} // namespace Internal +} // namespace QmlProfiler diff --git a/src/plugins/qmlprofiler/quick3dframeview.cpp b/src/plugins/qmlprofiler/quick3dframeview.cpp new file mode 100644 index 00000000000..4b9221a6bcc --- /dev/null +++ b/src/plugins/qmlprofiler/quick3dframeview.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "quick3dframeview.h" + +#include + +#include +#include +#include +#include +#include +#include + +namespace QmlProfiler { +namespace Internal { + +Quick3DFrameView::Quick3DFrameView(QmlProfilerModelManager *profilerModelManager, QWidget *parent) + : QmlProfilerEventsView(parent) +{ + setObjectName(QLatin1String("QmlProfiler.Quick3DFrame.Dock")); + setWindowTitle(tr("Quick3D Frame")); + + auto model = new Quick3DFrameModel(profilerModelManager); + m_mainView.reset(new Quick3DMainView(model, false, this)); + connect(m_mainView.get(), &Quick3DMainView::gotoSourceLocation, + this, &Quick3DFrameView::gotoSourceLocation); + + m_compareFrameView.reset(new Quick3DMainView(model, true, this)); + connect(m_compareFrameView.get(), &Quick3DMainView::gotoSourceLocation, + this, &Quick3DFrameView::gotoSourceLocation); + + auto groupLayout = new QVBoxLayout(this); + groupLayout->setContentsMargins(0,0,0,0); + groupLayout->setSpacing(0); + auto hMainLayout = new QHBoxLayout(this); + hMainLayout->setContentsMargins(0,0,0,0); + hMainLayout->setSpacing(0); + auto hFrameLayout = new QHBoxLayout(this); + hFrameLayout->setContentsMargins(0,0,0,0); + hFrameLayout->setSpacing(0); + auto view3DComboBox = new QComboBox(this); + auto view3DComboModel = new QStringListModel(this); + auto frameComboBox = new QComboBox(this); + auto frameComboModel = new QStringListModel(this); + auto selectView3DLabel = new QLabel(tr("Select View3D"), this); + auto selectFrameLabel = new QLabel(tr("Compare Frame"), this); + view3DComboBox->setModel(view3DComboModel); + frameComboBox->setModel(frameComboModel); + hFrameLayout->addWidget(selectView3DLabel); + hFrameLayout->addWidget(view3DComboBox); + hFrameLayout->addWidget(selectFrameLabel); + hFrameLayout->addWidget(frameComboBox); + groupLayout->addLayout(hFrameLayout); + hMainLayout->addWidget(m_mainView.get()); + hMainLayout->addWidget(m_compareFrameView.get()); + groupLayout->addLayout(hMainLayout); + connect(model, &Quick3DFrameModel::modelReset, [model, view3DComboModel, frameComboModel](){ + QStringList list; + list << tr("All"); + list << model->view3DNames(); + view3DComboModel->setStringList(list); + list.clear(); + list << tr("None"); + list << model->frameNames(tr("All")); + frameComboModel->setStringList(list); + }); + connect(view3DComboBox, &QComboBox::currentTextChanged, [this, model, frameComboModel](const QString &text){ + m_mainView->setFilterView3D(text); + model->setFilterView3D(text); + QStringList list; + list << tr("None"); + list << model->frameNames(text); + frameComboModel->setStringList(list); + }); + connect(frameComboBox, &QComboBox::currentTextChanged, [model, this](const QString &text){ + model->setFilterFrame(text); + m_compareFrameView->setFilterFrame(text); + }); + + setLayout(groupLayout); +} + +void Quick3DFrameView::selectByTypeId(int) +{ + +} + +void Quick3DFrameView::onVisibleFeaturesChanged(quint64) +{ + +} + +Quick3DMainView::Quick3DMainView(Quick3DFrameModel *model, bool compareView, QWidget *parent) + : Utils::TreeView(parent), m_model(model), m_compareView(compareView) +{ + setObjectName("Quick3DMainView"); + setFrameStyle(QFrame::NoFrame); + QHeaderView *h = header(); + h->setSectionResizeMode(QHeaderView::Interactive); + h->setDefaultSectionSize(100); + h->setMinimumSectionSize(50); + + auto sortModel = new QSortFilterProxyModel(this); + sortModel->setSourceModel(model); + sortModel->setSortRole(Quick3DFrameModel::SortRole); + sortModel->setSortCaseSensitivity(Qt::CaseInsensitive); + sortModel->setFilterRole(compareView ? Quick3DFrameModel::CompareRole : Quick3DFrameModel::FilterRole); + if (m_compareView) + sortModel->setFilterFixedString("+"); + setModel(sortModel); + + connect(this, &QAbstractItemView::activated, this, [this](const QModelIndex &index) { + QString location = m_model->location(index.data(Quick3DFrameModel::IndexRole).toInt()); + if (!location.isEmpty()) { + QString file, line; + int lineIdx = location.lastIndexOf(QStringLiteral(".qml:")); + int nameIdx = location.lastIndexOf(QStringLiteral(" ")); + if (lineIdx < 0) + return; + lineIdx += 4; + file = location.mid(nameIdx + 1, lineIdx - nameIdx - 1); + line = location.right(location.length() - lineIdx - 1); + QUrl url(file); + emit gotoSourceLocation(url.fileName(), line.toInt(), 0); + } + }); + m_sortModel = sortModel; + + setSortingEnabled(true); + sortByColumn(Quick3DFrameModel::FrameGroup, Qt::AscendingOrder); + + setRootIsDecorated(true); + setColumnWidth(0, 300); +} + +void Quick3DMainView::setFilterView3D(const QString &objectName) +{ + if (objectName == tr("All")) + m_sortModel->setFilterFixedString(""); + else + m_sortModel->setFilterFixedString(objectName); +} + +void Quick3DMainView::setFilterFrame(const QString &) +{ + m_sortModel->setFilterFixedString("+"); +} + +} // namespace Internal +} // namespace QmlProfiler diff --git a/src/plugins/qmlprofiler/quick3dframeview.h b/src/plugins/qmlprofiler/quick3dframeview.h new file mode 100644 index 00000000000..963103b588d --- /dev/null +++ b/src/plugins/qmlprofiler/quick3dframeview.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "qmlprofilermodelmanager.h" +#include "quick3dframemodel.h" +#include "qmlprofilereventsview.h" +#include "qmlprofilereventtypes.h" + +#include + +#include +#include + +#include + +namespace QmlProfiler { +namespace Internal { + +class Quick3DMainView; + +class Quick3DFrameView : public QmlProfilerEventsView +{ + Q_OBJECT +public: + explicit Quick3DFrameView(QmlProfilerModelManager *profilerModelManager, + QWidget *parent = nullptr); + ~Quick3DFrameView() override = default; + + void selectByTypeId(int typeIndex) override; + void onVisibleFeaturesChanged(quint64 features) override; + +private: + + std::unique_ptr m_mainView; + std::unique_ptr m_compareFrameView; +}; + +class Quick3DMainView : public Utils::TreeView +{ + Q_OBJECT +public: + explicit Quick3DMainView(Quick3DFrameModel *model, bool compareView, QWidget *parent = nullptr); + ~Quick3DMainView() override = default; + + void setFilterView3D(const QString &objectName); + void setFilterFrame(const QString &objectName); + +signals: + void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber); + +private: + Quick3DFrameModel *m_model; + QSortFilterProxyModel *m_sortModel; + bool m_compareView; +}; + +} // namespace Internal +} // namespace QmlProfiler diff --git a/src/plugins/qmlprofiler/quick3dmodel.cpp b/src/plugins/qmlprofiler/quick3dmodel.cpp index 110140d7de0..2415e9573a9 100644 --- a/src/plugins/qmlprofiler/quick3dmodel.cpp +++ b/src/plugins/qmlprofiler/quick3dmodel.cpp @@ -4,16 +4,23 @@ #include "qmlprofilerconstants.h" #include "qmlprofilertr.h" #include "quick3dmodel.h" - #include namespace QmlProfiler { namespace Internal { +int Quick3DModel::eventDataId(int id) +{ + static int ID_MASK = 0xff000000; + static int ID_MARKER = 0xed000000; + if ((id & ID_MASK) == ID_MARKER) + return id - ID_MARKER; + return 0; +} + Quick3DModel::Quick3DModel(QmlProfilerModelManager *manager, - Timeline::TimelineModelAggregator *parent) : - QmlProfilerTimelineModel(manager, Quick3DEvent, UndefinedRangeType, ProfileQuick3D, parent), - m_maximumMsgType(-1) + Timeline::TimelineModelAggregator *parent) : + QmlProfilerTimelineModel(manager, Quick3DEvent, UndefinedRangeType, ProfileQuick3D, parent) { } @@ -32,6 +39,9 @@ static const char *messageTypes[] = { QT_TRANSLATE_NOOP("QmlProfiler", "Generate Shader"), QT_TRANSLATE_NOOP("QmlProfiler", "Load Shader"), QT_TRANSLATE_NOOP("QmlProfiler", "Particle Update"), + QT_TRANSLATE_NOOP("QmlProfiler", "Render Call"), + QT_TRANSLATE_NOOP("QmlProfiler", "Render Pass"), + QT_TRANSLATE_NOOP("QmlProfiler", "Event Data"), QT_TRANSLATE_NOOP("QmlProfiler", "Mesh Memory consumption"), QT_TRANSLATE_NOOP("QmlProfiler", "Texture Memory consumption"), @@ -65,12 +75,12 @@ QString Quick3DModel::unloadMessageType(uint i) QVariantList Quick3DModel::labels() const { QVariantList result; - for (int i = 0; i <= m_maximumMsgType; ++i) { + for (int detailType : m_sortedTypes) { QVariantMap element; element.insert(QLatin1String("displayName"), - i != ParticleUpdate ? Tr::tr("Render Thread") : Tr::tr("GUI Thread")); - element.insert(QLatin1String("description"), messageType(i)); - element.insert(QLatin1String("id"), i); + detailType != ParticleUpdate ? Tr::tr("Render Thread") : Tr::tr("GUI Thread")); + element.insert(QLatin1String("description"), messageType(detailType)); + element.insert(QLatin1String("id"), detailType); result << element; } return result; @@ -83,6 +93,8 @@ float Quick3DModel::relativeHeight(int index) const return qMin(1.0f, (float)m_data[index].data / (float)m_maxTextureSize); if (detailType == MeshMemoryConsumption) return qMin(1.0f, (float)m_data[index].data / (float)m_maxMeshSize); + if (detailType == RenderPass) + return qMin(1.0f, (float)m_data[index].nests / (float)m_maxNestedRenderCalls); return 1.0f; } @@ -93,9 +105,45 @@ qint64 Quick3DModel::rowMaxValue(int rowNumber) const return m_maxTextureSize; if (index == MeshMemoryConsumption) return m_maxMeshSize; + if (index == RenderPass) + return m_maxNestedRenderCalls; return 0; } +bool Quick3DModel::resolveType(const QString &object, int detailType, QString &type) +{ + switch (detailType) { + case RenderFrame: + case SynchronizeFrame: + case PrepareFrame: + type = QLatin1String("View3D"); + break; + case MeshLoad: + case CustomMeshLoad: + case TextureLoad: + case LoadShader: + case GenerateShader: + type = QLatin1String("URL"); + break; + case ParticleUpdate: + type = QLatin1String("ParticleSystem"); + break; + case RenderCall: + if (object.contains(QLatin1String("Material"))) + type = QLatin1String("Material"); + if (object.contains(QLatin1String("Model"))) + type = QLatin1String("Model"); + return !type.isEmpty(); + break; + case RenderPass: + type = QLatin1String("Pass"); + break; + default: + break; + } + return !type.isEmpty(); +} + QVariantMap Quick3DModel::details(int index) const { auto detailType = m_data[index].additionalType; @@ -115,16 +163,41 @@ QVariantMap Quick3DModel::details(int index) const result.insert(Tr::tr("Draw Calls"), calls); result.insert(Tr::tr("Render Passes"), passes); } + if ((detailType == RenderPass || detailType == PrepareFrame) && m_data[index].data) { + quint32 width = m_data[index].data & 0xffffffff; + quint32 height = m_data[index].data >> 32; + result.insert(tr("Width"), width); + result.insert(tr("Height"), height); + } if ((detailType >= MeshLoad && detailType <= TextureLoad) || (detailType >= MeshMemoryConsumption && detailType <= TextureMemoryConsumption)) { result.insert(Tr::tr("Total Memory Usage"), m_data[index].data); } + if (detailType == RenderCall) { + quint32 primitives = m_data[index].data & 0xffffffff; + quint32 instances = m_data[index].data >> 32; + result.insert(tr("Primitives"), primitives); + if (instances > 1) + result.insert(tr("Instances"), instances); + } + if (!m_data[index].eventData.isEmpty()) { + for (int i = 0; i < m_data[index].eventData.size(); i++) { + int p = m_data[index].eventData[i]; + if (m_eventData.contains(p)) { + const QmlEventType &et = modelManager()->eventType(m_eventData[p]); + QString type; + if (resolveType(et.data(), detailType, type)) + result.insert(type, et.data()); + } + } + } return result; } int Quick3DModel::expandedRow(int index) const { - return selectionId(index) + 1; + const Item &item = m_data[index]; + return m_sortedTypes.indexOf(item.additionalType) + 1; } int Quick3DModel::collapsedRow(int index) const @@ -138,64 +211,105 @@ void Quick3DModel::loadEvent(const QmlEvent &event, const QmlEventType &type) int detailType = type.detailType(); if (detailType >= MaximumQuick3DFrameType) return; - qint64 eventDuration = event.number(0); - qint64 eventTime = event.timestamp() - eventDuration; - QVector numbers = event.numbers>(); - quint64 data = numbers.size() > 1 ? event.number(1) : 0; + if (detailType == EventData) { + m_eventData.insert(m_eventData.size() + 1, event.typeIndex()); + return; + } - int typeCount = detailType; + QList numbers = event.numbers>(); + if (numbers.size() == 0) + return; + qint64 eventDuration = numbers[0]; + qint64 eventTime = event.timestamp() - eventDuration; + quint64 data = numbers.size() > 1 ? numbers[1] : 0; + QList eventData; + // The rest are pairs of event data id's + for (int i = 2; i < numbers.size(); i++) { + qint32 h = eventDataId(numbers[i] >> 32); + qint32 l = eventDataId(numbers[i] & 0xffffffff); + if (m_eventData.contains(h)) + eventData.push_back(h); + if (m_eventData.contains(l)) + eventData.push_back(l); + } + m_types << detailType; if (detailType == MeshLoad || detailType == CustomMeshLoad) { bool updatePrevValues = true; + if (m_prevMeshStartTime != -1) { bool unload = m_prevMeshData > data; - m_data.insert(insert(eventTime, eventDuration, detailType), - Item(detailType, data, unload)); + Item i = Item(detailType, data, unload); + i.eventData = eventData; + m_data.insert(insert(eventTime, eventDuration, detailType), i); if (m_prevMeshData != data) { m_data.insert(insert(m_prevMeshStartTime, eventTime - m_prevMeshStartTime, MeshMemoryConsumption), Item(MeshMemoryConsumption, m_prevMeshData)); + m_types << MeshMemoryConsumption; } else { updatePrevValues = false; } } else { - m_data.insert(insert(eventTime, eventDuration, detailType), - Item(detailType, data)); + Item i = Item(detailType, data); + i.eventData = eventData; + m_data.insert(insert(eventTime, eventDuration, detailType), i); } m_maxMeshSize = qMax(m_maxMeshSize, data); if (updatePrevValues) { m_prevMeshStartTime = eventTime; m_prevMeshData = data; } - typeCount = MeshMemoryConsumption; } else if (detailType == TextureLoad) { bool updatePrevValues = true; + if (m_prevTexStartTime != -1) { bool unload = m_prevTexData > data; - m_data.insert(insert(eventTime, eventDuration, detailType), - Item(detailType, data, unload)); + Item i = Item(detailType, data, unload); + i.eventData = eventData; + m_data.insert(insert(eventTime, eventDuration, detailType), i); if (m_prevTexData != data) { m_data.insert(insert(m_prevTexStartTime, eventTime - m_prevTexStartTime, TextureMemoryConsumption), Item(TextureMemoryConsumption, m_prevTexData)); + m_types << TextureMemoryConsumption; } else { updatePrevValues = false; } } else { - m_data.insert(insert(eventTime, eventDuration, detailType), - Item(detailType, data)); + Item i = Item(detailType, data, false); + i.eventData = eventData; + m_data.insert(insert(eventTime, eventDuration, detailType), i); } m_maxTextureSize = qMax(m_maxTextureSize, data); if (updatePrevValues) { m_prevTexData = data; m_prevTexStartTime = eventTime; } - typeCount = TextureMemoryConsumption; } else { - m_data.insert(insert(eventTime, eventDuration, detailType), - Item(detailType, data)); + Item i = Item(detailType, data); + i.eventData = eventData; + auto index = insert(eventTime, eventDuration, detailType); + m_data.insert(index, i); + } +} + +void Quick3DModel::calculateRenderPassNesting() +{ + QList nesting; + for (int item = 0; item < m_data.size(); item++) { + if (m_data[item].additionalType != RenderPass) + continue; + while (!nesting.isEmpty()) { + auto l = nesting.last(); + if (l >= startTime(item)) + break; + else + nesting.removeLast(); + } + nesting.push_back(endTime(item)); + m_data[item].nests = nesting.size(); + m_maxNestedRenderCalls = qMax(m_maxNestedRenderCalls, nesting.size()); } - if (typeCount > m_maximumMsgType) - m_maximumMsgType = typeCount; } void Quick3DModel::finalize() @@ -204,22 +318,29 @@ void Quick3DModel::finalize() m_data.insert(insert(m_prevMeshStartTime, modelManager()->traceEnd() - m_prevMeshStartTime, MeshMemoryConsumption), Item(MeshMemoryConsumption, m_prevMeshData)); + m_types << MeshMemoryConsumption; } if (m_prevTexStartTime != -1) { m_data.insert(insert(m_prevTexStartTime, modelManager()->traceEnd() - m_prevTexStartTime, TextureMemoryConsumption), Item(TextureMemoryConsumption, m_prevTexData)); + m_types << TextureMemoryConsumption; } computeNesting(); setCollapsedRowCount(Constants::QML_MIN_LEVEL + 1); - setExpandedRowCount(m_maximumMsgType + 2); + m_sortedTypes = m_types.values(); + std::sort(m_sortedTypes.begin(), m_sortedTypes.end(), [](int a, int b){ return a < b;}); + setExpandedRowCount(m_sortedTypes.size() + 1); QmlProfilerTimelineModel::finalize(); + calculateRenderPassNesting(); + } void Quick3DModel::clear() { m_data.clear(); - m_maximumMsgType = -1; + m_types.clear(); + m_sortedTypes.clear(); m_prevTexStartTime = -1; m_prevMeshStartTime = -1; m_maxMeshSize = 0; @@ -227,10 +348,49 @@ void Quick3DModel::clear() QmlProfilerTimelineModel::clear(); } +QVariantMap Quick3DModel::locationFromEvent(int index) const +{ + QVariantMap ret; + for (auto e : m_data[index].eventData) { + if (m_eventData.contains(e)) { + const QmlEventType &et = modelManager()->eventType(m_eventData[e]); + const QString data = et.data(); + QString file, line; + int lineIdx = data.lastIndexOf(QStringLiteral(".qml:")); + int nameIdx = data.lastIndexOf(QStringLiteral(" ")); + if (lineIdx < 0) + return ret; + lineIdx += 4; + file = data.mid(nameIdx + 1, lineIdx - nameIdx - 1); + line = data.right(data.length() - lineIdx - 1); + QUrl url(file); + ret.insert(QStringLiteral("file"), url.fileName()); + ret.insert(QStringLiteral("line"), line.toInt()); + ret.insert(QStringLiteral("column"), 0); + break; + } + } + return ret; +} + QVariantMap Quick3DModel::location(int index) const { + QVariantMap ret; + if (!m_data[index].eventData.isEmpty()) + ret = locationFromEvent(index); + if (!ret.isEmpty()) + return ret; return locationFromTypeId(index); } +int Quick3DModel::typeId(int index) const +{ + for (auto ed : m_data[index].eventData) { + if (m_eventData.contains(ed)) + return m_eventData[ed]; + } + return QmlProfilerTimelineModel::typeId(index); +} + } // namespace Internal } // namespace QmlProfiler diff --git a/src/plugins/qmlprofiler/quick3dmodel.h b/src/plugins/qmlprofiler/quick3dmodel.h index 69561c62864..5e111a534d5 100644 --- a/src/plugins/qmlprofiler/quick3dmodel.h +++ b/src/plugins/qmlprofiler/quick3dmodel.h @@ -17,8 +17,10 @@ public: Item(int additionalType, quint64 data, bool unload = false) : additionalType(additionalType), data(data), unload(unload) {} int additionalType = 0; + int nests = 0; quint64 data = 0; bool unload = false; + QList eventData; }; enum MessageType @@ -32,7 +34,9 @@ public: GenerateShader, LoadShader, ParticleUpdate, - + RenderCall, + RenderPass, + EventData, // additional types MeshMemoryConsumption, TextureMemoryConsumption @@ -51,19 +55,28 @@ public: void finalize() override; void clear() override; QVariantMap location(int index) const override; + int typeId(int index) const override; + + static int eventDataId(int id); private: static QString messageType(uint i); static QString unloadMessageType(uint i); + static bool resolveType(const QString &object, int detailType, QString &type); + QVariantMap locationFromEvent(int poid) const; + void calculateRenderPassNesting(); - int m_maximumMsgType = 0; + QSet m_types; + QList m_sortedTypes; qint64 m_prevTexStartTime = -1; qint64 m_prevMeshStartTime = -1; quint64 m_prevMeshData = 0; quint64 m_prevTexData = 0; quint64 m_maxMeshSize = 0; quint64 m_maxTextureSize = 0; + int m_maxNestedRenderCalls = 1; QVector m_data; + QHash m_eventData; }; } // namespace Internal