Files
renderdoc/qrenderdoc/Code/qrenderdoc.cpp
T
2018-01-01 17:55:29 +00:00

378 lines
11 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2016-2018 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include <QApplication>
#include <QDir>
#include <QFileInfo>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QStandardPaths>
#include <QSysInfo>
#include "Code/CaptureContext.h"
#include "Code/QRDUtils.h"
#include "Code/Resources.h"
#include "Code/pyrenderdoc/PythonContext.h"
#include "Windows/Dialogs/CrashDialog.h"
#include "Windows/MainWindow.h"
#include "version.h"
#if defined(Q_OS_WIN32)
extern "C" {
_declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
}
#endif
REPLAY_PROGRAM_MARKER()
void sharedLogOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
LogType logtype = LogType::Comment;
switch(type)
{
case QtDebugMsg: logtype = LogType::Debug; break;
case QtInfoMsg: logtype = LogType::Comment; break;
case QtWarningMsg: logtype = LogType::Warning; break;
case QtCriticalMsg: logtype = LogType::Error; break;
case QtFatalMsg: logtype = LogType::Fatal; break;
}
RENDERDOC_LogMessage(logtype, "QTRD", context.file, context.line, msg.toUtf8().data());
}
int main(int argc, char *argv[])
{
// call this as the very first thing - no-op on other platforms, but on linux it means
// XInitThreads will be called allowing driver access to xlib on multiple threads.
QCoreApplication::setAttribute(Qt::AA_X11InitThreads);
qInstallMessageHandler(sharedLogOutput);
#if !defined(RELEASE)
for(int i = 0; i < argc; i++)
{
if(!QString::compare(QString::fromUtf8(argv[i]), lit("--unittest"), Qt::CaseInsensitive))
{
QCoreApplication app(argc, argv);
PythonContext::GlobalInit();
bool errors = false;
qInfo() << "Checking python binding consistency.";
{
PythonContextHandle py;
errors = py.ctx().CheckInterfaces();
}
if(errors)
{
qCritical() << "Found errors in python bindings. Please fix!";
return 1;
}
qInfo() << "Python bindings are consistent.";
return 0;
}
}
#endif
qInfo() << "QRenderDoc initialising.";
QString filename;
bool temp = false;
for(int i = 0; i < argc; i++)
{
if(!QString::compare(QString::fromUtf8(argv[i]), lit("--tempfile"), Qt::CaseInsensitive))
temp = true;
}
for(int i = 0; i < argc; i++)
{
if(!QString::compare(QString::fromUtf8(argv[i]), lit("--install_vulkan_layer")) && i + 1 < argc)
{
if(!QString::compare(QString::fromUtf8(argv[i + 1]), lit("root")))
RENDERDOC_UpdateVulkanLayerRegistration(true);
else
RENDERDOC_UpdateVulkanLayerRegistration(false);
return 0;
}
}
bool updateApplied = false;
for(int i = 0; i < argc; i++)
{
if(!QString::compare(QString::fromUtf8(argv[i]), lit("--updatefailed"), Qt::CaseInsensitive))
{
if(i < argc - 1)
RDDialog::critical(NULL, QApplication::translate("qrenderdoc", "Error updating"),
QApplication::translate("qrenderdoc", "Error applying update: %1")
.arg(QString::fromUtf8(argv[i + 1])));
else
RDDialog::critical(NULL, QApplication::translate("qrenderdoc", "Error updating"),
QApplication::translate("qrenderdoc", "Unknown error applying update"));
}
if(!QString::compare(QString::fromUtf8(argv[i]), lit("--updatedone"), Qt::CaseInsensitive))
{
updateApplied = true;
RENDERDOC_UpdateInstalledVersionNumber();
}
}
QString remoteHost;
uint remoteIdent = 0;
for(int i = 0; i + 1 < argc; i++)
{
if(!QString::compare(QString::fromUtf8(argv[i]), lit("--remoteaccess"), Qt::CaseInsensitive) ||
!QString::compare(QString::fromUtf8(argv[i]), lit("--targetcontrol"), Qt::CaseInsensitive))
{
QRegularExpression regexp(lit("^([a-zA-Z0-9_-]+:)?([0-9]+)$"));
QRegularExpressionMatch match = regexp.match(QString::fromUtf8(argv[i + 1]));
if(match.hasMatch())
{
QString host = match.captured(1);
if(host.length() > 0 && host[host.length() - 1] == QLatin1Char(':'))
host.chop(1);
bool ok = false;
uint32_t ident = match.captured(2).toUInt(&ok);
if(ok)
{
remoteHost = host;
remoteIdent = ident;
}
}
}
}
QString crashReportPath;
if(argc == 3 && !QString::compare(QString::fromUtf8(argv[1]), lit("--crash"), Qt::CaseInsensitive))
{
crashReportPath = QString::fromUtf8(argv[2]);
// 'consume' the report path so it doesn't get opened as a capture file
argc = 2;
}
QList<QString> pyscripts;
for(int i = 0; i + 1 < argc; i++)
{
QString a = QString::fromUtf8(argv[i]);
if(!QString::compare(a, lit("--python"), Qt::CaseInsensitive) ||
!QString::compare(a, lit("--py"), Qt::CaseInsensitive) ||
!QString::compare(a, lit("--script"), Qt::CaseInsensitive))
{
QString f = QString::fromUtf8(argv[i + 1]);
QFileInfo checkFile(f);
if(checkFile.exists() && checkFile.isFile())
{
pyscripts.push_back(f);
}
}
}
if(argc > 1)
{
filename = QString::fromUtf8(argv[argc - 1]);
QFileInfo checkFile(filename);
if(!checkFile.exists() || !checkFile.isFile() || checkFile.suffix().toLower() == lit("py"))
filename = QString();
}
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication application(argc, argv);
RegisterMetatypeConversions();
{
PersistantConfig config;
{
QString configPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir dir(configPath);
if(!dir.exists())
dir.mkpath(configPath);
}
QString configFilename = configFilePath(lit("UI.config"));
if(!config.Load(configFilename))
{
RDDialog::critical(
NULL, CaptureContext::tr("Error loading config"),
CaptureContext::tr(
"Error loading config file\n%1\nA default config is loaded and will be saved out.")
.arg(configFilename));
}
if(!config.Analytics_TotalOptOut)
Analytics::Load();
bool isDarkTheme = IsDarkTheme();
bool styleSet = config.SetStyle();
// unrecognised style, or empty (none set), choose a default
if(!styleSet)
{
config.UIStyle = isDarkTheme ? lit("RDDark") : lit("RDLight");
config.SetStyle();
}
config.SetupFormatting();
Resources::Initialise();
GUIInvoke::init();
{
GlobalEnvironment env;
#if defined(RENDERDOC_PLATFORM_LINUX)
env.xlibDisplay = QX11Info::display();
#endif
rdcarray<rdcstr> args;
if(!crashReportPath.isEmpty())
args.push_back("--crash");
RENDERDOC_InitGlobalEnv(env, args);
}
if(!crashReportPath.isEmpty())
{
QFile f(crashReportPath);
if(f.exists() && f.open(QIODevice::ReadOnly | QIODevice::Text))
{
QVariantMap json = JSONToVariant(QString::fromUtf8(f.readAll()));
if(json.contains(lit("report")))
{
CrashDialog dialog(config, json);
RDDialog::show(&dialog);
}
}
}
else
{
PythonContext::GlobalInit();
CaptureContext ctx(filename, remoteHost, remoteIdent, temp, config);
Analytics::Prompt(ctx, config);
ANALYTIC_SET(Environment.RenderDocVersion, lit(FULL_VERSION_STRING));
#if defined(DISTRIBUTION_VERSION)
ANALYTIC_SET(Environment.DistributionVersion, lit(DISTRIBUTION_NAME));
#endif
ANALYTIC_SET(Environment.Bitness, ((sizeof(void *) == sizeof(uint64_t)) ? 64 : 32));
ANALYTIC_SET(Environment.OSVersion, QSysInfo::prettyProductName());
#if RENDERDOC_STABLE_BUILD
ANALYTIC_SET(Environment.OfficialBuildRun, true);
#else
ANALYTIC_SET(Environment.DevelBuildRun, true);
#endif
ANALYTIC_SET(DaysUsed[QDateTime::currentDateTime().date().day()], true);
if(!pyscripts.isEmpty())
{
PythonContextHandle py;
ANALYTIC_SET(UIFeatures.PythonInterop, true);
py.ctx().setGlobal("pyrenderdoc", (ICaptureContext *)&ctx);
QObject::connect(&py.ctx(), &PythonContext::exception,
[](const QString &type, const QString &value, int, QList<QString> frames) {
QString exString;
if(!frames.isEmpty())
{
exString += QApplication::translate(
"qrenderdoc", "Traceback (most recent call last):\n");
for(const QString &f : frames)
exString += QFormatStr(" %1\n").arg(f);
}
exString += QFormatStr("%1: %2\n").arg(type).arg(value);
qCritical("%s", exString.toUtf8().data());
});
QObject::connect(&py.ctx(), &PythonContext::textOutput,
[](bool isStdError, const QString &output) {
if(isStdError)
qCritical("%s", output.toUtf8().data());
else
qInfo("%s", output.toUtf8().data());
});
for(const QString &f : pyscripts)
{
qInfo() << "running" << f;
py.ctx().executeFile(f);
}
}
if(updateApplied)
{
config.CheckUpdate_UpdateAvailable = false;
config.CheckUpdate_UpdateResponse = "";
config.Save();
}
while(ctx.isRunning())
{
application.processEvents(QEventLoop::WaitForMoreEvents);
QCoreApplication::sendPostedEvents();
QCoreApplication::sendPostedEvents(NULL, QEvent::DeferredDelete);
}
config.Save();
}
PythonContext::GlobalShutdown();
Formatter::shutdown();
}
return 0;
}