forked from qt-creator/qt-creator
Add support for new Quick3D profiling events
- Add support for the new RenderCall and RenderPass events. - Add support for more info from existing events. - Add quick3d frame view Task-number: QTBUG-105970 Task-number: QTBUG-105971 Change-Id: Ia1b6f466da5b195558fd5880b622084634c9090b Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
@@ -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)
|
||||
|
@@ -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",
|
||||
]
|
||||
}
|
||||
|
@@ -47,9 +47,10 @@ enum Quick3DEventType {
|
||||
Quick3DParticleUpdate,
|
||||
Quick3DGenerateShader,
|
||||
Quick3DLoadShader,
|
||||
Quick3DRenderCall,
|
||||
Quick3DRenderPass,
|
||||
Quick3DEventData,
|
||||
MaximumQuick3DFrameType,
|
||||
NumQuick3DRenderThreadFrameTypes = Quick3DParticleUpdate,
|
||||
NumQuick3DGUIThreadFrameTypes = MaximumQuick3DFrameType - NumQuick3DRenderThreadFrameTypes,
|
||||
};
|
||||
|
||||
enum RangeType {
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -166,13 +166,19 @@ QDataStream &operator>>(QDataStream &stream, QmlTypedEvent &event)
|
||||
case Quick3DEvent: {
|
||||
|
||||
QVarLengthArray<qint64> params;
|
||||
qint64 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<Message>(messageType), UndefinedRangeType, subtype);
|
||||
event.type.setData(QString::fromUtf8(str));
|
||||
event.event.setNumbers<QVarLengthArray<qint64>, qint64>(params);
|
||||
break;
|
||||
}
|
||||
|
528
src/plugins/qmlprofiler/quick3dframemodel.cpp
Normal file
528
src/plugins/qmlprofiler/quick3dframemodel.cpp
Normal file
@@ -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 <tracing/timelineformattime.h>
|
||||
#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<quint64> numbers = event.numbers<QList<quint64>>();
|
||||
if (numbers.size() == 0)
|
||||
return;
|
||||
qint64 eventDuration = numbers[0];
|
||||
qint64 eventTime = event.timestamp() - eventDuration;
|
||||
quint64 data = numbers.size() > 1 ? numbers[1] : 0;
|
||||
QList<int> 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<int> Quick3DFrameModel::frameIndices(const QString &view3DFilter) const
|
||||
{
|
||||
QList<int> 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
|
151
src/plugins/qmlprofiler/quick3dframemodel.h
Normal file
151
src/plugins/qmlprofiler/quick3dframemodel.h
Normal file
@@ -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 <utils/qtcassert.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QStack>
|
||||
#include <QVector>
|
||||
#include <QPointer>
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
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<int> 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<int> 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<Item> m_data;
|
||||
Item m_stackBottom;
|
||||
QHash<int, FrameTime> m_frameTimes;
|
||||
QHash<int, int> m_eventData;
|
||||
QList<int> m_acceptedDetailTypes;
|
||||
QPointer<QmlProfilerModelManager> m_modelManager;
|
||||
int m_filterView3D = -1;
|
||||
int m_filterFrame = -1;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QmlProfiler
|
173
src/plugins/qmlprofiler/quick3dframeview.cpp
Normal file
173
src/plugins/qmlprofiler/quick3dframeview.cpp
Normal file
@@ -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 <coreplugin/minisplitter.h>
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QComboBox>
|
||||
#include <QStringListModel>
|
||||
#include <QLabel>
|
||||
|
||||
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
|
82
src/plugins/qmlprofiler/quick3dframeview.h
Normal file
82
src/plugins/qmlprofiler/quick3dframeview.h
Normal file
@@ -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 <utils/itemviews.h>
|
||||
|
||||
#include <QPointer>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <memory>
|
||||
|
||||
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<Quick3DMainView> m_mainView;
|
||||
std::unique_ptr<Quick3DMainView> 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
|
@@ -4,16 +4,23 @@
|
||||
#include "qmlprofilerconstants.h"
|
||||
#include "qmlprofilertr.h"
|
||||
#include "quick3dmodel.h"
|
||||
|
||||
#include <tracing/timelineformattime.h>
|
||||
|
||||
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)
|
||||
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<qint64>(0);
|
||||
qint64 eventTime = event.timestamp() - eventDuration;
|
||||
QVector<quint64> numbers = event.numbers<QVector<quint64>>();
|
||||
quint64 data = numbers.size() > 1 ? event.number<quint64>(1) : 0;
|
||||
if (detailType == EventData) {
|
||||
m_eventData.insert(m_eventData.size() + 1, event.typeIndex());
|
||||
return;
|
||||
}
|
||||
|
||||
int typeCount = detailType;
|
||||
QList<quint64> numbers = event.numbers<QList<quint64>>();
|
||||
if (numbers.size() == 0)
|
||||
return;
|
||||
qint64 eventDuration = numbers[0];
|
||||
qint64 eventTime = event.timestamp() - eventDuration;
|
||||
quint64 data = numbers.size() > 1 ? numbers[1] : 0;
|
||||
QList<int> 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<qint64> 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
|
||||
|
@@ -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<int> 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<int> m_types;
|
||||
QList<int> 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<Item> m_data;
|
||||
QHash<int, int> m_eventData;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
Reference in New Issue
Block a user