diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aec31f2de4..6ffb7f6dee 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -490,7 +490,7 @@ public: // Don't actually crash in debug builds, in case this apparent deadlock is simply from // the developer actively debugging code #ifdef NDEBUG - deadlockDetectionCrash(); + deadlockDetectionCrash(); #endif } } @@ -773,7 +773,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { steamClient->init(); } - DependencyManager::set(); PROFILE_SET_THREAD_NAME("Main Thread"); #if defined(Q_OS_WIN) @@ -958,10 +957,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); setProperty(hifi::properties::CRASHED, _previousSessionCrashed); - { const QString TEST_SCRIPT = "--testScript"; - const QString TRACE_FILE = "--traceFile"; const QStringList args = arguments(); for (int i = 0; i < args.size() - 1; ++i) { if (args.at(i) == TEST_SCRIPT) { @@ -969,10 +966,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo if (QFileInfo(testScriptPath).exists()) { setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath)); } - } else if (args.at(i) == TRACE_FILE) { - QString traceFilePath = args.at(i + 1); - setProperty(hifi::properties::TRACING, traceFilePath); - DependencyManager::get()->startTracing(); } } } @@ -1018,6 +1011,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto nodeList = DependencyManager::get(); nodeList->startThread(); + const char** constArgv = const_cast(argv); + if (cmdOptionExists(argc, constArgv, "--disableWatchdog")) { + DISABLE_WATCHDOG = true; + } // Set up a watchdog thread to intentionally crash the application on deadlocks if (!DISABLE_WATCHDOG) { (new DeadlockWatchdogThread())->start(); @@ -1227,7 +1224,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); connect(&_entityEditSender, &EntityEditPacketSender::addingEntityWithCertificate, this, &Application::addingEntityWithCertificate); - const char** constArgv = const_cast(argv); QString concurrentDownloadsStr = getCmdOption(argc, constArgv, "--concurrent-downloads"); bool success; int concurrentDownloads = concurrentDownloadsStr.toInt(&success); diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 51ec4b1327..22db128f7e 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -31,6 +31,8 @@ #include "UserActivityLogger.h" #include "MainWindow.h" +#include "Profile.h" + #ifdef Q_OS_WIN extern "C" { typedef int(__stdcall * CHECKMINSPECPROC) (); @@ -40,6 +42,26 @@ extern "C" { int main(int argc, const char* argv[]) { setupHifiApplication(BuildInfo::INTERFACE_NAME); + // Early check for --traceFile argument + auto tracer = DependencyManager::set(); + const char * traceFile = nullptr; + const QString traceFileFlag("--traceFile"); + float traceDuration = 0.0f; + for (int a = 1; a < argc; ++a) { + if (traceFileFlag == argv[a] && argc > a + 1) { + traceFile = argv[a + 1]; + if (argc > a + 2) { + traceDuration = atof(argv[a + 2]); + } + break; + } + } + if (traceFile != nullptr) { + tracer->startTracing(); + } + + PROFILE_SYNC_BEGIN(startup, "main startup", ""); + #ifdef Q_OS_LINUX QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); #endif @@ -235,7 +257,18 @@ int main(int argc, const char* argv[]) { argvExtended.push_back("--ignore-gpu-blacklist"); int argcExtended = (int)argvExtended.size(); + PROFILE_SYNC_END(startup, "main startup", ""); + PROFILE_SYNC_BEGIN(startup, "app full ctor", ""); Application app(argcExtended, const_cast(argvExtended.data()), startupTime, runningMarkerExisted); + PROFILE_SYNC_END(startup, "app full ctor", ""); + + + QTimer exitTimer; + if (traceDuration > 0.0f) { + exitTimer.setSingleShot(true); + QObject::connect(&exitTimer, &QTimer::timeout, &app, &Application::quit); + exitTimer.start(int(1000 * traceDuration)); + } #if 0 // If we failed the OpenGLVersion check, log it. @@ -273,6 +306,11 @@ int main(int argc, const char* argv[]) { qCDebug(interfaceapp, "Created QT Application."); exitCode = app.exec(); server.close(); + + if (traceFile != nullptr) { + tracer->stopTracing(); + tracer->serialize(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/" + traceFile); + } } Application::shutdownPlugins(); diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index e1b68c88ca..7dc6965076 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -129,6 +129,7 @@ void Context::executeFrame(const FramePointer& frame) const { } bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings, const Shader::CompilationHandler& handler) { + PROFILE_RANGE_EX(app, "makeProgram", 0xff4040c0, shader.getID()); // If we're running in another DLL context, we need to fetch the program callback out of the application // FIXME find a way to do this without reliance on Qt app properties if (!_makeProgramCallback) { diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp index aa7898569b..b539a84925 100755 --- a/libraries/gpu/src/gpu/Shader.cpp +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -75,6 +75,7 @@ Shader::Pointer Shader::createGeometry(const Source& source) { } ShaderPointer Shader::createOrReuseProgramShader(Type type, const Pointer& vertexShader, const Pointer& geometryShader, const Pointer& pixelShader) { + PROFILE_RANGE(app, "createOrReuseProgramShader"); ProgramMapKey key(0); if (vertexShader && vertexShader->getType() == VERTEX) { diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp index 9c1bb79355..2da1c41340 100644 --- a/libraries/qml/src/qml/OffscreenSurface.cpp +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -27,6 +27,8 @@ #include "impl/SharedObject.h" #include "impl/TextureCache.h" +#include "Profile.h" + using namespace hifi::qml; using namespace hifi::qml::impl; @@ -284,6 +286,7 @@ void OffscreenSurface::loadInternal(const QUrl& qmlSource, bool createNewContext, QQuickItem* parent, const QmlContextObjectCallback& callback) { + PROFILE_RANGE_EX(app, "OffscreenSurface::loadInternal", 0xffff00ff, 0, { std::make_pair("url", qmlSource.toDisplayString()) }); if (QThread::currentThread() != thread()) { qFatal("Called load on a non-surface thread"); } @@ -304,7 +307,11 @@ void OffscreenSurface::loadInternal(const QUrl& qmlSource, } auto targetContext = contextForUrl(finalQmlSource, parent, createNewContext); - auto qmlComponent = new QQmlComponent(getSurfaceContext()->engine(), finalQmlSource, QQmlComponent::PreferSynchronous); + QQmlComponent* qmlComponent; + { + PROFILE_RANGE(app, "new QQmlComponent"); + qmlComponent = new QQmlComponent(getSurfaceContext()->engine(), finalQmlSource, QQmlComponent::PreferSynchronous); + } if (qmlComponent->isLoading()) { connect(qmlComponent, &QQmlComponent::statusChanged, this, [=](QQmlComponent::Status) { finishQmlLoad(qmlComponent, targetContext, parent, callback); }); @@ -318,6 +325,7 @@ void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, QQuickItem* parent, const QmlContextObjectCallback& callback) { + PROFILE_RANGE(app, "finishQmlLoad"); disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0); if (qmlComponent->isError()) { for (const auto& error : qmlComponent->errors()) { diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp index d593169d94..b2057117f6 100644 --- a/libraries/qml/src/qml/impl/SharedObject.cpp +++ b/libraries/qml/src/qml/impl/SharedObject.cpp @@ -105,7 +105,10 @@ void SharedObject::create(OffscreenSurface* surface) { // Create a QML engine. auto qmlEngine = acquireEngine(surface); - _qmlContext = new QQmlContext(qmlEngine->rootContext(), qmlEngine); + { + PROFILE_RANGE(startup, "new QQmlContext"); + _qmlContext = new QQmlContext(qmlEngine->rootContext(), qmlEngine); + } surface->onRootContextCreated(_qmlContext); emit surface->rootContextCreated(_qmlContext); @@ -175,6 +178,7 @@ static size_t globalEngineRefCount{ 0 }; #endif QQmlEngine* SharedObject::acquireEngine(OffscreenSurface* surface) { + PROFILE_RANGE(startup, "acquireEngine"); Q_ASSERT(QThread::currentThread() == qApp->thread()); QQmlEngine* result = nullptr; diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index 463b45451b..b0842d63cd 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -36,7 +36,7 @@ public: } }; -Engine::Engine() : Task("Engine", EngineTask::JobModel::create()), +Engine::Engine() : Task(EngineTask::JobModel::create("Engine")), _renderContext(std::make_shared()) { } diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 92e22d86f6..35cc66315b 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -134,9 +134,12 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p locations->lightClusterFrustumBufferUnit = -1; } - auto gpuPipeline = gpu::Pipeline::create(program, state); - auto shapePipeline = std::make_shared(gpuPipeline, locations, batchSetter, itemSetter); - addPipelineHelper(filter, key, 0, shapePipeline); + { + PROFILE_RANGE(app, "Pipeline::create"); + auto gpuPipeline = gpu::Pipeline::create(program, state); + auto shapePipeline = std::make_shared(gpuPipeline, locations, batchSetter, itemSetter); + addPipelineHelper(filter, key, 0, shapePipeline); + } } const ShapePipelinePointer ShapePlumber::pickPipeline(RenderArgs* args, const Key& key) const { diff --git a/libraries/shared/src/Profile.cpp b/libraries/shared/src/Profile.cpp index 97def2015a..f3cbbf9262 100644 --- a/libraries/shared/src/Profile.cpp +++ b/libraries/shared/src/Profile.cpp @@ -27,6 +27,7 @@ Q_LOGGING_CATEGORY(trace_simulation_animation, "trace.simulation.animation") Q_LOGGING_CATEGORY(trace_simulation_animation_detail, "trace.simulation.animation.detail") Q_LOGGING_CATEGORY(trace_simulation_physics, "trace.simulation.physics") Q_LOGGING_CATEGORY(trace_simulation_physics_detail, "trace.simulation.physics.detail") +Q_LOGGING_CATEGORY(trace_startup, "trace.startup") Q_LOGGING_CATEGORY(trace_workload, "trace.workload") #if defined(NSIGHT_FOUND) diff --git a/libraries/shared/src/Profile.h b/libraries/shared/src/Profile.h index f2a747afa3..e78ce210c9 100644 --- a/libraries/shared/src/Profile.h +++ b/libraries/shared/src/Profile.h @@ -32,6 +32,7 @@ Q_DECLARE_LOGGING_CATEGORY(trace_simulation_animation) Q_DECLARE_LOGGING_CATEGORY(trace_simulation_animation_detail) Q_DECLARE_LOGGING_CATEGORY(trace_simulation_physics) Q_DECLARE_LOGGING_CATEGORY(trace_simulation_physics_detail) +Q_DECLARE_LOGGING_CATEGORY(trace_startup) Q_DECLARE_LOGGING_CATEGORY(trace_workload) class Duration { diff --git a/libraries/task/src/task/Task.h b/libraries/task/src/task/Task.h index 08df55dddd..022dd99200 100644 --- a/libraries/task/src/task/Task.h +++ b/libraries/task/src/task/Task.h @@ -47,7 +47,7 @@ protected: bool _doAbortTask{ false }; }; -// JobContext class is the base calss for the context object which is passed through all the Job::run calls thoughout the graph of jobs +// JobContext class is the base class for the context object which is passed through all the Job::run calls thoughout the graph of jobs // It is used to communicate to the job::run its context and various state information the job relies on. // It specifically provide access to: // - The taskFlow object allowing for messaging control flow commands from within a Job::run @@ -73,19 +73,21 @@ class JobConcept { public: using Config = JobConfig; - JobConcept(QConfigPointer config) : _config(config) {} + JobConcept(const std::string& name, QConfigPointer config) : _config(config), _name(name) {} virtual ~JobConcept() = default; + + const std::string& getName() const { return _name; } virtual const Varying getInput() const { return Varying(); } virtual const Varying getOutput() const { return Varying(); } virtual QConfigPointer& getConfiguration() { return _config; } virtual void applyConfiguration() = 0; - void setCPURunTime(double mstime) { std::static_pointer_cast(_config)->setCPURunTime(mstime); } QConfigPointer _config; protected: + const std::string _name; }; @@ -122,7 +124,7 @@ public: class Concept : public JobConcept { public: - Concept(QConfigPointer config) : JobConcept(config) {} + Concept(const std::string& name, QConfigPointer config) : JobConcept(name, config) {} virtual ~Concept() = default; virtual void run(const ContextPointer& jobContext) = 0; @@ -143,8 +145,8 @@ public: const Varying getOutput() const override { return _output; } template - Model(const Varying& input, QConfigPointer config, A&&... args) : - Concept(config), + Model(const std::string& name, const Varying& input, QConfigPointer config, A&&... args) : + Concept(name, config), _data(Data(std::forward(args)...)), _input(input), _output(Output()) { @@ -152,12 +154,14 @@ public: } template - static std::shared_ptr create(const Varying& input, A&&... args) { - return std::make_shared(input, std::make_shared(), std::forward(args)...); + static std::shared_ptr create(const std::string& name, const Varying& input, A&&... args) { + return std::make_shared(name, input, std::make_shared(), std::forward(args)...); } void applyConfiguration() override { + Duration profileRange(trace_render(), ("configure::" + JobConcept::getName()).c_str()); + jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); } @@ -173,8 +177,9 @@ public: template using ModelO = Model; template using ModelIO = Model; - Job(std::string name, ConceptPointer concept) : _concept(concept), _name(name) {} + Job(ConceptPointer concept) : _concept(concept) {} + const std::string& getName() const { return _concept->getName(); } const Varying getInput() const { return _concept->getInput(); } const Varying getOutput() const { return _concept->getOutput(); } QConfigPointer& getConfiguration() const { return _concept->getConfiguration(); } @@ -193,9 +198,9 @@ public: } virtual void run(const ContextPointer& jobContext) { - PerformanceTimer perfTimer(_name.c_str()); + PerformanceTimer perfTimer(getName().c_str()); // NOTE: rather than use the PROFILE_RANGE macro, we create a Duration manually - Duration profileRange(jobContext->profileCategory, _name.c_str()); + Duration profileRange(jobContext->profileCategory, ("run::" + getName()).c_str()); auto start = usecTimestampNow(); _concept->run(jobContext); @@ -203,11 +208,8 @@ public: _concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0); } - const std::string& getName() const { return _name; } - protected: ConceptPointer _concept; - std::string _name = ""; }; @@ -230,7 +232,7 @@ public: using ConceptPointer = typename JobType::ConceptPointer; using Jobs = std::vector; - Task(std::string name, ConceptPointer concept) : JobType(name, concept) {} + Task(ConceptPointer concept) : JobType(concept) {} class TaskConcept : public Concept { public: @@ -259,11 +261,11 @@ public: return jobIt; } - TaskConcept(const Varying& input, QConfigPointer config) : Concept(config), _input(input) {} + TaskConcept(const std::string& name, const Varying& input, QConfigPointer config) : Concept(name, config), _input(input) {} // Create a new job in the container's queue; returns the job's output template const Varying addJob(std::string name, const Varying& input, NA&&... args) { - _jobs.emplace_back(name, (NT::JobModel::create(input, std::forward(args)...))); + _jobs.emplace_back((NT::JobModel::create(name, input, std::forward(args)...))); // Conect the child config to this task's config std::static_pointer_cast(Concept::getConfiguration())->connectChildConfig(_jobs.back().getConfiguration(), name); @@ -284,16 +286,18 @@ public: Data _data; - TaskModel(const Varying& input, QConfigPointer config) : - TaskConcept(input, config), + TaskModel(const std::string& name, const Varying& input, QConfigPointer config) : + TaskConcept(name, input, config), _data(Data()) {} template - static std::shared_ptr create(const Varying& input, A&&... args) { - auto model = std::make_shared(input, std::make_shared()); - - model->_data.build(*(model), model->_input, model->_output, std::forward(args)...); + static std::shared_ptr create(const std::string& name, const Varying& input, A&&... args) { + auto model = std::make_shared(name, input, std::make_shared()); + { + Duration profileRange(trace_render(), ("build::" + model->getName()).c_str()); + model->_data.build(*(model), model->_input, model->_output, std::forward(args)...); + } // Recreate the Config to use the templated type model->createConfiguration(); model->applyConfiguration(); @@ -302,9 +306,9 @@ public: } template - static std::shared_ptr create(A&&... args) { + static std::shared_ptr create(const std::string& name, A&&... args) { const auto input = Varying(Input()); - return create(input, std::forward(args)...); + return create(name, input, std::forward(args)...); } void createConfiguration() { @@ -326,6 +330,7 @@ public: } void applyConfiguration() override { + Duration profileRange(trace_render(), ("configure::" + JobConcept::getName()).c_str()); jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); for (auto& job : TaskConcept::_jobs) { job.applyConfiguration(); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 12e9b8b87c..cfff0405c0 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -256,6 +256,15 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) { #if !defined(Q_OS_ANDROID) rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext)); rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext)); + { + PROFILE_RANGE(startup, "FileTypeProfile"); + rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext)); + } + { + PROFILE_RANGE(startup, "HFWebEngineProfile"); + rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext)); + + } #endif rootContext->setContextProperty("Paths", DependencyManager::get().data()); rootContext->setContextProperty("Tablet", DependencyManager::get().data()); diff --git a/libraries/ui/src/ui/OffscreenQmlSurfaceCache.cpp b/libraries/ui/src/ui/OffscreenQmlSurfaceCache.cpp index 7efa36624b..51fe11fdc7 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurfaceCache.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurfaceCache.cpp @@ -12,6 +12,7 @@ #include #include "OffscreenQmlSurface.h" +#include "Profile.h" OffscreenQmlSurfaceCache::OffscreenQmlSurfaceCache() { } @@ -38,6 +39,7 @@ void OffscreenQmlSurfaceCache::reserve(const QString& rootSource, int count) { } void OffscreenQmlSurfaceCache::release(const QString& rootSource, const QSharedPointer& surface) { + PROFILE_RANGE(app, "buildSurface"); surface->pause(); _cache[rootSource].push_back(surface); }