Merge pull request #716 from daleglass-overte/reorganize-startup

Reorganize startup to allow more control over plugins
This commit is contained in:
Dale Glass 2023-12-08 00:26:05 +01:00 committed by GitHub
commit 36c700d175
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 572 additions and 154 deletions

View file

@ -724,8 +724,8 @@ extern DisplayPluginList getDisplayPlugins();
extern InputPluginList getInputPlugins();
extern void saveInputPluginSettings(const InputPluginList& plugins);
bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, bool runningMarkerExisted) {
qInstallMessageHandler(messageHandler);
bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted) {
const int listenPort = parser.isSet("listenPort") ? parser.value("listenPort").toInt() : INVALID_PORT;
@ -743,6 +743,7 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b
bool previousSessionCrashed { false };
if (!inTestMode) {
// TODO: FIX
previousSessionCrashed = CrashRecoveryHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
}
@ -763,13 +764,12 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b
}
}
// Tell the plugin manager about our statically linked plugins
DependencyManager::set<ScriptInitializers>();
DependencyManager::set<PluginManager>();
// Tell the plugin manager about our statically linked plugins
auto pluginManager = PluginManager::getInstance();
pluginManager->setInputPluginProvider([] { return getInputPlugins(); });
pluginManager->setDisplayPluginProvider([] { return getDisplayPlugins(); });
pluginManager->setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); });
if (auto steamClient = pluginManager->getSteamClientPlugin()) {
steamClient->init();
}
@ -777,6 +777,7 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b
oculusPlatform->init();
}
PROFILE_SET_THREAD_NAME("Main Thread");
#if defined(Q_OS_WIN)
@ -993,8 +994,7 @@ bool Application::initMenu() {
Application::Application(
int& argc, char** argv,
const QCommandLineParser& parser,
QElapsedTimer& startupTimer,
bool runningMarkerExisted
QElapsedTimer& startupTimer
) :
QApplication(argc, argv),
_window(new MainWindow(desktop())),
@ -1004,10 +1004,7 @@ Application::Application(
#ifndef Q_OS_ANDROID
_logger(new FileLogger(this)),
#endif
_previousSessionCrashed(setupEssentials(argc, argv, parser, runningMarkerExisted)),
_entitySimulation(std::make_shared<PhysicalEntitySimulation>()),
_physicsEngine(std::make_shared<PhysicsEngine>(Vectors::ZERO)),
_entityClipboard(std::make_shared<EntityTree>()),
_previousSessionCrashed(false), //setupEssentials(parser, false)),
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
_hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT),
@ -1032,12 +1029,72 @@ Application::Application(
_snapshotSound(nullptr),
_sampleSound(nullptr)
{
auto steamClient = PluginManager::getInstance()->getSteamClientPlugin();
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
setProperty(hifi::properties::CRASHED, _previousSessionCrashed);
LogHandler::getInstance().moveToThread(thread());
LogHandler::getInstance().setupRepeatedMessageFlusher();
qInstallMessageHandler(messageHandler);
DependencyManager::set<PathUtils>();
}
void Application::initializePluginManager(const QCommandLineParser& parser) {
DependencyManager::set<PluginManager>();
auto pluginManager = PluginManager::getInstance();
// To avoid any confusion: the getInputPlugins and getDisplayPlugins are not the ones
// from PluginManager, but functions exported by input-plugins/InputPlugin.cpp and
// display-plugins/DisplayPlugin.cpp.
//
// These functions provide the plugin manager with static default plugins.
pluginManager->setInputPluginProvider([] { return getInputPlugins(); });
pluginManager->setDisplayPluginProvider([] { return getDisplayPlugins(); });
pluginManager->setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); });
// This must be a member function -- PluginManager must exist, and for that
// QApplication must exist, or it can't find the plugin path, as QCoreApplication:applicationDirPath
// won't work yet.
if (parser.isSet("display")) {
auto preferredDisplays = parser.value("display").split(',', Qt::SkipEmptyParts);
qInfo() << "Setting prefered display plugins:" << preferredDisplays;
PluginManager::getInstance()->setPreferredDisplayPlugins(preferredDisplays);
}
if (parser.isSet("disableDisplayPlugins")) {
auto disabledDisplays = parser.value("disableDisplayPlugins").split(',', Qt::SkipEmptyParts);
qInfo() << "Disabling following display plugins:" << disabledDisplays;
PluginManager::getInstance()->disableDisplays(disabledDisplays);
}
if (parser.isSet("disableInputPlugins")) {
auto disabledInputs = parser.value("disableInputPlugins").split(',', Qt::SkipEmptyParts);
qInfo() << "Disabling following input plugins:" << disabledInputs;
PluginManager::getInstance()->disableInputs(disabledInputs);
}
}
void Application::initialize(const QCommandLineParser &parser) {
//qCDebug(interfaceapp) << "Setting up essentials";
setupEssentials(parser, _previousSessionCrashed);
qCDebug(interfaceapp) << "Initializing application";
_entitySimulation = std::make_shared<PhysicalEntitySimulation>();
_physicsEngine = std::make_shared<PhysicsEngine>(Vectors::ZERO);
_entityClipboard = std::make_shared<EntityTree>();
_octreeProcessor = std::make_shared<OctreePacketProcessor>();
_entityEditSender = std::make_shared<EntityEditPacketSender>();
_graphicsEngine = std::make_shared<GraphicsEngine>();
_applicationOverlay = std::make_shared<ApplicationOverlay>();
auto steamClient = PluginManager::getInstance()->getSteamClientPlugin();
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
{
if (parser.isSet("testScript")) {
@ -1405,7 +1462,7 @@ Application::Application(
connect(myAvatar.get(), &MyAvatar::positionGoneTo, this, [this] {
if (!_physicsEnabled) {
// when we arrive somewhere without physics enabled --> startSafeLanding
_octreeProcessor.startSafeLanding();
_octreeProcessor->startSafeLanding();
}
}, Qt::QueuedConnection);
@ -1578,9 +1635,9 @@ Application::Application(
qCDebug(interfaceapp, "init() complete.");
// create thread for parsing of octree data independent of the main network and rendering threads
_octreeProcessor.initialize(_enableProcessOctreeThread);
connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);
_entityEditSender.initialize(_enableProcessOctreeThread);
_octreeProcessor->initialize(_enableProcessOctreeThread);
connect(_octreeProcessor.get(), &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);
_entityEditSender->initialize(_enableProcessOctreeThread);
_idleLoopStdev.reset();
@ -1698,7 +1755,7 @@ Application::Application(
userActivityLogger.logAction("launch", properties);
}
_entityEditSender.setMyAvatar(myAvatar.get());
_entityEditSender->setMyAvatar(myAvatar.get());
// The entity octree will have to know about MyAvatar for the parentJointName import
getEntities()->getTree()->setMyAvatar(myAvatar);
@ -1707,7 +1764,7 @@ Application::Application(
// For now we're going to set the PPS for outbound packets to be super high, this is
// probably not the right long term solution. But for now, we're going to do this to
// allow you to move an entity around in your hand
_entityEditSender.setPacketsPerSecond(3000); // super high!!
_entityEditSender->setPacketsPerSecond(3000); // super high!!
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
@ -2375,7 +2432,7 @@ Application::Application(
connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
connect(_window, SIGNAL(windowMinimizedChanged(bool)), this, SLOT(windowMinimizedChanged(bool)));
qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);
qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)_sessionRunTimer.elapsed() / 1000.0);
EntityTreeRenderer::setEntitiesShouldFadeFunction([this]() {
SharedNodePointer entityServerNode = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::EntityServer);
@ -2572,7 +2629,7 @@ Application::Application(
}
_pendingIdleEvent = false;
_graphicsEngine.startup();
_graphicsEngine->startup();
qCDebug(interfaceapp) << "Directory Service session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
@ -2879,43 +2936,59 @@ void Application::cleanupBeforeQuit() {
Application::~Application() {
// remove avatars from physics engine
auto avatarManager = DependencyManager::get<AvatarManager>();
avatarManager->clearOtherAvatars();
auto myCharacterController = getMyAvatar()->getCharacterController();
myCharacterController->clearDetailedMotionStates();
if (auto avatarManager = DependencyManager::get<AvatarManager>()) {
// AvatarManager may not yet exist in case of an early exit
PhysicsEngine::Transaction transaction;
avatarManager->buildPhysicsTransaction(transaction);
_physicsEngine->processTransaction(transaction);
avatarManager->handleProcessedPhysicsTransaction(transaction);
avatarManager->deleteAllAvatars();
avatarManager->clearOtherAvatars();
auto myCharacterController = getMyAvatar()->getCharacterController();
myCharacterController->clearDetailedMotionStates();
_physicsEngine->setCharacterController(nullptr);
PhysicsEngine::Transaction transaction;
avatarManager->buildPhysicsTransaction(transaction);
_physicsEngine->processTransaction(transaction);
avatarManager->handleProcessedPhysicsTransaction(transaction);
avatarManager->deleteAllAvatars();
}
if (_physicsEngine) {
_physicsEngine->setCharacterController(nullptr);
}
// the _shapeManager should have zero references
_shapeManager.collectGarbage();
assert(_shapeManager.getNumShapes() == 0);
// shutdown graphics engine
_graphicsEngine.shutdown();
if (_graphicsEngine) {
// shutdown graphics engine
_graphicsEngine->shutdown();
}
_gameWorkload.shutdown();
DependencyManager::destroy<Preferences>();
PlatformHelper::shutdown();
_entityClipboard->eraseAllOctreeElements();
_entityClipboard.reset();
_octreeProcessor.terminate();
_entityEditSender.terminate();
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
steamClient->shutdown();
if (_entityClipboard) {
_entityClipboard->eraseAllOctreeElements();
_entityClipboard.reset();
}
if (auto oculusPlatform = PluginManager::getInstance()->getOculusPlatformPlugin()) {
oculusPlatform->shutdown();
if (_octreeProcessor) {
_octreeProcessor->terminate();
}
if (_entityEditSender) {
_entityEditSender->terminate();
}
if (auto pluginManager = PluginManager::getInstance()) {
if (auto steamClient = pluginManager->getSteamClientPlugin()) {
steamClient->shutdown();
}
if (auto oculusPlatform = pluginManager->getOculusPlatformPlugin()) {
oculusPlatform->shutdown();
}
}
DependencyManager::destroy<PluginManager>();
@ -2943,7 +3016,9 @@ Application::~Application() {
DependencyManager::destroy<GeometryCache>();
DependencyManager::destroy<ScreenshareScriptingInterface>();
DependencyManager::get<ResourceManager>()->cleanup();
if (auto resourceManager = DependencyManager::get<ResourceManager>()) {
resourceManager->cleanup();
}
// remove the NodeList from the DependencyManager
DependencyManager::destroy<NodeList>();
@ -2957,13 +3032,14 @@ Application::~Application() {
_window->deleteLater();
// make sure that the quit event has finished sending before we take the application down
auto closeEventSender = DependencyManager::get<CloseEventSender>();
while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) {
// sleep a little so we're not spinning at 100%
std::this_thread::sleep_for(std::chrono::milliseconds(10));
if (auto closeEventSender = DependencyManager::get<CloseEventSender>()) {
while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) {
// sleep a little so we're not spinning at 100%
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
// quit the thread used by the closure event sender
closeEventSender->thread()->quit();
}
// quit the thread used by the closure event sender
closeEventSender->thread()->quit();
// Can't log to file past this point, FileLogger about to be deleted
qInstallMessageHandler(LogHandler::verboseMessageHandler);
@ -3103,7 +3179,7 @@ void Application::initializeGL() {
glClear(GL_COLOR_BUFFER_BIT);
_glWidget->swapBuffers();
_graphicsEngine.initializeGPU(_glWidget);
_graphicsEngine->initializeGPU(_glWidget);
}
void Application::initializeDisplayPlugins() {
@ -3115,7 +3191,7 @@ void Application::initializeDisplayPlugins() {
// Once time initialization code
DisplayPluginPointer targetDisplayPlugin;
for(const auto& displayPlugin : displayPlugins) {
displayPlugin->setContext(_graphicsEngine.getGPUContext());
displayPlugin->setContext(_graphicsEngine->getGPUContext());
if (displayPlugin->getName() == lastActiveDisplayPluginName) {
targetDisplayPlugin = displayPlugin;
}
@ -3167,7 +3243,7 @@ void Application::initializeDisplayPlugins() {
void Application::initializeRenderEngine() {
// FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread.
DeadlockWatchdogThread::withPause([&] {
_graphicsEngine.initializeRender();
_graphicsEngine->initializeRender();
DependencyManager::get<Keyboard>()->registerKeyboardHighlighting();
});
}
@ -3424,7 +3500,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data());
surfaceContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
surfaceContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
surfaceContext->setContextProperty("FrameTimings", &_graphicsEngine._frameTimingsScriptingInterface);
surfaceContext->setContextProperty("FrameTimings", &_graphicsEngine->_frameTimingsScriptingInterface);
surfaceContext->setContextProperty("Rates", new RatesScriptingInterface(this));
surfaceContext->setContextProperty("TREE_SCALE", TREE_SCALE);
@ -4060,7 +4136,7 @@ std::map<QString, QString> Application::prepareServerlessDomainContents(QUrl dom
bool success = tmpTree->readFromByteArray(domainURL.toString(), data);
if (success) {
tmpTree->reaverageOctreeElements();
tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), "domain", 0, 0, 0);
tmpTree->sendEntities(_entityEditSender.get(), getEntities()->getTree(), "domain", 0, 0, 0);
}
std::map<QString, QString> namedPaths = tmpTree->getNamedPaths();
@ -4130,8 +4206,8 @@ void Application::onPresent(quint32 frameCount) {
postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority);
}
expected = false;
if (_graphicsEngine.checkPendingRenderEvent() && !isAboutToQuit()) {
postEvent(_graphicsEngine._renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render));
if (_graphicsEngine->checkPendingRenderEvent() && !isAboutToQuit()) {
postEvent(_graphicsEngine->_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render));
}
}
@ -4201,7 +4277,9 @@ bool Application::handleFileOpenEvent(QFileOpenEvent* fileEvent) {
}
bool Application::notify(QObject * object, QEvent * event) {
if (thread() == QThread::currentThread()) {
if (thread() == QThread::currentThread() && _profilingInitialized ) {
// _profilingInitialized gets set once we're reading for profiling.
// this prevents a deadlock due to profiling not working yet
PROFILE_RANGE_IF_LONGER(app, "notify", 2)
return QApplication::notify(object, event);
}
@ -5246,8 +5324,8 @@ void Application::idle() {
PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", uint32_t, ResourceCache::getPendingRequestCount());
PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get<StatTracker>()->getStat("Processing").toInt());
PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get<StatTracker>()->getStat("PendingProcessing").toInt());
auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration();
PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_graphicsEngine.getGPUContext()->getFrameTimerGPUAverage());
auto renderConfig = _graphicsEngine->getRenderEngine()->getConfiguration();
PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_graphicsEngine->getGPUContext()->getFrameTimerGPUAverage());
PROFILE_RANGE(app, __FUNCTION__);
@ -5614,7 +5692,7 @@ bool Application::importEntities(const QString& urlOrFilename, const bool isObse
}
QVector<EntityItemID> Application::pasteEntities(const QString& entityHostType, float x, float y, float z) {
return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), entityHostType, x, y, z);
return _entityClipboard->sendEntities(_entityEditSender.get(), getEntities()->getTree(), entityHostType, x, y, z);
}
void Application::init() {
@ -5664,7 +5742,7 @@ void Application::init() {
_physicsEngine->init();
EntityTreePointer tree = getEntities()->getTree();
_entitySimulation->init(tree, _physicsEngine, &_entityEditSender);
_entitySimulation->init(tree, _physicsEngine, _entityEditSender.get());
tree->setSimulation(_entitySimulation);
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
@ -5688,7 +5766,7 @@ void Application::init() {
}
}, Qt::QueuedConnection);
_gameWorkload.startup(getEntities()->getWorkloadSpace(), _graphicsEngine.getRenderScene(), _entitySimulation);
_gameWorkload.startup(getEntities()->getWorkloadSpace(), _graphicsEngine->getRenderScene(), _entitySimulation);
_entitySimulation->setWorkloadSpace(getEntities()->getWorkloadSpace());
}
@ -5860,7 +5938,7 @@ void Application::updateLOD(float deltaTime) const {
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
if (!isThrottleRendering()) {
float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
float engineRunTime = (float)(_graphicsEngine.getRenderEngine()->getConfiguration().get()->getCPURunTime());
float engineRunTime = (float)(_graphicsEngine->getRenderEngine()->getConfiguration().get()->getCPURunTime());
float gpuTime = getGPUContext()->getFrameTimerGPUAverage();
float batchTime = getGPUContext()->getFrameTimerBatchAverage();
auto lodManager = DependencyManager::get<LODManager>();
@ -5896,8 +5974,8 @@ void Application::updateThreads(float deltaTime) {
// parse voxel packets
if (!_enableProcessOctreeThread) {
_octreeProcessor.threadRoutine();
_entityEditSender.threadRoutine();
_octreeProcessor->threadRoutine();
_entityEditSender->threadRoutine();
}
}
@ -6020,7 +6098,7 @@ void Application::resetPhysicsReadyInformation() {
_gpuTextureMemSizeStabilityCount = 0;
_gpuTextureMemSizeAtLastCheck = 0;
_physicsEnabled = false;
_octreeProcessor.stopSafeLanding();
_octreeProcessor->stopSafeLanding();
}
void Application::reloadResourceCaches() {
@ -6165,7 +6243,7 @@ void Application::updateSecondaryCameraViewFrustum() {
// camera should be.
// Code based on SecondaryCameraJob
auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration();
auto renderConfig = _graphicsEngine->getRenderEngine()->getConfiguration();
assert(renderConfig);
auto camera = dynamic_cast<SecondaryCameraJobConfig*>(renderConfig->getConfig("SecondaryCamera"));
@ -6283,7 +6361,7 @@ void Application::tryToEnablePhysics() {
auto myAvatar = getMyAvatar();
if (myAvatar->isReadyForPhysics()) {
myAvatar->getCharacterController()->setPhysicsEngine(_physicsEngine);
_octreeProcessor.resetSafeLanding();
_octreeProcessor->resetSafeLanding();
_physicsEnabled = true;
setIsInterstitialMode(false);
myAvatar->updateMotionBehaviorFromMenu();
@ -6292,7 +6370,7 @@ void Application::tryToEnablePhysics() {
}
void Application::update(float deltaTime) {
PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_graphicsEngine._renderFrameCount + 1);
PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_graphicsEngine->_renderFrameCount + 1);
if (_aboutToQuit) {
return;
@ -6309,12 +6387,12 @@ void Application::update(float deltaTime) {
if (isServerlessMode()) {
tryToEnablePhysics();
} else if (_failedToConnectToEntityServer) {
if (_octreeProcessor.safeLandingIsActive()) {
_octreeProcessor.stopSafeLanding();
if (_octreeProcessor->safeLandingIsActive()) {
_octreeProcessor->stopSafeLanding();
}
} else {
_octreeProcessor.updateSafeLanding();
if (_octreeProcessor.safeLandingIsComplete()) {
_octreeProcessor->updateSafeLanding();
if (_octreeProcessor->safeLandingIsComplete()) {
tryToEnablePhysics();
}
}
@ -6801,7 +6879,7 @@ void Application::update(float deltaTime) {
}
void Application::updateRenderArgs(float deltaTime) {
_graphicsEngine.editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) {
_graphicsEngine->editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) {
PerformanceTimer perfTimer("editRenderArgs");
appRenderArgs._headPose = getHMDSensorPose();
@ -6830,7 +6908,7 @@ void Application::updateRenderArgs(float deltaTime) {
_viewFrustum.setProjection(adjustedProjection);
_viewFrustum.calculate();
}
appRenderArgs._renderArgs = RenderArgs(_graphicsEngine.getGPUContext(), lodManager->getVisibilityDistance(),
appRenderArgs._renderArgs = RenderArgs(_graphicsEngine->getGPUContext(), lodManager->getVisibilityDistance(),
lodManager->getBoundaryLevelAdjust(), lodManager->getLODFarHalfAngleTan(), lodManager->getLODNearHalfAngleTan(),
lodManager->getLODFarDistance(), lodManager->getLODNearDistance(), RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::MONO, RenderArgs::DEFERRED, RenderArgs::RENDER_DEBUG_NONE);
@ -6969,7 +7047,7 @@ int Application::sendNackPackets() {
// if there are octree packets from this node that are waiting to be processed,
// don't send a NACK since the missing packets may be among those waiting packets.
if (_octreeProcessor.hasPacketsToProcessFrom(nodeUUID)) {
if (_octreeProcessor->hasPacketsToProcessFrom(nodeUUID)) {
return;
}
@ -7011,7 +7089,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
const bool isModifiedQuery = !_physicsEnabled;
if (isModifiedQuery) {
if (!_octreeProcessor.safeLandingIsActive()) {
if (!_octreeProcessor->safeLandingIsActive()) {
// don't send the octreeQuery until SafeLanding knows it has started
return;
}
@ -7280,12 +7358,12 @@ void Application::resettingDomain() {
void Application::nodeAdded(SharedNodePointer node) {
if (node->getType() == NodeType::EntityServer) {
if (_failedToConnectToEntityServer && !_entityServerConnectionTimer.isActive()) {
_octreeProcessor.stopSafeLanding();
_octreeProcessor->stopSafeLanding();
_failedToConnectToEntityServer = false;
} else if (_entityServerConnectionTimer.isActive()) {
_entityServerConnectionTimer.stop();
}
_octreeProcessor.startSafeLanding();
_octreeProcessor->startSafeLanding();
_entityServerConnectionTimer.setInterval(ENTITY_SERVER_CONNECTION_TIMEOUT);
_entityServerConnectionTimer.start();
}
@ -7357,9 +7435,9 @@ void Application::nodeKilled(SharedNodePointer node) {
// OctreePacketProcessor::nodeKilled is not being called when NodeList::nodeKilled is emitted.
// This may have to do with GenericThread::threadRoutine() blocking the QThread event loop
_octreeProcessor.nodeKilled(node);
_octreeProcessor->nodeKilled(node);
_entityEditSender.nodeKilled(node);
_entityEditSender->nodeKilled(node);
if (node->getType() == NodeType::AudioMixer) {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled");
@ -7448,7 +7526,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptManagerPoint
// setup the packet sender of the script engine's scripting interfaces so
// we can use the same ones from the application.
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
entityScriptingInterface->setPacketSender(&_entityEditSender);
entityScriptingInterface->setPacketSender(_entityEditSender.get());
entityScriptingInterface->setEntityTree(getEntities()->getTree());
if (property(hifi::properties::TEST).isValid()) {
@ -8762,26 +8840,6 @@ void Application::sendLambdaEvent(const std::function<void()>& f) {
}
}
void Application::initPlugins(const QCommandLineParser& parser) {
if (parser.isSet("display")) {
auto preferredDisplays = parser.value("display").split(',', Qt::SkipEmptyParts);
qInfo() << "Setting prefered display plugins:" << preferredDisplays;
PluginManager::getInstance()->setPreferredDisplayPlugins(preferredDisplays);
}
if (parser.isSet("disable-displays")) {
auto disabledDisplays = parser.value("disable-displays").split(',', Qt::SkipEmptyParts);
qInfo() << "Disabling following display plugins:" << disabledDisplays;
PluginManager::getInstance()->disableDisplays(disabledDisplays);
}
if (parser.isSet("disable-inputs")) {
auto disabledInputs = parser.value("disable-inputs").split(',', Qt::SkipEmptyParts);
qInfo() << "Disabling following input plugins:" << disabledInputs;
PluginManager::getInstance()->disableInputs(disabledInputs);
}
}
void Application::shutdownPlugins() {
}

View file

@ -123,6 +123,31 @@ class Application : public QApplication,
friend class OctreePacketProcessor;
public:
/**
* @brief Initialize the plugin manager
*
* This both does the initial startup and parses arguments. This
* is necessary because the plugin manager's options must be set
* before any usage of it is made, or they won't apply.
*
* @param parser
*/
void initializePluginManager(const QCommandLineParser& parser);
/**
* @brief Initialize everything
*
* This is a QApplication, and for Qt reasons it's desirable to create this object
* as early as possible. Without that some Qt functions don't work, like QCoreApplication::applicationDirPath()
*
* So we keep the constructor as minimal as possible, and do the rest of the work in
* this function.
*/
void initialize(const QCommandLineParser &parser);
void setPreviousSessionCrashed(bool value) { _previousSessionCrashed = value; }
// virtual functions required for PluginContainer
virtual ui::Menu* getPrimaryMenu() override;
virtual void requestReset() override { resetSensors(false); }
@ -135,15 +160,12 @@ public:
virtual DisplayPluginPointer getActiveDisplayPlugin() const override;
// FIXME? Empty methods, do we still need them?
static void initPlugins(const QCommandLineParser& parser);
static void shutdownPlugins();
Application(
int& argc, char** argv,
const QCommandLineParser& parser,
QElapsedTimer& startup_time,
bool runningMarkerExisted
QElapsedTimer& startup_time
);
~Application();
@ -197,16 +219,16 @@ public:
const ConicalViewFrustums& getConicalViews() const override { return _conicalViews; }
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
const OctreePacketProcessor& getOctreePacketProcessor() const { return *_octreeProcessor; }
QSharedPointer<EntityTreeRenderer> getEntities() const { return DependencyManager::get<EntityTreeRenderer>(); }
MainWindow* getWindow() const { return _window; }
EntityTreePointer getEntityClipboard() const { return _entityClipboard; }
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
std::shared_ptr<EntityEditPacketSender> getEntityEditPacketSender() { return _entityEditSender; }
ivec2 getMouse() const;
ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; }
const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; }
ApplicationOverlay& getApplicationOverlay() { return *_applicationOverlay; }
const ApplicationOverlay& getApplicationOverlay() const { return *_applicationOverlay; }
CompositorHelper& getApplicationCompositor() const;
Overlays& getOverlays() { return _overlays; }
@ -214,8 +236,8 @@ public:
PerformanceManager& getPerformanceManager() { return _performanceManager; }
RefreshRateManager& getRefreshRateManager() { return _refreshRateManager; }
size_t getRenderFrameCount() const { return _graphicsEngine.getRenderFrameCount(); }
float getRenderLoopRate() const { return _graphicsEngine.getRenderLoopRate(); }
size_t getRenderFrameCount() const { return _graphicsEngine->getRenderFrameCount(); }
float getRenderLoopRate() const { return _graphicsEngine->getRenderLoopRate(); }
float getNumCollisionObjects() const;
float getTargetRenderFrameRate() const; // frames/second
@ -293,9 +315,9 @@ public:
void setMaxOctreePacketsPerSecond(int maxOctreePPS);
int getMaxOctreePacketsPerSecond() const;
render::ScenePointer getMain3DScene() override { return _graphicsEngine.getRenderScene(); }
render::EnginePointer getRenderEngine() override { return _graphicsEngine.getRenderEngine(); }
gpu::ContextPointer getGPUContext() const { return _graphicsEngine.getGPUContext(); }
render::ScenePointer getMain3DScene() override { return _graphicsEngine->getRenderScene(); }
render::EnginePointer getRenderEngine() override { return _graphicsEngine->getRenderEngine(); }
gpu::ContextPointer getGPUContext() const { return _graphicsEngine->getGPUContext(); }
const GameWorkload& getGameWorkload() const { return _gameWorkload; }
@ -709,8 +731,8 @@ private:
bool _enableProcessOctreeThread;
bool _interstitialMode { false };
OctreePacketProcessor _octreeProcessor;
EntityEditPacketSender _entityEditSender;
std::shared_ptr<OctreePacketProcessor> _octreeProcessor;
std::shared_ptr<EntityEditPacketSender> _entityEditSender;
StDev _idleLoopStdev;
float _idleLoopMeasuredJitter;
@ -757,13 +779,13 @@ private:
GameWorkload _gameWorkload;
GraphicsEngine _graphicsEngine;
std::shared_ptr<GraphicsEngine> _graphicsEngine;
void updateRenderArgs(float deltaTime);
bool _disableLoginScreen { true };
Overlays _overlays;
ApplicationOverlay _applicationOverlay;
std::shared_ptr<ApplicationOverlay> _applicationOverlay;
OverlayConductor _overlayConductor;
DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
@ -860,5 +882,7 @@ private:
bool _crashOnShutdown { false };
DiscordPresence* _discordPresence{ nullptr };
bool _profilingInitialized { false };
};
#endif // hifi_Application_h

View file

@ -1035,8 +1035,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
std::pair<bool, bool> zoneInteractionProperties;
entityTree->withWriteLock([&] {
zoneInteractionProperties = entityTreeRenderer->getZoneInteractionProperties();
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
entityTree->updateEntityQueryAACube(shared_from_this(), packetSender, false, true);
std::shared_ptr<EntityEditPacketSender> packetSender = qApp->getEntityEditPacketSender();
entityTree->updateEntityQueryAACube(shared_from_this(), packetSender.get(), false, true);
});
bool isPhysicsEnabled = qApp->isPhysicsEnabled();
bool zoneAllowsFlying = zoneInteractionProperties.first;
@ -1729,7 +1729,7 @@ void MyAvatar::handleChangedAvatarEntityData() {
entityTree->deleteEntitiesByID(entitiesToDelete);
// ADD real entities
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
auto packetSender = qApp->getEntityEditPacketSender();
for (const auto& id : entitiesToAdd) {
bool blobFailed = false;
EntityItemProperties properties;
@ -4231,7 +4231,7 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) {
_avatarEntitiesLock.withReadLock([&] {
avatarEntityIDs = _packedAvatarEntityData.keys();
});
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
auto packetSender = qApp->getEntityEditPacketSender();
entityTree->withWriteLock([&] {
for (const auto& entityID : avatarEntityIDs) {
auto entity = entityTree->findEntityByID(entityID);
@ -6888,7 +6888,7 @@ void MyAvatar::sendPacket(const QUuid& entityID) const {
if (entityTree) {
entityTree->withWriteLock([&] {
// force an update packet
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
auto packetSender = qApp->getEntityEditPacketSender();
packetSender->queueEditAvatarEntityMessage(entityTree, entityID);
});
}

View file

@ -24,6 +24,7 @@
#include <SharedUtil.h>
#include <NetworkAccessManager.h>
#include <gl/GLHelpers.h>
#include <iostream>
#include "AddressManager.h"
#include "Application.h"
@ -33,6 +34,9 @@
#include "MainWindow.h"
#include "Profile.h"
#include "LogHandler.h"
#include <plugins/PluginManager.h>
#include <plugins/DisplayPlugin.h>
#include <plugins/CodecPlugin.h>
#ifdef Q_OS_WIN
#include <Windows.h>
@ -63,11 +67,24 @@ int main(int argc, const char* argv[]) {
}
#endif
// Setup QCoreApplication settings, install log message handler
setupHifiApplication(BuildInfo::INTERFACE_NAME);
// Journald by default in user applications is probably a bit too modern still.
LogHandler::getInstance().setShouldUseJournald(false);
// Extend argv to enable WebGL rendering
std::vector<const char*> argvExtended(&argv[0], &argv[argc]);
argvExtended.push_back("--ignore-gpu-blocklist");
#ifdef Q_OS_ANDROID
argvExtended.push_back("--suppress-settings-reset");
#endif
int argcExtended = (int)argvExtended.size();
QElapsedTimer startupTime;
startupTime.start();
QCommandLineParser parser;
parser.setApplicationDescription("Overte -- A free/libre and open-source virtual worlds client");
QCommandLineOption helpOption = parser.addHelpOption();
@ -125,12 +142,12 @@ int main(int argc, const char* argv[]) {
"displays"
);
QCommandLineOption disableDisplaysOption(
"disable-displays",
"disableDisplayPlugins",
"Displays to disable. Valid options include \"OpenVR (Vive)\" and \"Oculus Rift\"",
"string"
);
QCommandLineOption disableInputsOption(
"disable-inputs",
"disableInputPlugins",
"Inputs to disable. Valid options include \"OpenVR (Vive)\" and \"Oculus Rift\"",
"string"
);
@ -246,6 +263,19 @@ int main(int argc, const char* argv[]) {
"Logging options, comma separated: color,nocolor,process_id,thread_id,milliseconds,keep_repeats,journald,nojournald",
"options"
);
QCommandLineOption getPluginsOption(
"getPlugins",
"Print out a list of plugins in JSON"
);
QCommandLineOption abortAfterStartupOption(
"abortAfterStartup",
"Debug option. Aborts right after startup."
);
QCommandLineOption abortAfterInitOption(
"abortAfterInit",
"Debug option. Aborts after initialization, right before the program starts running the event loop."
);
// "--qmljsdebugger", which appears in output from "--help-all".
// Those below don't seem to be optional.
// --ignore-gpu-blacklist
@ -288,6 +318,10 @@ int main(int argc, const char* argv[]) {
parser.addOption(quitWhenFinishedOption);
parser.addOption(fastHeartbeatOption);
parser.addOption(logOption);
parser.addOption(abortAfterStartupOption);
parser.addOption(abortAfterInitOption);
parser.addOption(getPluginsOption);
QString applicationPath;
// A temporary application instance is needed to get the location of the running executable
@ -310,6 +344,16 @@ int main(int argc, const char* argv[]) {
#endif
}
// TODO: We need settings for Application, but Settings needs an Application
// to handle events. Needs splitting into two parts: enough initialization
// for Application to work, and then thread start afterwards.
Setting::init();
Application app(argcExtended, const_cast<char**>(argvExtended.data()), parser, startupTime);
if (parser.isSet("abortAfterStartup")) {
return 99;
}
// We want to configure the logging system as early as possible
auto& logHandler = LogHandler::getInstance();
@ -321,6 +365,75 @@ int main(int argc, const char* argv[]) {
}
}
app.initializePluginManager(parser);
if (parser.isSet(getPluginsOption)) {
auto pluginManager = PluginManager::getInstance();
QJsonObject pluginsJson;
for (const auto &plugin : pluginManager->getPluginInfo()) {
QJsonObject data;
data["data"] = plugin.metaData;
data["loaded"] = plugin.loaded;
data["disabled"] = plugin.disabled;
data["filteredOut"] = plugin.filteredOut;
data["wrongVersion"] = plugin.wrongVersion;
pluginsJson[plugin.name] = data;
}
QJsonObject inputJson;
for (const auto &plugin : pluginManager->getInputPlugins()) {
QJsonObject data;
data["subdeviceNames"] = QJsonArray::fromStringList(plugin->getSubdeviceNames());
data["deviceName"] = plugin->getDeviceName();
data["configurable"] = plugin->configurable();
data["isHandController"] = plugin->isHandController();
data["isHeadController"] = plugin->isHeadController();
data["isActive"] = plugin->isActive();
data["isSupported"] = plugin->isSupported();
inputJson[plugin->getName()] = data;
}
QJsonObject displayJson;
for (const auto &plugin : pluginManager->getDisplayPlugins()) {
QJsonObject data;
data["isHmd"] = plugin->isHmd();
data["isStereo"] = plugin->isStereo();
data["targetFramerate"] = plugin->getTargetFrameRate();
data["hasAsyncReprojection"] = plugin->hasAsyncReprojection();
data["isActive"] = plugin->isActive();
data["isSupported"] = plugin->isSupported();
displayJson[plugin->getName()] = data;
}
QJsonObject codecsJson;
for (const auto &plugin : pluginManager->getCodecPlugins()) {
QJsonObject data;
data["isActive"] = plugin->isActive();
data["isSupported"] = plugin->isSupported();
codecsJson[plugin->getName()] = data;
}
QJsonObject platformsJson;
platformsJson["steamAvailable"] = (pluginManager->getSteamClientPlugin() != nullptr);
platformsJson["oculusAvailable"] = (pluginManager->getOculusPlatformPlugin() != nullptr);
QJsonObject root;
root["plugins"] = pluginsJson;
root["inputs"] = inputJson;
root["displays"] = displayJson;
root["codecs"] = codecsJson;
root["platforms"] = platformsJson;
std::cout << QJsonDocument(root).toJson().toStdString() << "\n";
return 0;
}
// Act on arguments for early termination.
if (parser.isSet(versionOption)) {
parser.showVersion();
@ -407,10 +520,9 @@ int main(int argc, const char* argv[]) {
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
QElapsedTimer startupTime;
startupTime.start();
Setting::init();
// Instance UserActivityLogger now that the settings are loaded
auto& ual = UserActivityLogger::getInstance();
@ -549,7 +661,7 @@ int main(int argc, const char* argv[]) {
// Oculus initialization MUST PRECEDE OpenGL context creation.
// The nature of the Application constructor means this has to be either here,
// or in the main window ctor, before GL startup.
Application::initPlugins(parser);
//app.configurePlugins(parser);
#ifdef Q_OS_WIN
// If we're running in steam mode, we need to do an explicit check to ensure we're up to the required min spec
@ -587,17 +699,10 @@ int main(int argc, const char* argv[]) {
SandboxUtils::runLocalSandbox(serverContentPath, true, noUpdater);
}
// Extend argv to enable WebGL rendering
std::vector<const char*> argvExtended(&argv[0], &argv[argc]);
argvExtended.push_back("--ignore-gpu-blocklist");
#ifdef Q_OS_ANDROID
argvExtended.push_back("--suppress-settings-reset");
#endif
int argcExtended = (int)argvExtended.size();
PROFILE_SYNC_END(startup, "main startup", "");
PROFILE_SYNC_BEGIN(startup, "app full ctor", "");
Application app(argcExtended, const_cast<char**>(argvExtended.data()), parser, startupTime, runningMarkerExisted);
app.setPreviousSessionCrashed(runningMarkerExisted);
app.initialize(parser);
PROFILE_SYNC_END(startup, "app full ctor", "");
#if defined(Q_OS_LINUX)
@ -665,6 +770,9 @@ int main(int argc, const char* argv[]) {
translator.load("i18n/interface_en");
app.installTranslator(&translator);
qCDebug(interfaceapp, "Created QT Application.");
if (parser.isSet("abortAfterInit")) {
return 99;
}
exitCode = app.exec();
server.close();

View file

@ -72,12 +72,13 @@ int getPluginInterfaceVersionFromMetaData(const QJsonObject& object) {
QStringList preferredDisplayPlugins;
QStringList disabledDisplays;
QStringList disabledInputs;
std::vector<PluginManager::PluginInfo> pluginInfo;
bool isDisabled(QJsonObject metaData) {
auto name = getPluginNameFromMetaData(metaData);
auto iid = getPluginIIDFromMetaData(metaData);
if (iid == DisplayProvider_iid) {
if (iid == DisplayProvider_iid || iid == SteamClientProvider_iid || iid == OculusPlatformProvider_iid) {
return disabledDisplays.contains(name);
} else if (iid == InputProvider_iid) {
return disabledInputs.contains(name);
@ -126,18 +127,28 @@ int PluginManager::instantiate() {
qCDebug(plugins) << "Attempting plugin" << qPrintable(plugin);
auto loader = QSharedPointer<QPluginLoader>::create(pluginPath + plugin);
const QJsonObject pluginMetaData = loader->metaData();
PluginInfo info;
info.name = plugin;
info.metaData = pluginMetaData;
#if defined(HIFI_PLUGINMANAGER_DEBUG)
QJsonDocument metaDataDoc(pluginMetaData);
qCInfo(plugins) << "Metadata for " << qPrintable(plugin) << ": " << QString(metaDataDoc.toJson());
#endif
if (isDisabled(pluginMetaData)) {
qCWarning(plugins) << "Plugin" << qPrintable(plugin) << "is disabled";
info.disabled = true;
pluginInfo.push_back(info);
// Skip this one, it's disabled
continue;
}
if (!_pluginFilter(pluginMetaData)) {
qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "doesn't pass provided filter";
info.filteredOut = true;
pluginInfo.push_back(info);
continue;
}
@ -145,16 +156,22 @@ int PluginManager::instantiate() {
qCWarning(plugins) << "Plugin" << qPrintable(plugin) << "interface version doesn't match, not loading:"
<< getPluginInterfaceVersionFromMetaData(pluginMetaData)
<< "doesn't match" << HIFI_PLUGIN_INTERFACE_VERSION;
info.wrongVersion = true;
pluginInfo.push_back(info);
continue;
}
if (loader->load()) {
qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "loaded successfully";
info.loaded = true;
loadedPlugins.push_back(loader);
} else {
qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "failed to load:";
qCDebug(plugins) << " " << qPrintable(loader->errorString());
}
pluginInfo.push_back(info);
}
} else {
qWarning() << "pluginPath does not exit..." << pluginDir;
@ -163,6 +180,11 @@ int PluginManager::instantiate() {
return loadedPlugins;
}
std::vector<PluginManager::PluginInfo> PluginManager::getPluginInfo() const {
getLoadedPlugins(); // This builds the pluginInfo list
return pluginInfo;
}
const CodecPluginList& PluginManager::getCodecPlugins() {
static CodecPluginList codecPlugins;
static std::once_flag once;
@ -272,14 +294,6 @@ DisplayPluginList PluginManager::getAllDisplayPlugins() {
return _displayPlugins;
}
void PluginManager::disableDisplayPlugin(const QString& name) {
auto it = std::remove_if(_displayPlugins.begin(), _displayPlugins.end(), [&](const DisplayPluginPointer& plugin){
return plugin->getName() == name;
});
_displayPlugins.erase(it, _displayPlugins.end());
}
const InputPluginList& PluginManager::getInputPlugins() {
static std::once_flag once;
static auto deviceAddedCallback = [&](QString deviceName) {

View file

@ -12,54 +12,268 @@
#include <DependencyManager.h>
#include <SettingHandle.h>
#include <QJsonDocument>
#include <QJsonObject>
#include "Forward.h"
class QPluginLoader;
using PluginManagerPointer = QSharedPointer<PluginManager>;
/**
* @brief Manages loadable plugins
*
* The current implementation does initialization only once, as soon as it's needed.
* Once things are initialized the configuration is made permanent.
*
* Both loadable and statically modules are supported. Static modules have to be provided
* with setDisplayPluginProvider, setInputPluginProvider and setCodecPluginProvider.
*
* @warning Users of the PluginManager must take care to do any configuration very early
* on, because changes become impossible once initialization is done. Plugins can't be
* added or removed once that happens.
*
* Initialization is performed in the getDisplayPlugins, getInputPlugins and getCodecPlugins
* functions.
*/
class PluginManager : public QObject, public Dependency {
SINGLETON_DEPENDENCY
Q_OBJECT
public:
/**
* @brief Information about known plugins
*
*/
struct PluginInfo {
/**
* @brief Plugin metadata
*/
QJsonObject metaData;
/**
* @brief Filename
*
*/
QString name;
/**
* @brief Whether the plugin has been disabled
*
*/
bool disabled = false;
/**
* @brief Whether the plugin has been filtered out by a filter
*
*/
bool filteredOut = false;
/**
* @brief Whether the plugin has been not loaded because it's the wrong version
*
*/
bool wrongVersion = false;
/**
* @brief Whether the plugin has been loaded successfully
*
*/
bool loaded = false;
};
static PluginManagerPointer getInstance();
/**
* @brief Get the list of display plugins
*
* @note Calling this function will perform initialization and
* connects events to all the known the plugins on the first call.
*
* @return const DisplayPluginList&
*/
const DisplayPluginList& getDisplayPlugins();
/**
* @brief Get the list of input plugins
*
* @note Calling this function will perform initialization and
* connects events to all the known the plugins on the first call.
*
* @return const InputPluginList&
*/
const InputPluginList& getInputPlugins();
/**
* @brief Get the list of audio codec plugins
*
* @note Calling this function will perform initialization and
* connects events to all the known the plugins on the first call.
*
* @return const CodecPluginList&
*/
const CodecPluginList& getCodecPlugins();
/**
* @brief Get the pointer to the Steam client plugin
*
* This may return a null pointer if Steam support isn't built in.
*
* @return const SteamClientPluginPointer
*/
const SteamClientPluginPointer getSteamClientPlugin();
/**
* @brief Get the pointer to the Oculus Platform Plugin
*
* This may return a null pointer if Oculus support isn't built in.
*
* @return const OculusPlatformPluginPointer
*/
const OculusPlatformPluginPointer getOculusPlatformPlugin();
/**
* @brief Returns the list of preferred display plugins
*
* The preferred display plugins are set by setPreferredDisplayPlugins.
*
* @return DisplayPluginList
*/
DisplayPluginList getPreferredDisplayPlugins();
/**
* @brief Sets the list of preferred display plugins
*
* @note This must be called early, before any call to getPreferredDisplayPlugins.
*
* @param displays
*/
void setPreferredDisplayPlugins(const QStringList& displays);
void disableDisplayPlugin(const QString& name);
/**
* @brief Disable a list of displays
*
* This adds the display to a list of displays not to be used.
*
* @param displays
*/
void disableDisplays(const QStringList& displays);
/**
* @brief Disable a list of inputs
*
* This adds the input to a list of inputs not to be used.
* @param inputs
*/
void disableInputs(const QStringList& inputs);
/**
* @brief Save the settings
*
*/
void saveSettings();
/**
* @brief Set the container for plugins
*
* This will be passed to all active plugins on initialization.
*
* @param container
*/
void setContainer(PluginContainer* container) { _container = container; }
int instantiate();
void shutdown();
// Application that have statically linked plugins can expose them to the plugin manager with these function
/**
* @brief Provide a list of statically linked plugins.
*
* This is used to provide a list of statically linked plugins to the plugin manager.
*
* @note This must be called very early on, and only works once. Once the plugin manager
* builds its internal list of plugins, the final list becomes set in stone.
*
* @param provider A std::function that returns a list of display plugins
*/
void setDisplayPluginProvider(const DisplayPluginProvider& provider);
/**
* @brief Provide a list of statically linked plugins.
*
* This is used to provide a list of statically linked plugins to the plugin manager.
*
* @note This must be called very early on, and only works once. Once the plugin manager
* builds its internal list of plugins, the final list becomes set in stone.
*
* @param provider A std::function that returns a list of input plugins
*/
void setInputPluginProvider(const InputPluginProvider& provider);
/**
* @brief Provide a list of statically linked plugins.
*
* This is used to provide a list of statically linked plugins to the plugin manager.
*
* @note This must be called very early on, and only works once. Once the plugin manager
* builds its internal list of plugins, the final list becomes set in stone.
*
* @param provider A std::function that returns a list of codec plugins
*/
void setCodecPluginProvider(const CodecPluginProvider& provider);
/**
* @brief Set the input plugin persister
*
* @param persister A std::function that saves input plugin settings
*/
void setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister);
/**
* @brief Get the list of running input devices
*
* @return QStringList List of input devices in running state
*/
QStringList getRunningInputDeviceNames() const;
using PluginFilter = std::function<bool(const QJsonObject&)>;
/**
* @brief Set the plugin filter that determines whether a plugin will be used or not
*
* @note This must be called very early on. Once the plugin manager
* builds its internal list of plugins, the final list becomes set in stone.
*
* As of writing, this is used in the audio mixer.
*
* @param pluginFilter
*/
void setPluginFilter(PluginFilter pluginFilter) { _pluginFilter = pluginFilter; }
/**
* @brief Get a list of all the display plugins
*
* @return DisplayPluginList List of display plugins
*/
Q_INVOKABLE DisplayPluginList getAllDisplayPlugins();
bool getEnableOculusPluginSetting() { return _enableOculusPluginSetting.get(); }
void setEnableOculusPluginSetting(bool value);
/**
* @brief Returns information about known plugins
*
* This is a function for informative/debugging purposes.
*
* @return std::vector<PluginInfo>
*/
std::vector<PluginInfo> getPluginInfo() const;
signals:
void inputDeviceRunningChanged(const QString& pluginName, bool isRunning, const QStringList& runningDevices);
private:
PluginManager() = default;