diff --git a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml index 338a76989f..8053673e9c 100644 --- a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml +++ b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml @@ -25,11 +25,12 @@ Rectangle { signal canceled() signal restart() - property int count: 3 + property int count: 5 property string calibratingText: "CALIBRATING..." property string calibratingCountText: "CALIBRATION STARTING IN" property string calibrationSuccess: "CALIBRATION COMPLETED" property string calibrationFailed: "CALIBRATION FAILED" + property string instructionText: "Please stand in a T-Pose during calibration" HifiConstants { id: hifi } visible: true @@ -158,6 +159,15 @@ Rectangle { onClicked: { restart(); + statusText.color = hifi.colors.blueHighlight; + statusText.text = info.calibratingCountText; + directions.text = instructionText; + countDown.visible = true; + busyIndicator.running = true; + busyRotation.from = 0 + busyRotation.to = 360 + busyIndicator.source = blueIndicator; + closeWindow.stop(); numberAnimation.stop(); info.count = (timer.interval / 1000); numberAnimation.start(); @@ -178,6 +188,7 @@ Rectangle { } } + function start(interval, countNumber) { countDown.visible = true; statusText.color = hifi.colors.blueHighlight; @@ -201,6 +212,7 @@ Rectangle { busyIndicator.running = false; statusText.text = info.calibrationSuccess statusText.color = hifi.colors.greenHighlight + directions.text = "SUCCESS" closeWindow.start(); } diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 52a935ab19..96413534c3 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -458,7 +458,7 @@ Rectangle { width: glyphButton.width + calibrationText.width + padding height: hifi.dimensions.controlLineHeight anchors.top: bottomSeperator.bottom - anchors.topMargin: 10 + anchors.topMargin: 15 anchors.left: parent.left anchors.leftMargin: leftMargin @@ -590,16 +590,24 @@ Rectangle { lastConfiguration = composeConfigurationSettings(); } + Component.onDestruction: { + var settings = InputConfiguration.configurationSettings(pluginName); + var data = { + "num_pucks": settings["puckCount"] + } + UserActivityLogger.logAction("mocap_ui_close_dialog", data); + } + HifiControls.SpinBox { id: timeToCalibrate width: 70 anchors.top: calibrationButton.bottom - anchors.topMargin: 40 + anchors.topMargin: 20 anchors.left: parent.left anchors.leftMargin: leftMargin - minimumValue: 3 - value: 3 + minimumValue: 5 + value: 5 colorScheme: hifi.colorSchemes.dark onEditingFinished: { @@ -641,17 +649,39 @@ Rectangle { to: 0 } + function logAction(action, status) { + console.log("calibrated from ui"); + var data = { + "num_pucks": status["puckCount"], + "puck_configuration": status["configuration"], + "head_puck": status["head_puck"], + "hand_puck": status["hand_pucks"] + } + UserActivityLogger.logAction(action, data); + } + function calibrationStatusInfo(status) { var calibrationScreen = stack.currentItem; + + if (!status["UI"]) { + calibratingScreen = screen.createObject(); + stack.push(calibratingScreen); + } + if (status["calibrated"]) { calibrationScreen.success(); + + if (status["UI"]) { + logAction("mocap_ui_success", status); + } + } else if (!status["calibrated"]) { - var uncalibrated = status["success"]; - if (!uncalibrated) { - calibrationScreen.failure(); + calibrationScreen.failure(); + + if (status["UI"]) { + logAction("mocap_ui_failed", status); } } - updateCalibrationButton(); } @@ -717,6 +747,12 @@ Rectangle { initializeButtonState(); updateCalibrationText(); + + var data = { + "num_pucks": settings["puckCount"] + }; + + UserActivityLogger.logAction("mocap_ui_open_dialog", data); } function displayTrackerConfiguration(type) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 377819c0a0..f2bf0aea0a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -2164,48 +2165,74 @@ void Application::paintGL() { return; } - auto displayPlugin = getActiveDisplayPlugin(); - // FIXME not needed anymore? - _offscreenContext->makeCurrent(); + DisplayPluginPointer displayPlugin; + { + PROFILE_RANGE(render, "/getActiveDisplayPlugin"); + displayPlugin = getActiveDisplayPlugin(); + } - // If a display plugin loses it's underlying support, it - // needs to be able to signal us to not use it - if (!displayPlugin->beginFrameRender(_frameCount)) { - _inPaint = false; - updateDisplayMode(); - return; + { + PROFILE_RANGE(render, "/offscreenMakeCurrent"); + // FIXME not needed anymore? + _offscreenContext->makeCurrent(); + } + + { + PROFILE_RANGE(render, "/pluginBeginFrameRender"); + // If a display plugin loses it's underlying support, it + // needs to be able to signal us to not use it + if (!displayPlugin->beginFrameRender(_frameCount)) { + _inPaint = false; + updateDisplayMode(); + return; + } } // update the avatar with a fresh HMD pose - getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose()); + { + PROFILE_RANGE(render, "/updateAvatar"); + getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose()); + } auto lodManager = DependencyManager::get(); + RenderArgs renderArgs; { - QMutexLocker viewLocker(&_viewMutex); - _viewFrustum.calculate(); - } - RenderArgs renderArgs(_gpuContext, getEntities(), lodManager->getOctreeSizeScale(), - lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, - RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); - { - QMutexLocker viewLocker(&_viewMutex); - renderArgs.setViewFrustum(_viewFrustum); + PROFILE_RANGE(render, "/buildFrustrumAndArgs"); + { + QMutexLocker viewLocker(&_viewMutex); + _viewFrustum.calculate(); + } + renderArgs = RenderArgs(_gpuContext, getEntities(), lodManager->getOctreeSizeScale(), + lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, + RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); + { + QMutexLocker viewLocker(&_viewMutex); + renderArgs.setViewFrustum(_viewFrustum); + } } - PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::paintGL()"); - resizeGL(); - - _gpuContext->beginFrame(getHMDSensorPose()); - // Reset the gpu::Context Stages - // Back to the default framebuffer; - gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) { - batch.resetStages(); - }); + { + PROFILE_RANGE(render, "/resizeGL"); + PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::paintGL()"); + resizeGL(); + } { + PROFILE_RANGE(render, "/gpuContextReset"); + _gpuContext->beginFrame(getHMDSensorPose()); + // Reset the gpu::Context Stages + // Back to the default framebuffer; + gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) { + batch.resetStages(); + }); + } + + + { + PROFILE_RANGE(render, "/renderOverlay"); PerformanceTimer perfTimer("renderOverlay"); // NOTE: There is no batch associated with this renderArgs // the ApplicationOverlay class assumes it's viewport is setup to be the device size @@ -2216,114 +2243,127 @@ void Application::paintGL() { glm::vec3 boomOffset; { - PerformanceTimer perfTimer("CameraUpdates"); + PROFILE_RANGE(render, "/updateCamera"); + { + PerformanceTimer perfTimer("CameraUpdates"); - auto myAvatar = getMyAvatar(); - boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; + auto myAvatar = getMyAvatar(); + boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; - if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); - Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); - cameraMenuChanged(); - } - - // The render mode is default or mirror if the camera is in mirror mode, assigned further below - renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - - // Always use the default eye position, not the actual head eye position. - // Using the latter will cause the camera to wobble with idle animations, - // or with changes from the face tracker - if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { - if (isHMDMode()) { - mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - _myCamera.setPosition(extractTranslation(camMat)); - _myCamera.setOrientation(glm::quat_cast(camMat)); - } else { - _myCamera.setPosition(myAvatar->getDefaultEyePosition()); - _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); + cameraMenuChanged(); } - } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - if (isHMDMode()) { - auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - _myCamera.setOrientation(glm::normalize(glm::quat_cast(hmdWorldMat))); - _myCamera.setPosition(extractTranslation(hmdWorldMat) + - myAvatar->getOrientation() * boomOffset); - } else { - _myCamera.setOrientation(myAvatar->getHead()->getOrientation()); - if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + _myCamera.getOrientation() * boomOffset); - } else { - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + myAvatar->getOrientation() * boomOffset); - } - } - } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - if (isHMDMode()) { - auto mirrorBodyOrientation = myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); - glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); - // Mirror HMD yaw and roll - glm::vec3 mirrorHmdEulers = glm::eulerAngles(hmdRotation); - mirrorHmdEulers.y = -mirrorHmdEulers.y; - mirrorHmdEulers.z = -mirrorHmdEulers.z; - glm::quat mirrorHmdRotation = glm::quat(mirrorHmdEulers); + // The render mode is default or mirror if the camera is in mirror mode, assigned further below + renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - glm::quat worldMirrorRotation = mirrorBodyOrientation * mirrorHmdRotation; - - _myCamera.setOrientation(worldMirrorRotation); - - glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); - // Mirror HMD lateral offsets - hmdOffset.x = -hmdOffset.x; - - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) - + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror - + mirrorBodyOrientation * hmdOffset); - } else { - _myCamera.setOrientation(myAvatar->getWorldAlignedOrientation() - * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) - + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * - glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); - } - renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - } else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) { - EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); - if (cameraEntity != nullptr) { + // Always use the default eye position, not the actual head eye position. + // Using the latter will cause the camera to wobble with idle animations, + // or with changes from the face tracker + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { if (isHMDMode()) { - glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); - _myCamera.setOrientation(cameraEntity->getRotation() * hmdRotation); - glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(cameraEntity->getPosition() + (hmdRotation * hmdOffset)); + mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + _myCamera.setPosition(extractTranslation(camMat)); + _myCamera.setOrientation(glm::quat_cast(camMat)); } else { - _myCamera.setOrientation(cameraEntity->getRotation()); - _myCamera.setPosition(cameraEntity->getPosition()); + _myCamera.setPosition(myAvatar->getDefaultEyePosition()); + _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); + } + } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + if (isHMDMode()) { + auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + _myCamera.setOrientation(glm::normalize(glm::quat_cast(hmdWorldMat))); + _myCamera.setPosition(extractTranslation(hmdWorldMat) + + myAvatar->getOrientation() * boomOffset); + } else { + _myCamera.setOrientation(myAvatar->getHead()->getOrientation()); + if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + _myCamera.getOrientation() * boomOffset); + } else { + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + myAvatar->getOrientation() * boomOffset); + } + } + } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + if (isHMDMode()) { + auto mirrorBodyOrientation = myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); + + glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); + // Mirror HMD yaw and roll + glm::vec3 mirrorHmdEulers = glm::eulerAngles(hmdRotation); + mirrorHmdEulers.y = -mirrorHmdEulers.y; + mirrorHmdEulers.z = -mirrorHmdEulers.z; + glm::quat mirrorHmdRotation = glm::quat(mirrorHmdEulers); + + glm::quat worldMirrorRotation = mirrorBodyOrientation * mirrorHmdRotation; + + _myCamera.setOrientation(worldMirrorRotation); + + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + // Mirror HMD lateral offsets + hmdOffset.x = -hmdOffset.x; + + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) + + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + + mirrorBodyOrientation * hmdOffset); + } else { + _myCamera.setOrientation(myAvatar->getWorldAlignedOrientation() + * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) + + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * + glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + } + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + } else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) { + EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); + if (cameraEntity != nullptr) { + if (isHMDMode()) { + glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); + _myCamera.setOrientation(cameraEntity->getRotation() * hmdRotation); + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + _myCamera.setPosition(cameraEntity->getPosition() + (hmdRotation * hmdOffset)); + } else { + _myCamera.setOrientation(cameraEntity->getRotation()); + _myCamera.setPosition(cameraEntity->getPosition()); + } } } - } - // Update camera position - if (!isHMDMode()) { - _myCamera.update(1.0f / _frameCounter.rate()); + // Update camera position + if (!isHMDMode()) { + _myCamera.update(1.0f / _frameCounter.rate()); + } } } - getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform()); + { + PROFILE_RANGE(render, "/updateCompositor"); + getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform()); + } - // Primary rendering pass - auto framebufferCache = DependencyManager::get(); - const QSize size = framebufferCache->getFrameBufferSize(); - // Final framebuffer that will be handled to the display-plugin - auto finalFramebuffer = framebufferCache->getFramebuffer(); + gpu::FramebufferPointer finalFramebuffer; + QSize finalFramebufferSize; + { + PROFILE_RANGE(render, "/getOutputFramebuffer"); + // Primary rendering pass + auto framebufferCache = DependencyManager::get(); + finalFramebufferSize = framebufferCache->getFrameBufferSize(); + // Final framebuffer that will be handled to the display-plugin + finalFramebuffer = framebufferCache->getFramebuffer(); + } { PROFILE_RANGE(render, "/mainRender"); PerformanceTimer perfTimer("mainRender"); renderArgs._boomOffset = boomOffset; + // FIXME is this ever going to be different from the size previously set in the render args + // in the overlay render? // Viewport is assigned to the size of the framebuffer - renderArgs._viewport = ivec4(0, 0, size.width(), size.height()); + renderArgs._viewport = ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height()); if (displayPlugin->isStereo()) { // Stereo modes will typically have a larger projection matrix overall, // so we ask for the 'mono' projection matrix, which for stereo and HMD @@ -3623,6 +3663,133 @@ bool Application::shouldPaint(float nsecsElapsed) { #include #include #pragma comment(lib, "pdh.lib") +#pragma comment(lib, "ntdll.lib") + +extern "C" { + enum SYSTEM_INFORMATION_CLASS { + SystemBasicInformation = 0, + SystemProcessorPerformanceInformation = 8, + }; + + struct SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { + LARGE_INTEGER IdleTime; + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER DpcTime; + LARGE_INTEGER InterruptTime; + ULONG InterruptCount; + }; + + struct SYSTEM_BASIC_INFORMATION { + ULONG Reserved; + ULONG TimerResolution; + ULONG PageSize; + ULONG NumberOfPhysicalPages; + ULONG LowestPhysicalPageNumber; + ULONG HighestPhysicalPageNumber; + ULONG AllocationGranularity; + ULONG_PTR MinimumUserModeAddress; + ULONG_PTR MaximumUserModeAddress; + ULONG_PTR ActiveProcessorsAffinityMask; + CCHAR NumberOfProcessors; + }; + + NTSYSCALLAPI NTSTATUS NTAPI NtQuerySystemInformation( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +} +template +NTSTATUS NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, T& t) { + return NtQuerySystemInformation(SystemInformationClass, &t, (ULONG)sizeof(T), nullptr); +} + +template +NTSTATUS NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, std::vector& t) { + return NtQuerySystemInformation(SystemInformationClass, t.data(), (ULONG)(sizeof(T) * t.size()), nullptr); +} + + +template +void updateValueAndDelta(std::pair& pair, T newValue) { + auto& value = pair.first; + auto& delta = pair.second; + delta = (value != 0) ? newValue - value : 0; + value = newValue; +} + +struct MyCpuInfo { + using ValueAndDelta = std::pair; + std::string name; + ValueAndDelta kernel { 0, 0 }; + ValueAndDelta user { 0, 0 }; + ValueAndDelta idle { 0, 0 }; + float kernelUsage { 0.0f }; + float userUsage { 0.0f }; + + void update(const SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION& cpuInfo) { + updateValueAndDelta(kernel, cpuInfo.KernelTime.QuadPart); + updateValueAndDelta(user, cpuInfo.UserTime.QuadPart); + updateValueAndDelta(idle, cpuInfo.IdleTime.QuadPart); + auto totalTime = kernel.second + user.second + idle.second; + if (totalTime != 0) { + kernelUsage = (FLOAT)kernel.second / totalTime; + userUsage = (FLOAT)user.second / totalTime; + } else { + kernelUsage = userUsage = 0.0f; + } + } +}; + +void updateCpuInformation() { + static std::once_flag once; + static SYSTEM_BASIC_INFORMATION systemInfo {}; + static SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION cpuTotals; + static std::vector cpuInfos; + static std::vector myCpuInfos; + static MyCpuInfo myCpuTotals; + std::call_once(once, [&] { + NtQuerySystemInformation( SystemBasicInformation, systemInfo); + cpuInfos.resize(systemInfo.NumberOfProcessors); + myCpuInfos.resize(systemInfo.NumberOfProcessors); + for (size_t i = 0; i < systemInfo.NumberOfProcessors; ++i) { + myCpuInfos[i].name = "cpu." + std::to_string(i); + } + myCpuTotals.name = "cpu.total"; + }); + NtQuerySystemInformation(SystemProcessorPerformanceInformation, cpuInfos); + + // Zero the CPU totals. + memset(&cpuTotals, 0, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); + for (size_t i = 0; i < systemInfo.NumberOfProcessors; ++i) { + auto& cpuInfo = cpuInfos[i]; + // KernelTime includes IdleTime. + cpuInfo.KernelTime.QuadPart -= cpuInfo.IdleTime.QuadPart; + + // Update totals + cpuTotals.IdleTime.QuadPart += cpuInfo.IdleTime.QuadPart; + cpuTotals.KernelTime.QuadPart += cpuInfo.KernelTime.QuadPart; + cpuTotals.UserTime.QuadPart += cpuInfo.UserTime.QuadPart; + + // Update friendly structure + auto& myCpuInfo = myCpuInfos[i]; + myCpuInfo.update(cpuInfo); + PROFILE_COUNTER(app, myCpuInfo.name.c_str(), { + { "kernel", myCpuInfo.kernelUsage }, + { "user", myCpuInfo.userUsage } + }); + } + + myCpuTotals.update(cpuTotals); + PROFILE_COUNTER(app, myCpuTotals.name.c_str(), { + { "kernel", myCpuTotals.kernelUsage }, + { "user", myCpuTotals.userUsage } + }); +} + static ULARGE_INTEGER lastCPU, lastSysCPU, lastUserCPU; static int numProcessors; @@ -3675,6 +3842,26 @@ void getCpuUsage(vec3& systemAndUser) { systemAndUser.z = (float)counterVal.doubleValue; } +void setupCpuMonitorThread() { + initCpuUsage(); + auto cpuMonitorThread = QThread::currentThread(); + + QTimer* timer = new QTimer(); + timer->setInterval(50); + QObject::connect(timer, &QTimer::timeout, [] { + updateCpuInformation(); + vec3 kernelUserAndSystem; + getCpuUsage(kernelUserAndSystem); + PROFILE_COUNTER(app, "cpuProcess", { { "system", kernelUserAndSystem.x }, { "user", kernelUserAndSystem.y } }); + PROFILE_COUNTER(app, "cpuSystem", { { "system", kernelUserAndSystem.z } }); + }); + QObject::connect(cpuMonitorThread, &QThread::finished, [=] { + timer->deleteLater(); + cpuMonitorThread->deleteLater(); + }); + timer->start(); +} + #endif @@ -3695,15 +3882,17 @@ void Application::idle(float nsecsElapsed) { } #ifdef Q_OS_WIN + // If tracing is enabled then monitor the CPU in a separate thread static std::once_flag once; - std::call_once(once, [] { - initCpuUsage(); + std::call_once(once, [&] { + if (trace_app().isDebugEnabled()) { + QThread* cpuMonitorThread = new QThread(qApp); + cpuMonitorThread->setObjectName("cpuMonitorThread"); + QObject::connect(cpuMonitorThread, &QThread::started, [this] { setupCpuMonitorThread(); }); + QObject::connect(qApp, &QCoreApplication::aboutToQuit, cpuMonitorThread, &QThread::quit); + cpuMonitorThread->start(); + } }); - - vec3 kernelUserAndSystem; - getCpuUsage(kernelUserAndSystem); - PROFILE_COUNTER(app, "cpuProcess", { { "system", kernelUserAndSystem.x }, { "user", kernelUserAndSystem.y } }); - PROFILE_COUNTER(app, "cpuSystem", { { "system", kernelUserAndSystem.z } }); #endif diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 0c73f1fa70..399c730dbd 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -40,19 +40,23 @@ Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays") void Overlays::cleanupAllOverlays() { + QMap overlaysHUD; + QMap overlaysWorld; { - foreach(Overlay::Pointer overlay, _overlaysHUD) { - _overlaysToDelete.push_back(overlay); - } - foreach(Overlay::Pointer overlay, _overlaysWorld) { - _overlaysToDelete.push_back(overlay); - } - _overlaysHUD.clear(); - _overlaysWorld.clear(); -#if OVERLAY_PANELS - _panels.clear(); -#endif + QMutexLocker locker(&_mutex); + overlaysHUD.swap(_overlaysHUD); + overlaysWorld.swap(_overlaysWorld); } + + foreach(Overlay::Pointer overlay, overlaysHUD) { + _overlaysToDelete.push_back(overlay); + } + foreach(Overlay::Pointer overlay, overlaysWorld) { + _overlaysToDelete.push_back(overlay); + } +#if OVERLAY_PANELS + _panels.clear(); +#endif cleanupOverlaysToDelete(); } @@ -63,12 +67,12 @@ void Overlays::init() { } void Overlays::update(float deltatime) { - { - foreach(Overlay::Pointer thisOverlay, _overlaysHUD) { + QMutexLocker locker(&_mutex); + foreach(const auto& thisOverlay, _overlaysHUD) { thisOverlay->update(deltatime); } - foreach(Overlay::Pointer thisOverlay, _overlaysWorld) { + foreach(const auto& thisOverlay, _overlaysWorld) { thisOverlay->update(deltatime); } } @@ -110,7 +114,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) { int height = size.y; mat4 legacyProjection = glm::ortho(0, width, height, 0, -1000, 1000); - + QMutexLocker locker(&_mutex); foreach(Overlay::Pointer thisOverlay, _overlaysHUD) { // Reset all batch pipeline settings between overlay @@ -132,7 +136,10 @@ void Overlays::enable() { _enabled = true; } +// Note, can't be invoked by scripts, but can be called by the InterfaceParentFinder +// class on packet processing threads Overlay::Pointer Overlays::getOverlay(OverlayID id) const { + QMutexLocker locker(&_mutex); if (_overlaysHUD.contains(id)) { return _overlaysHUD[id]; } @@ -193,13 +200,17 @@ OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) { overlay->setOverlayID(thisID); overlay->setStackOrder(_stackOrder++); if (overlay->is3D()) { - _overlaysWorld[thisID] = overlay; + { + QMutexLocker locker(&_mutex); + _overlaysWorld[thisID] = overlay; + } render::ScenePointer scene = qApp->getMain3DScene(); render::Transaction transaction; overlay->addToScene(overlay, scene, transaction); scene->enqueueTransaction(transaction); } else { + QMutexLocker locker(&_mutex); _overlaysHUD[thisID] = overlay; } @@ -231,9 +242,12 @@ OverlayID Overlays::cloneOverlay(OverlayID id) { bool Overlays::editOverlay(OverlayID id, const QVariant& properties) { if (QThread::currentThread() != thread()) { - bool result; - BLOCKING_INVOKE_METHOD(this, "editOverlay", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id), Q_ARG(QVariant, properties)); - return result; + // NOTE editOverlay can be called very frequently in scripts and can't afford to + // block waiting on the main thread. Additionally, no script actually + // examines the return value and does something useful with it, so use a non-blocking + // invoke and just always return true + QMetaObject::invokeMethod(this, "editOverlay", Q_ARG(OverlayID, id), Q_ARG(QVariant, properties)); + return true; } Overlay::Pointer thisOverlay = getOverlay(id); @@ -246,9 +260,9 @@ bool Overlays::editOverlay(OverlayID id, const QVariant& properties) { bool Overlays::editOverlays(const QVariant& propertiesById) { if (QThread::currentThread() != thread()) { - bool result; - BLOCKING_INVOKE_METHOD(this, "editOverlays", Q_RETURN_ARG(bool, result), Q_ARG(QVariant, propertiesById)); - return result; + // NOTE see comment on editOverlay for why this is not a blocking call + QMetaObject::invokeMethod(this, "editOverlays", Q_ARG(QVariant, propertiesById)); + return true; } QVariantMap map = propertiesById.toMap(); @@ -275,6 +289,7 @@ void Overlays::deleteOverlay(OverlayID id) { Overlay::Pointer overlayToDelete; { + QMutexLocker locker(&_mutex); if (_overlaysHUD.contains(id)) { overlayToDelete = _overlaysHUD.take(id); } else if (_overlaysWorld.contains(id)) { @@ -374,30 +389,22 @@ OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) { return result; } - glm::vec2 pointCopy = point; if (!_enabled) { return UNKNOWN_OVERLAY_ID; } - QMapIterator i(_overlaysHUD); - const float LARGE_NEGATIVE_FLOAT = -9999999; - glm::vec3 origin(pointCopy.x, pointCopy.y, LARGE_NEGATIVE_FLOAT); - glm::vec3 direction(0, 0, 1); - glm::vec3 thisSurfaceNormal; + QMutexLocker locker(&_mutex); + QMapIterator i(_overlaysHUD); unsigned int bestStackOrder = 0; OverlayID bestOverlayID = UNKNOWN_OVERLAY_ID; - while (i.hasNext()) { i.next(); - OverlayID thisID = i.key(); - if (!i.value()->is3D()) { - auto thisOverlay = std::dynamic_pointer_cast(i.value()); - if (thisOverlay && thisOverlay->getVisible() && thisOverlay->isLoaded() && - thisOverlay->getBoundingRect().contains(pointCopy.x, pointCopy.y, false)) { - if (thisOverlay->getStackOrder() > bestStackOrder) { - bestOverlayID = thisID; - bestStackOrder = thisOverlay->getStackOrder(); - } + auto thisOverlay = std::dynamic_pointer_cast(i.value()); + if (thisOverlay && thisOverlay->getVisible() && thisOverlay->isLoaded() && + thisOverlay->getBoundingRect().contains(point.x, point.y, false)) { + if (thisOverlay->getStackOrder() > bestStackOrder) { + bestOverlayID = i.key(); + bestStackOrder = thisOverlay->getStackOrder(); } } } @@ -466,6 +473,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR float bestDistance = std::numeric_limits::max(); bool bestIsFront = false; + QMutexLocker locker(&_mutex); RayToOverlayIntersectionResult result; QMapIterator i(_overlaysWorld); while (i.hasNext()) { @@ -598,13 +606,20 @@ QSizeF Overlays::textSize(OverlayID id, const QString& text) { return result; } - Overlay::Pointer thisOverlay = _overlaysHUD[id]; + Overlay::Pointer thisOverlay; + { + QMutexLocker locker(&_mutex); + thisOverlay = _overlaysHUD[id]; + } if (thisOverlay) { if (auto textOverlay = std::dynamic_pointer_cast(thisOverlay)) { return textOverlay->textSize(text); } } else { - thisOverlay = _overlaysWorld[id]; + { + QMutexLocker locker(&_mutex); + thisOverlay = _overlaysWorld[id]; + } if (auto text3dOverlay = std::dynamic_pointer_cast(thisOverlay)) { return text3dOverlay->textSize(text); } @@ -675,6 +690,7 @@ bool Overlays::isAddedOverlay(OverlayID id) { return result; } + QMutexLocker locker(&_mutex); return _overlaysHUD.contains(id) || _overlaysWorld.contains(id); } @@ -949,7 +965,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { return result; } - + QMutexLocker locker(&_mutex); QMapIterator i(_overlaysWorld); int checked = 0; while (i.hasNext()) { diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index d3fa70225e..bfb775b041 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -319,6 +319,7 @@ signals: private: void cleanupOverlaysToDelete(); + mutable QMutex _mutex; QMap _overlaysHUD; QMap _overlaysWorld; #if OVERLAY_PANELS diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 5c8f59f20f..20749cd567 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -198,11 +198,7 @@ gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) { std::unique_lock lock(_texturesByHashesMutex); weakPointer = _texturesByHashes[hash]; } - auto result = weakPointer.lock(); - if (result) { - qCWarning(modelnetworking) << "QQQ Returning live texture for hash " << hash.c_str(); - } - return result; + return weakPointer.lock(); } gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture) { diff --git a/libraries/render-utils/src/local_lights_shading.slf b/libraries/render-utils/src/local_lights_shading.slf index a4e28a9757..c6310cb079 100644 --- a/libraries/render-utils/src/local_lights_shading.slf +++ b/libraries/render-utils/src/local_lights_shading.slf @@ -34,6 +34,8 @@ in vec2 _texCoord0; out vec4 _fragColor; void main(void) { + _fragColor = vec4(0.0); + // Grab the fragment data from the uv vec2 texCoord = _texCoord0.st; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index b51cb0ee3b..3a876a0e0a 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1821,7 +1821,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac clearExceptions(); } } else { - scriptWarningMessage("Script.include() skipping evaluation of previously included url:" + url.toString()); + scriptPrintedMessage("Script.include() skipping evaluation of previously included url:" + url.toString()); } } } diff --git a/libraries/shared/src/ThreadHelpers.cpp b/libraries/shared/src/ThreadHelpers.cpp index 14ae35762b..8f3d16a577 100644 --- a/libraries/shared/src/ThreadHelpers.cpp +++ b/libraries/shared/src/ThreadHelpers.cpp @@ -17,10 +17,6 @@ void moveToNewNamedThread(QObject* object, const QString& name, std::functionsetObjectName(name); - if (priority != QThread::InheritPriority) { - thread->setPriority(priority); - } - QString tempName = name; QObject::connect(thread, &QThread::started, [startCallback] { startCallback(); @@ -32,6 +28,9 @@ void moveToNewNamedThread(QObject* object, const QString& name, std::functionmoveToThread(thread); thread->start(); + if (priority != QThread::InheritPriority) { + thread->setPriority(priority); + } } void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority) { diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 648373ccc2..2f9aa4405a 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -249,6 +249,7 @@ ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : contro _configStringMap[Config::FeetAndHips] = QString("FeetAndHips"); _configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest"); _configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders"); + _configStringMap[Config::FeetHipsChestAndShoulders] = QString("FeetHipsChestAndShoulders"); } void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { @@ -325,6 +326,7 @@ void ViveControllerManager::InputDevice::calibrateFromUI(const controller::Input if (_calibrate) { uncalibrate(); calibrate(inputCalibrationData); + emitCalibrationStatus(); _calibrate = false; } } @@ -372,29 +374,23 @@ QJsonObject ViveControllerManager::InputDevice::configurationSettings() { Locker locker(_lock); QJsonObject configurationSettings; configurationSettings["trackerConfiguration"] = configToString(_preferedConfig); - configurationSettings["HMDHead"] = (_headConfig == HeadConfig::HMD) ? true : false; - configurationSettings["handController"] = (_handConfig == HandConfig::HandController) ? true : false; + configurationSettings["HMDHead"] = (_headConfig == HeadConfig::HMD); + configurationSettings["handController"] = (_handConfig == HandConfig::HandController); + configurationSettings["puckCount"] = (int)_validTrackedObjects.size(); return configurationSettings; } -void ViveControllerManager::InputDevice::emitCalibrationStatus(const bool success) { +void ViveControllerManager::InputDevice::emitCalibrationStatus() { auto inputConfiguration = DependencyManager::get(); QJsonObject status = QJsonObject(); - - if (_calibrated && success) { - status["calibrated"] = _calibrated; - status["configuration"] = configToString(_preferedConfig); - } else if (!_calibrated && !success) { - status["calibrated"] = _calibrated; - status["success"] = success; - } else if (!_calibrated && success) { - status["calibrated"] = _calibrated; - status["success"] = success; - status["configuration"] = configToString(_preferedConfig); - status["puckCount"] = (int)_validTrackedObjects.size(); - } + status["calibrated"] = _calibrated; + status["configuration"] = configToString(_preferedConfig); + status["head_puck"] = (_headConfig == HeadConfig::Puck); + status["hand_pucks"] = (_handConfig == HandConfig::Pucks); + status["puckCount"] = (int)_validTrackedObjects.size(); + status["UI"] = _calibrate; - emit inputConfiguration->calibrationStatus(status); //inputConfiguration->calibrated(status); + emit inputConfiguration->calibrationStatus(status); } void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData) { @@ -437,12 +433,29 @@ void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceInde } } +void ViveControllerManager::InputDevice::sendUserActivityData(QString activity) { + QJsonObject jsonData = { + {"num_pucks", (int)_validTrackedObjects.size()}, + {"configuration", configToString(_preferedConfig)}, + {"head_puck", (_headConfig == HeadConfig::Puck) ? true : false}, + {"hand_pucks", (_handConfig == HandConfig::Pucks) ? true : false} + }; + + UserActivityLogger::getInstance().logAction(activity, jsonData); +} + void ViveControllerManager::InputDevice::calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration) { if (!_calibrated) { calibrate(inputCalibration); + if (_calibrated) { + sendUserActivityData("mocap_button_success"); + } else { + sendUserActivityData("mocap_button_fail"); + } + emitCalibrationStatus(); } else { uncalibrate(); - emitCalibrationStatus(true); + sendUserActivityData("mocap_button_uncalibrate"); } } @@ -454,7 +467,6 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr if (puckCount == 0) { uncalibrate(); - emitCalibrationStatus(false); return; } @@ -473,10 +485,8 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr if (!headConfigured || !handsConfigured || !bodyConfigured) { uncalibrate(); - emitCalibrationStatus(false); } else { _calibrated = true; - emitCalibrationStatus(true); qDebug() << "PuckCalibration: " << configToString(_config) << " Configuration Successful"; } } @@ -566,8 +576,6 @@ bool ViveControllerManager::InputDevice::configureBody(glm::mat4& defaultToRefer return true; } qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks"; - uncalibrate(); - emitCalibrationStatus(false); return false; } diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 0b0406bb60..8397f8d804 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -73,6 +73,7 @@ private: void calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration); void calibrate(const controller::InputCalibrationData& inputCalibration); void uncalibrate(); + void sendUserActivityData(QString activity); void configureCalibrationSettings(const QJsonObject configurationSettings); QJsonObject configurationSettings(); controller::Pose addOffsetToPuckPose(int joint) const; @@ -106,7 +107,7 @@ private: void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); void calibrateFromHandController(const controller::InputCalibrationData& inputCalibrationData); void calibrateFromUI(const controller::InputCalibrationData& inputCalibrationData); - void emitCalibrationStatus(const bool success); + void emitCalibrationStatus(); void calibrateNextFrame(); @@ -139,7 +140,7 @@ private: FeetAndHips, FeetHipsAndChest, FeetHipsAndShoulders, - FeetHipsChestAndShoulders, + FeetHipsChestAndShoulders }; enum class HeadConfig { diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index a3d1923aa9..5ea1dd0963 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -20,7 +20,9 @@ var blastShareText = "Blast to my Connections", hifiShareText = "Share to Snaps Feed", hifiAlreadySharedText = "Already Shared to Snaps Feed", facebookShareText = "Share to Facebook", - twitterShareText = "Share to Twitter"; + twitterShareText = "Share to Twitter", + shareButtonLabelTextActive = "SHARE:", + shareButtonLabelTextInactive = "SHARE"; function fileExtensionMatches(filePath, extension) { return filePath.split('.').pop().toLowerCase() === extension; @@ -138,6 +140,8 @@ function selectImageToShare(selectedID, isSelected) { var imageContainer = document.getElementById(selectedID), image = document.getElementById(selectedID + 'img'), shareBar = document.getElementById(selectedID + "shareBar"), + showShareButtonsDots = document.getElementById(selectedID + "showShareButtonsDots"), + showShareButtonsLabel = document.getElementById(selectedID + "showShareButtonsLabel"), shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv"), shareBarHelp = document.getElementById(selectedID + "shareBarHelp"), showShareButtonsButtonDiv = document.getElementById(selectedID + "showShareButtonsButtonDiv"), @@ -156,6 +160,9 @@ function selectImageToShare(selectedID, isSelected) { shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)"; shareBar.style.pointerEvents = "initial"; + showShareButtonsDots.style.visibility = "hidden"; + showShareButtonsLabel.innerHTML = shareButtonLabelTextActive; + shareButtonsDiv.style.visibility = "visible"; shareBarHelp.style.visibility = "visible"; @@ -176,6 +183,9 @@ function selectImageToShare(selectedID, isSelected) { shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)"; shareBar.style.pointerEvents = "none"; + showShareButtonsDots.style.visibility = "visible"; + showShareButtonsLabel.innerHTML = shareButtonLabelTextInactive; + shareButtonsDiv.style.visibility = "hidden"; shareBarHelp.style.visibility = "hidden"; } @@ -185,6 +195,7 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl shareBarHelpID = parentID + "shareBarHelp", shareButtonsDivID = parentID + "shareButtonsDiv", showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv", + showShareButtonsDotsID = parentID + "showShareButtonsDots", showShareButtonsLabelID = parentID + "showShareButtonsLabel", blastToConnectionsButtonID = parentID + "blastToConnectionsButton", shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton", @@ -199,8 +210,8 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl if (canShare) { shareBarInnerHTML = '' + '
' + - '' + - '' + + '' + + '' + '' + '
' + '' + @@ -217,7 +228,7 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); }; } else { shareBarInnerHTML = '
' + - '' + + '' + '' + '' + '
' + @@ -230,7 +241,7 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl } } else { shareBarInnerHTML = '
' + - '' + + '' + '' + '' + '
' + diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp index b81d092662..28f91e8121 100644 --- a/tools/ac-client/src/ACClientApp.cpp +++ b/tools/ac-client/src/ACClientApp.cpp @@ -90,14 +90,16 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : auto nodeList = DependencyManager::get(); - // start the nodeThread so its event loop is running - nodeList->startThread(); // setup a timer for domain-server check ins QTimer* domainCheckInTimer = new QTimer(nodeList.data()); connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn); domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); + // start the nodeThread so its event loop is running + // (must happen after the checkin timer is created with the nodelist as it's parent) + nodeList->startThread(); + const DomainHandler& domainHandler = nodeList->getDomainHandler(); connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));