blob: 87cc08e59e547f602965f45559d56ac33275cced [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qsysteminformationservice_p.h"
#include "qsysteminformationservice_p_p.h"
#ifdef Q_OS_ANDROID
#include <QtCore/QStandardPaths>
#endif
#include <QtCore/QThreadPool>
#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
#include <QtCore/QDateTime>
#include <QtCore/QUrl>
#include <QtCore/QDir>
#include <QtGui/QDesktopServices>
#include <Qt3DCore/QAspectEngine>
#include <Qt3DCore/QAbstractAspect>
#include <Qt3DCore/private/qabstractaspect_p.h>
#include <Qt3DCore/private/qaspectengine_p.h>
#include <Qt3DCore/private/aspectcommanddebugger_p.h>
QT_BEGIN_NAMESPACE
namespace {
struct FrameHeader
{
FrameHeader()
: frameId(0)
, jobCount(0)
, frameType(WorkerJob)
{
}
enum FrameType {
WorkerJob = 0,
Submission
};
quint32 frameId;
quint16 jobCount;
quint16 frameType; // Submission or worker job
};
}
namespace Qt3DCore {
QSystemInformationServicePrivate::QSystemInformationServicePrivate(QAspectEngine *aspectEngine,
const QString &description)
: QAbstractServiceProviderPrivate(QServiceLocator::SystemInformation, description)
, m_aspectEngine(aspectEngine)
, m_submissionStorage(nullptr)
, m_frameId(0)
, m_commandDebugger(nullptr)
{
m_traceEnabled = qEnvironmentVariableIsSet("QT3D_TRACE_ENABLED");
m_graphicsTraceEnabled = qEnvironmentVariableIsSet("QT3D_GRAPHICS_TRACE_ENABLED");
if (m_traceEnabled || m_graphicsTraceEnabled)
m_jobsStatTimer.start();
const bool commandServerEnabled = qEnvironmentVariableIsSet("QT3D_COMMAND_SERVER_ENABLED");
if (commandServerEnabled) {
m_commandDebugger = new Debug::AspectCommandDebugger(q_func());
m_commandDebugger->initialize();
}
}
QSystemInformationServicePrivate::~QSystemInformationServicePrivate() = default;
QSystemInformationServicePrivate *QSystemInformationServicePrivate::get(QSystemInformationService *q)
{
return q->d_func();
}
// Called by the jobs
void QSystemInformationServicePrivate::addJobLogStatsEntry(QSystemInformationServicePrivate::JobRunStats &stats)
{
if (!m_traceEnabled && !m_graphicsTraceEnabled)
return;
if (!m_jobStatsCached.hasLocalData()) {
auto jobVector = new QVector<JobRunStats>;
m_jobStatsCached.setLocalData(jobVector);
QMutexLocker lock(&m_localStoragesMutex);
m_localStorages.push_back(jobVector);
}
m_jobStatsCached.localData()->push_back(stats);
}
// Called from Submission thread (which can be main thread in Manual drive mode)
void QSystemInformationServicePrivate::addSubmissionLogStatsEntry(QSystemInformationServicePrivate::JobRunStats &stats)
{
if (!m_traceEnabled && !m_graphicsTraceEnabled)
return;
QMutexLocker lock(&m_localStoragesMutex);
if (!m_jobStatsCached.hasLocalData()) {
m_submissionStorage = new QVector<JobRunStats>;
m_jobStatsCached.setLocalData(m_submissionStorage);
}
// Handle the case where submission thread is also the main thread (Scene/Manual drive modes with no RenderThread)
if (m_submissionStorage == nullptr && m_jobStatsCached.hasLocalData())
m_submissionStorage = new QVector<JobRunStats>;
// When having no submission thread this can be null
m_submissionStorage->push_back(stats);
}
// Called after jobs have been executed (MainThread QAspectJobManager::enqueueJobs)
void QSystemInformationServicePrivate::writeFrameJobLogStats()
{
if (!m_traceEnabled && !m_graphicsTraceEnabled)
return;
using JobRunStats = QSystemInformationServicePrivate::JobRunStats;
if (!m_traceFile) {
const QString fileName = QStringLiteral("trace_") + QCoreApplication::applicationName() +
QDateTime::currentDateTime().toString(QStringLiteral("_yyMMdd-hhmmss_")) +
QSysInfo::productType() + QStringLiteral("_") + QSysInfo::buildAbi() + QStringLiteral(".qt3d");
#ifdef Q_OS_ANDROID
m_traceFile.reset(new QFile(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QStringLiteral("/") + fileName));
#else
// TODO fix for iOS
m_traceFile.reset(new QFile(fileName));
#endif
if (!m_traceFile->open(QFile::WriteOnly|QFile::Truncate))
qCritical("Failed to open trace file");
}
// Write Aspect + Job threads
{
FrameHeader header;
header.frameId = m_frameId;
header.jobCount = 0;
for (const QVector<JobRunStats> *storage : qAsConst(m_localStorages))
header.jobCount += storage->size();
m_traceFile->write(reinterpret_cast<char *>(&header), sizeof(FrameHeader));
for (QVector<JobRunStats> *storage : qAsConst(m_localStorages)) {
for (const JobRunStats &stat : *storage)
m_traceFile->write(reinterpret_cast<const char *>(&stat), sizeof(JobRunStats));
storage->clear();
}
}
// Write submission thread
{
QMutexLocker lock(&m_localStoragesMutex);
const int submissionJobSize = m_submissionStorage != nullptr ? m_submissionStorage->size() : 0;
if (submissionJobSize > 0) {
FrameHeader header;
header.frameId = m_frameId;
header.jobCount = submissionJobSize;
header.frameType = FrameHeader::Submission;
m_traceFile->write(reinterpret_cast<char *>(&header), sizeof(FrameHeader));
for (const JobRunStats &stat : *m_submissionStorage)
m_traceFile->write(reinterpret_cast<const char *>(&stat), sizeof(JobRunStats));
m_submissionStorage->clear();
}
}
m_traceFile->flush();
++m_frameId;
}
void QSystemInformationServicePrivate::updateTracing()
{
if (m_traceEnabled || m_graphicsTraceEnabled) {
if (!m_jobsStatTimer.isValid())
m_jobsStatTimer.start();
} else {
m_traceFile.reset();
}
}
QTaskLogger::QTaskLogger(QSystemInformationService *service, const JobId &jobId, Type type)
: m_service(service && service->isTraceEnabled() ? service : nullptr)
, m_type(type)
{
m_stats.jobId = jobId;
if (m_service) {
m_stats.startTime = QSystemInformationServicePrivate::get(m_service)->m_jobsStatTimer.nsecsElapsed();
m_stats.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
}
}
QTaskLogger::QTaskLogger(QSystemInformationService *service,
const quint32 jobType,
const quint32 instance,
QTaskLogger::Type type)
: m_service(service && service->isTraceEnabled() ? service : nullptr)
, m_type(type)
{
m_stats.jobId.typeAndInstance[0] = jobType;
m_stats.jobId.typeAndInstance[1] = instance;
if (m_service) {
m_stats.startTime = QSystemInformationServicePrivate::get(m_service)->m_jobsStatTimer.nsecsElapsed();
m_stats.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
}
}
QTaskLogger::~QTaskLogger() {
if (m_service) {
auto dservice = QSystemInformationServicePrivate::get(m_service);
if (m_stats.endTime == 0L)
m_stats.endTime = dservice->m_jobsStatTimer.nsecsElapsed();
switch (m_type) {
case AspectJob: dservice->addJobLogStatsEntry(m_stats); break;
case Submission: dservice->addSubmissionLogStatsEntry(m_stats); break;
}
}
}
void QTaskLogger::end(qint64 t)
{
m_stats.endTime = t > 0 || !m_service ? t : QSystemInformationServicePrivate::get(m_service)->m_jobsStatTimer.nsecsElapsed();
}
qint64 QTaskLogger::restart()
{
if (m_service)
m_stats.startTime = QSystemInformationServicePrivate::get(m_service)->m_jobsStatTimer.nsecsElapsed();
return m_stats.startTime;
}
/* !\internal
\class Qt3DCore::QSystemInformationService
\inmodule Qt3DCore
\brief Interface for a Qt3D system information service
This is an interface class that should be subclassesd by providers of the
system information service.
*/
/*
Creates an instance of QSystemInformationService, with a \a description for
the new service. This constructor is protected so only subclasses can
instantiate a QSystemInformationService object.
*/
QSystemInformationService::QSystemInformationService(QAspectEngine *aspectEngine)
: QAbstractServiceProvider(*new QSystemInformationServicePrivate(aspectEngine, QLatin1String("Default System Information Service")))
{
}
QSystemInformationService::QSystemInformationService(QAspectEngine *aspectEngine, const QString &description)
: QAbstractServiceProvider(*new QSystemInformationServicePrivate(aspectEngine, description))
{
}
/*
\internal
*/
QSystemInformationService::QSystemInformationService(QSystemInformationServicePrivate &dd)
: QAbstractServiceProvider(dd)
{
}
bool QSystemInformationService::isTraceEnabled() const
{
Q_D(const QSystemInformationService);
return d->m_traceEnabled;
}
bool QSystemInformationService::isGraphicsTraceEnabled() const
{
Q_D(const QSystemInformationService);
return d->m_graphicsTraceEnabled;
}
bool QSystemInformationService::isCommandServerEnabled() const
{
Q_D(const QSystemInformationService);
return d->m_commandDebugger != nullptr;
}
void QSystemInformationService::setTraceEnabled(bool traceEnabled)
{
Q_D(QSystemInformationService);
if (d->m_traceEnabled != traceEnabled) {
d->m_traceEnabled = traceEnabled;
emit traceEnabledChanged(d->m_traceEnabled);
d->updateTracing();
}
}
void QSystemInformationService::setGraphicsTraceEnabled(bool graphicsTraceEnabled)
{
Q_D(QSystemInformationService);
if (d->m_graphicsTraceEnabled != graphicsTraceEnabled) {
d->m_graphicsTraceEnabled = graphicsTraceEnabled;
emit graphicsTraceEnabledChanged(d->m_graphicsTraceEnabled);
d->updateTracing();
}
}
/*
\fn QStringList Qt3DCore::QSystemInformationService::aspectNames() const
Returns a string list containing the names of all registered aspects.
*/
QStringList QSystemInformationService::aspectNames() const
{
Q_D(const QSystemInformationService);
if (!d->m_aspectEngine)
return {};
QStringList res;
const auto aspects = d->m_aspectEngine->aspects();
if (aspects.isEmpty())
return { QLatin1String("No loaded aspects") };
QAspectEnginePrivate *dengine = QAspectEnginePrivate::get(d->m_aspectEngine);
for (auto aspect: aspects) {
const QString name = dengine->m_factory.aspectName(aspect);
if (!name.isEmpty())
res << name;
else
res << QLatin1String("<unnamed>");
}
return res;
}
/*
\fn int Qt3DCore::QSystemInformationService::threadPoolThreadCount() const
Returns the maximum number of threads in the Qt3D task manager's threadpool.
*/
int QSystemInformationService::threadPoolThreadCount() const
{
return QThreadPool::globalInstance()->maxThreadCount();
}
void QSystemInformationService::writePreviousFrameTraces()
{
Q_D(QSystemInformationService);
d->writeFrameJobLogStats();
}
void QSystemInformationService::revealLogFolder()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(QDir::currentPath()));
}
QVariant QSystemInformationService::executeCommand(const QString &command)
{
Q_D(QSystemInformationService);
if (command == QLatin1String("tracing on")) {
setTraceEnabled(true);
return {isTraceEnabled()};
}
if (command == QLatin1String("tracing off")) {
setTraceEnabled(false);
return {isTraceEnabled()};
}
if (command == QLatin1String("glprofiling on")) {
setGraphicsTraceEnabled(true);
return {isTraceEnabled()};
}
if (command == QLatin1String("glprofiling off")) {
setGraphicsTraceEnabled(false);
return {isTraceEnabled()};
}
return d->m_aspectEngine->executeCommand(command);
}
void QSystemInformationService::dumpCommand(const QString &command)
{
QVariant res = executeCommand(command);
QObject *obj = res.value<QObject *>();
if (obj) {
auto reply = qobject_cast<Qt3DCore::Debug::AsynchronousCommandReply*>(obj);
if (reply) {
connect(reply, &Debug::AsynchronousCommandReply::finished, this, [reply]() {
qWarning() << qPrintable( QLatin1String(reply->data()) );
});
return;
}
}
qWarning() << qPrintable(res.toString());
}
}
QT_END_NAMESPACE