diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 586f81def3..6b66863534 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -1,28 +1,28 @@ -Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file. +Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only macOS specific instructions are found in this file. ### Homebrew -[Homebrew](https://brew.sh/) is an excellent package manager for OS X. It makes install of some High Fidelity dependencies very simple. +[Homebrew](https://brew.sh/) is an excellent package manager for macOS. It makes install of some High Fidelity dependencies very simple. - brew install cmake openssl + brew install cmake openssl qt ### OpenSSL Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations. For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR: - export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2h_1/ + export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2l Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change. ### Qt -Download and install the [Qt 5.6.2 for macOS](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-mac-x64-clang-5.6.2.dmg). +Assuming you've installed Qt using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installations. +For Qt installed via homebrew, set QT_CMAKE_PREFIX_PATH: -Keep the default components checked when going through the installer. + export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt/5.9.1/lib/cmake -Once Qt is installed, you need to manually configure the following: -* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.2/5.6/clang_64/lib/cmake/` directory. +Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change. ### Xcode diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 9df606c227..c03721d097 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1162,7 +1162,8 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, _pendingBakes.remove(originalAssetHash); } -void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, QVector bakedFilePaths) { +void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, + QString bakedTempOutputDir, QVector bakedFilePaths) { bool errorCompletingBake { false }; QString errorReason; @@ -1234,6 +1235,16 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina } } + for (auto& filePath : bakedFilePaths) { + QFile file(filePath); + if (!file.remove()) { + qWarning() << "Failed to remove temporary file:" << filePath; + } + } + if (!QDir(bakedTempOutputDir).rmdir(".")) { + qWarning() << "Failed to remove temporary directory:" << bakedTempOutputDir; + } + if (!errorCompletingBake) { // create the meta file to store which version of the baking process we just completed writeMetaFile(originalAssetHash); diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 94be560c9b..aeb40a416f 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -100,7 +100,8 @@ private: void bakeAsset(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath); /// Move baked content for asset to baked directory and update baked status - void handleCompletedBake(QString originalAssetHash, QString assetPath, QVector bakedFilePaths); + void handleCompletedBake(QString originalAssetHash, QString assetPath, QString bakedTempOutputDir, + QVector bakedFilePaths); void handleFailedBake(QString originalAssetHash, QString assetPath, QString errors); void handleAbortedBake(QString originalAssetHash, QString assetPath); diff --git a/assignment-client/src/assets/BakeAssetTask.cpp b/assignment-client/src/assets/BakeAssetTask.cpp index 9073510f79..94a0739612 100644 --- a/assignment-client/src/assets/BakeAssetTask.cpp +++ b/assignment-client/src/assets/BakeAssetTask.cpp @@ -24,20 +24,39 @@ BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetP } +void cleanupTempFiles(QString tempOutputDir, std::vector files) { + for (const auto& filename : files) { + QFile f { filename }; + if (!f.remove()) { + qDebug() << "Failed to remove:" << filename; + } + } + if (!tempOutputDir.isEmpty()) { + QDir dir { tempOutputDir }; + if (!dir.rmdir(".")) { + qDebug() << "Failed to remove temporary directory:" << tempOutputDir; + } + } +}; + void BakeAssetTask::run() { _isBaking.store(true); qRegisterMetaType >("QVector"); TextureBakerThreadGetter fn = []() -> QThread* { return QThread::currentThread(); }; + QString tempOutputDir; + if (_assetPath.endsWith(".fbx")) { + tempOutputDir = PathUtils::generateTemporaryDir(); _baker = std::unique_ptr { - new FBXBaker(QUrl("file:///" + _filePath), fn, PathUtils::generateTemporaryDir()) + new FBXBaker(QUrl("file:///" + _filePath), fn, tempOutputDir) }; } else { + tempOutputDir = PathUtils::generateTemporaryDir(); _baker = std::unique_ptr { new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, - PathUtils::generateTemporaryDir()) + tempOutputDir) }; } @@ -52,6 +71,8 @@ void BakeAssetTask::run() { _wasAborted.store(true); + cleanupTempFiles(tempOutputDir, _baker->getOutputFiles()); + emit bakeAborted(_assetHash, _assetPath); } else if (_baker->hasErrors()) { qDebug() << "Failed to bake: " << _assetHash << _assetPath << _baker->getErrors(); @@ -60,6 +81,8 @@ void BakeAssetTask::run() { _didFinish.store(true); + cleanupTempFiles(tempOutputDir, _baker->getOutputFiles()); + emit bakeFailed(_assetHash, _assetPath, errors); } else { auto vectorOutputFiles = QVector::fromStdVector(_baker->getOutputFiles()); @@ -68,7 +91,7 @@ void BakeAssetTask::run() { _didFinish.store(true); - emit bakeComplete(_assetHash, _assetPath, vectorOutputFiles); + emit bakeComplete(_assetHash, _assetPath, tempOutputDir, vectorOutputFiles); } } diff --git a/assignment-client/src/assets/BakeAssetTask.h b/assignment-client/src/assets/BakeAssetTask.h index 45e7ec8702..90458ac223 100644 --- a/assignment-client/src/assets/BakeAssetTask.h +++ b/assignment-client/src/assets/BakeAssetTask.h @@ -35,7 +35,7 @@ public: bool didFinish() const { return _didFinish.load(); } signals: - void bakeComplete(QString assetHash, QString assetPath, QVector outputFiles); + void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir, QVector outputFiles); void bakeFailed(QString assetHash, QString assetPath, QString errors); void bakeAborted(QString assetHash, QString assetPath); diff --git a/domain-server/resources/web/assignment/placeholder.js b/domain-server/resources/web/assignment/placeholder.js index 2c1d8253aa..bf64539bea 100644 --- a/domain-server/resources/web/assignment/placeholder.js +++ b/domain-server/resources/web/assignment/placeholder.js @@ -1,3 +1,3 @@ // Here you can put a script that will be run by an assignment-client (AC) -// For examples, please go to http://public.highfidelity.io/scripts +// For examples, please go to https://github.com/highfidelity/hifi/tree/master/script-archive/acScripts // The directory named acScripts contains assignment-client specific scripts you can try. diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 6b60cbff7b..5358ad1adc 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -171,7 +171,7 @@ ScrollingWindow { } function handleGetMappingsError(errorString) { - errorMessageBox("There was a problem retreiving the list of assets from your Asset Server.\n" + errorString); + errorMessageBox("There was a problem retrieving the list of assets from your Asset Server.\n" + errorString); } function addToWorld() { diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index b1f80ac5e8..12c2ec1835 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -167,6 +167,9 @@ Rectangle { } RalewayRegular { anchors.verticalCenter: parent.verticalCenter; + width: margins.sizeText + margins.sizeLevel + anchors.left: parent.left + anchors.leftMargin: margins.sizeCheckBox size: 16; color: hifi.colors.lightGrayText; text: qsTr("CHOOSE INPUT DEVICE"); diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 80c9a3ec0f..e7bbaec755 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -893,6 +893,10 @@ Rectangle { } else { root.activeView = "checkoutSuccess"; } + root.balanceReceived = false; + root.purchasesReceived = false; + commerce.inventory(); + commerce.balance(); } // diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml index 420b51ba15..cc316a70e9 100644 --- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml +++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml @@ -39,7 +39,7 @@ Item { sendToParent({method: "needsLogIn"}); } else if (walletStatus === 3) { commerce.getSecurityImage(); - } else { + } else if (walletStatus > 3) { console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus); } } diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 361a89d047..e343b93117 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -36,6 +36,8 @@ Item { property string itemHref; property int displayedItemCount; property int itemEdition; + property int numberSold; + property int limitedRun; property string originalStatusText; property string originalStatusColor; @@ -50,7 +52,6 @@ Item { statusText.text = "CONFIRMED!"; statusText.color = hifi.colors.blueAccent; confirmedTimer.start(); - root.purchaseStatusChanged = false; } } @@ -60,6 +61,7 @@ Item { onTriggered: { statusText.text = root.originalStatusText; statusText.color = root.originalStatusColor; + root.purchaseStatusChanged = false; } } @@ -203,7 +205,7 @@ Item { Item { id: statusContainer; - visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated"; + visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.purchaseStatusChanged; anchors.left: itemName.left; anchors.top: certificateContainer.bottom; anchors.topMargin: 8; @@ -222,6 +224,8 @@ Item { "PENDING..." } else if (root.purchaseStatus === "invalidated") { "INVALIDATED" + } else if (root.numberSold !== -1) { + ("Sales: " + root.numberSold + "/" + (root.limitedRun === -1 ? "INFTY" : root.limitedRun)) } else { "" } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 0bb1515b69..990fd348c6 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -81,8 +81,10 @@ Rectangle { if (result.status !== 'success') { console.log("Failed to get purchases", result.message); } else { + var inventoryResult = processInventoryResult(result.data.assets); + purchasesModel.clear(); - purchasesModel.append(result.data.assets); + purchasesModel.append(inventoryResult); if (previousPurchasesModel.count !== 0) { checkIfAnyItemStatusChanged(); @@ -93,7 +95,7 @@ Rectangle { purchasesModel.setProperty(i, "statusChanged", false); } } - previousPurchasesModel.append(result.data.assets); + previousPurchasesModel.append(inventoryResult); buildFilteredPurchasesModel(); @@ -428,6 +430,8 @@ Rectangle { purchaseStatus: status; purchaseStatusChanged: statusChanged; itemEdition: model.edition_number; + numberSold: model.number_sold; + limitedRun: model.limited_run; displayedItemCount: model.displayedItemCount; anchors.topMargin: 12; anchors.bottomMargin: 12; @@ -590,6 +594,17 @@ Rectangle { // FUNCTION DEFINITIONS START // + function processInventoryResult(inventory) { + for (var i = 0; i < inventory.length; i++) { + if (inventory[i].status.length > 1) { + console.log("WARNING: Inventory result index " + i + " has a status of length >1!") + } + inventory[i].status = inventory[i].status[0]; + inventory[i].categories = inventory[i].categories.join(';'); + } + return inventory; + } + function populateDisplayedItemCounts() { var itemCountDictionary = {}; var currentItemId; diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index 5bd88ba790..8d5d9f97de 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -197,6 +197,8 @@ Item { height: 50; echoMode: TextInput.Password; placeholderText: "passphrase"; + activeFocusOnPress: true; + activeFocusOnTab: true; onFocusChanged: { root.keyboardRaised = focus; @@ -206,8 +208,8 @@ Item { anchors.fill: parent; onClicked: { - parent.focus = true; root.keyboardRaised = true; + mouse.accepted = false; } } diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 44cd700eac..a02496a252 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -172,7 +172,7 @@ Rectangle { } function handleGetMappingsError(errorString) { - errorMessageBox("There was a problem retreiving the list of assets from your Asset Server.\n" + errorString); + errorMessageBox("There was a problem retrieving the list of assets from your Asset Server.\n" + errorString); } function addToWorld() { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bfc4052075..0fc8c46cdc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -189,7 +189,6 @@ #include "InterfaceParentFinder.h" #include "ui/OctreeStatsProvider.h" -#include "FrameTimingsScriptingInterface.h" #include #include #include @@ -798,8 +797,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo installNativeEventFilter(&MyNativeEventFilter::getInstance()); #endif - _logger = new FileLogger(this); // After setting organization name in order to get correct directory - + + _logger = new FileLogger(this); qInstallMessageHandler(messageHandler); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf"); @@ -815,6 +814,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo (new DeadlockWatchdogThread())->start(); } + // Set File Logger Session UUID + auto avatarManager = DependencyManager::get(); + auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr; + auto accountManager = DependencyManager::get(); + + _logger->setSessionID(accountManager->getSessionID()); + if (steamClient) { qCDebug(interfaceapp) << "[VERSION] SteamVR buildID:" << steamClient->getSteamVRBuildID(); } @@ -931,8 +937,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // send a location update immediately discoverabilityManager->updateLocation(); - auto myAvatar = getMyAvatar(); - connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded); connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled); connect(nodeList.data(), &NodeList::nodeActivated, this, &Application::nodeActivated); @@ -943,9 +947,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // you might think we could just do this in NodeList but we only want this connection for Interface connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset); - // connect to appropriate slots on AccountManager - auto accountManager = DependencyManager::get(); - auto dialogsManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog); connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle); @@ -2023,7 +2024,8 @@ void Application::cleanupBeforeQuit() { // The cleanup process enqueues the transactions but does not process them. Calling this here will force the actual // removal of the items. // See https://highfidelity.fogbugz.com/f/cases/5328 - _main3DScene->processTransactionQueue(); + _main3DScene->enqueueFrame(); // flush all the transactions + _main3DScene->processTransactionQueue(); // process and apply deletions // first stop all timers directly or by invokeMethod // depending on what thread they run in @@ -2100,7 +2102,6 @@ Application::~Application() { _octreeProcessor.terminate(); _entityEditSender.terminate(); - DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); @@ -2221,8 +2222,6 @@ void Application::initializeGL() { update(0); } -FrameTimingsScriptingInterface _frameTimingsScriptingInterface; - extern void setupPreferences(); void Application::initializeUi() { @@ -2383,301 +2382,105 @@ void Application::initializeUi() { offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); } -void Application::paintGL() { - // Some plugins process message events, allowing paintGL to be called reentrantly. - if (_aboutToQuit || _window->isMinimized()) { - return; - } - - _renderFrameCount++; - _lastTimeRendered.start(); - - auto lastPaintBegin = usecTimestampNow(); - PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_renderFrameCount); - PerformanceTimer perfTimer("paintGL"); - - if (nullptr == _displayPlugin) { - return; - } - - DisplayPluginPointer displayPlugin; - { - PROFILE_RANGE(render, "/getActiveDisplayPlugin"); - displayPlugin = getActiveDisplayPlugin(); - } - - { - 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(_renderFrameCount)) { - updateDisplayMode(); - return; - } - } - - // update the avatar with a fresh HMD pose - { - PROFILE_RANGE(render, "/updateAvatar"); - getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose()); - } - - auto lodManager = DependencyManager::get(); - - RenderArgs renderArgs; - - float sensorToWorldScale = getMyAvatar()->getSensorToWorldScale(); - { - PROFILE_RANGE(render, "/buildFrustrumAndArgs"); - { - QMutexLocker viewLocker(&_viewMutex); - // adjust near clip plane to account for sensor scaling. - auto adjustedProjection = glm::perspective(_viewFrustum.getFieldOfView(), - _viewFrustum.getAspectRatio(), - DEFAULT_NEAR_CLIP * sensorToWorldScale, - _viewFrustum.getFarClip()); - _viewFrustum.setProjection(adjustedProjection); - _viewFrustum.calculate(); - } - renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(), - lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, - RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); - { - QMutexLocker viewLocker(&_viewMutex); - renderArgs.setViewFrustum(_viewFrustum); - } - } - - { - 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 - QSize size = getDeviceSize(); - renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); - _applicationOverlay.renderOverlay(&renderArgs); - } +void Application::updateCamera(RenderArgs& renderArgs) { + PROFILE_RANGE(render, "/updateCamera"); + PerformanceTimer perfTimer("CameraUpdates"); glm::vec3 boomOffset; - { - PROFILE_RANGE(render, "/updateCamera"); - { - PerformanceTimer perfTimer("CameraUpdates"); + auto myAvatar = getMyAvatar(); + boomOffset = myAvatar->getModelScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; - auto myAvatar = getMyAvatar(); - boomOffset = myAvatar->getModelScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; + // The render mode is default or mirror if the camera is in mirror mode, assigned further below + renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - // 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(glmExtractRotation(camMat)); - } else { - _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(glmExtractRotation(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->getOrientation() * 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->getModelScale(), 0) - + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror - + mirrorBodyOrientation * hmdOffset); - } else { - _myCamera.setOrientation(myAvatar->getOrientation() - * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 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()); - } - } + // 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(glmExtractRotation(camMat)); + } + else { + _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(glmExtractRotation(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); } - // Update camera position - if (!isHMDMode()) { - _myCamera.update(); + else { + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + myAvatar->getOrientation() * boomOffset); } } } + else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + if (isHMDMode()) { + auto mirrorBodyOrientation = myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); - { - PROFILE_RANGE(render, "/updateCompositor"); - getApplicationCompositor().setFrameInfo(_renderFrameCount, _myCamera.getTransform(), getMyAvatar()->getSensorToWorldMatrix()); - } + 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); - 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(); - } + glm::quat worldMirrorRotation = mirrorBodyOrientation * mirrorHmdRotation; - auto hmdInterface = DependencyManager::get(); - float ipdScale = hmdInterface->getIPDScale(); + _myCamera.setOrientation(worldMirrorRotation); - // scale IPD by sensorToWorldScale, to make the world seem larger or smaller accordingly. - ipdScale *= sensorToWorldScale; + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + // Mirror HMD lateral offsets + hmdOffset.x = -hmdOffset.x; - { - 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, finalFramebufferSize.width(), finalFramebufferSize.height()); - auto baseProjection = renderArgs.getViewFrustum().getProjection(); - 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 - // plugins will imply the combined projection for both eyes. - // - // This is properly implemented for the Oculus plugins, but for OpenVR - // and Stereo displays I'm not sure how to get / calculate it, so we're - // just relying on the left FOV in each case and hoping that the - // overall culling margin of error doesn't cause popping in the - // right eye. There are FIXMEs in the relevant plugins - _myCamera.setProjection(displayPlugin->getCullingProjection(baseProjection)); - renderArgs._context->enableStereo(true); - mat4 eyeOffsets[2]; - mat4 eyeProjections[2]; - - // FIXME we probably don't need to set the projection matrix every frame, - // only when the display plugin changes (or in non-HMD modes when the user - // changes the FOV manually, which right now I don't think they can. - for_each_eye([&](Eye eye) { - // For providing the stereo eye views, the HMD head pose has already been - // applied to the avatar, so we need to get the difference between the head - // pose applied to the avatar and the per eye pose, and use THAT as - // the per-eye stereo matrix adjustment. - mat4 eyeToHead = displayPlugin->getEyeToHeadTransform(eye); - // Grab the translation - vec3 eyeOffset = glm::vec3(eyeToHead[3]); - // Apply IPD scaling - mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * ipdScale); - eyeOffsets[eye] = eyeOffsetTransform; - eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection); - }); - renderArgs._context->setStereoProjections(eyeProjections); - renderArgs._context->setStereoViews(eyeOffsets); - - // Configure the type of display / stereo - renderArgs._displayMode = (isHMDMode() ? RenderArgs::STEREO_HMD : RenderArgs::STEREO_MONITOR); + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) + + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + + mirrorBodyOrientation * hmdOffset); } - renderArgs._blitFramebuffer = finalFramebuffer; - displaySide(&renderArgs, _myCamera); + else { + _myCamera.setOrientation(myAvatar->getOrientation() + * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 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(); } - gpu::Batch postCompositeBatch; - { - PROFILE_RANGE(render, "/postComposite"); - PerformanceTimer perfTimer("postComposite"); - renderArgs._batch = &postCompositeBatch; - renderArgs._batch->setViewportTransform(ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height())); - renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView()); - _overlays.render3DHUDOverlays(&renderArgs); - } - - auto frame = _gpuContext->endFrame(); - frame->frameIndex = _renderFrameCount; - frame->framebuffer = finalFramebuffer; - frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){ - DependencyManager::get()->releaseFramebuffer(framebuffer); - }; - frame->overlay = _applicationOverlay.getOverlayTexture(); - frame->postCompositeBatch = postCompositeBatch; - // deliver final scene rendering commands to the display plugin - { - PROFILE_RANGE(render, "/pluginOutput"); - PerformanceTimer perfTimer("pluginOutput"); - _renderLoopCounter.increment(); - displayPlugin->submitFrame(frame); - } - - // Reset the framebuffer and stereo state - renderArgs._blitFramebuffer.reset(); - renderArgs._context->enableStereo(false); - - { - Stats::getInstance()->setRenderDetails(renderArgs._details); - } - - uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; - _frameTimingsScriptingInterface.addValue(lastPaintDuration); + renderArgs._cameraMode = (int8_t)_myCamera.getMode(); } void Application::runTests() { @@ -5225,6 +5028,7 @@ void Application::update(float deltaTime) { avatarManager->postUpdate(deltaTime, getMain3DScene()); + { PROFILE_RANGE_EX(app, "PreRenderLambdas", 0xffff0000, (uint64_t)0); @@ -5235,9 +5039,123 @@ void Application::update(float deltaTime) { _postUpdateLambdas.clear(); } + editRenderArgs([this](AppRenderArgs& appRenderArgs) { + appRenderArgs._headPose= getHMDSensorPose(); + + auto myAvatar = getMyAvatar(); + + // update the avatar with a fresh HMD pose + { + PROFILE_RANGE(render, "/updateAvatar"); + myAvatar->updateFromHMDSensorMatrix(appRenderArgs._headPose); + } + + auto lodManager = DependencyManager::get(); + + float sensorToWorldScale = getMyAvatar()->getSensorToWorldScale(); + appRenderArgs._sensorToWorldScale = sensorToWorldScale; + { + PROFILE_RANGE(render, "/buildFrustrumAndArgs"); + { + QMutexLocker viewLocker(&_viewMutex); + // adjust near clip plane to account for sensor scaling. + auto adjustedProjection = glm::perspective(_viewFrustum.getFieldOfView(), + _viewFrustum.getAspectRatio(), + DEFAULT_NEAR_CLIP * sensorToWorldScale, + _viewFrustum.getFarClip()); + _viewFrustum.setProjection(adjustedProjection); + _viewFrustum.calculate(); + } + appRenderArgs._renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(), + lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, + RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); + appRenderArgs._renderArgs._scene = getMain3DScene(); + + { + QMutexLocker viewLocker(&_viewMutex); + appRenderArgs._renderArgs.setViewFrustum(_viewFrustum); + } + } + { + PROFILE_RANGE(render, "/resizeGL"); + PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::paintGL()"); + resizeGL(); + } + + this->updateCamera(appRenderArgs._renderArgs); + appRenderArgs._isStereo = false; + + { + auto hmdInterface = DependencyManager::get(); + float ipdScale = hmdInterface->getIPDScale(); + + // scale IPD by sensorToWorldScale, to make the world seem larger or smaller accordingly. + ipdScale *= sensorToWorldScale; + + auto baseProjection = appRenderArgs._renderArgs.getViewFrustum().getProjection(); + if (getActiveDisplayPlugin()->isStereo()) { + // Stereo modes will typically have a larger projection matrix overall, + // so we ask for the 'mono' projection matrix, which for stereo and HMD + // plugins will imply the combined projection for both eyes. + // + // This is properly implemented for the Oculus plugins, but for OpenVR + // and Stereo displays I'm not sure how to get / calculate it, so we're + // just relying on the left FOV in each case and hoping that the + // overall culling margin of error doesn't cause popping in the + // right eye. There are FIXMEs in the relevant plugins + _myCamera.setProjection(getActiveDisplayPlugin()->getCullingProjection(baseProjection)); + appRenderArgs._isStereo = true; + + auto& eyeOffsets = appRenderArgs._eyeOffsets; + auto& eyeProjections = appRenderArgs._eyeProjections; + + // FIXME we probably don't need to set the projection matrix every frame, + // only when the display plugin changes (or in non-HMD modes when the user + // changes the FOV manually, which right now I don't think they can. + for_each_eye([&](Eye eye) { + // For providing the stereo eye views, the HMD head pose has already been + // applied to the avatar, so we need to get the difference between the head + // pose applied to the avatar and the per eye pose, and use THAT as + // the per-eye stereo matrix adjustment. + mat4 eyeToHead = getActiveDisplayPlugin()->getEyeToHeadTransform(eye); + // Grab the translation + vec3 eyeOffset = glm::vec3(eyeToHead[3]); + // Apply IPD scaling + mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * ipdScale); + eyeOffsets[eye] = eyeOffsetTransform; + eyeProjections[eye] = getActiveDisplayPlugin()->getEyeProjection(eye, baseProjection); + }); + + // Configure the type of display / stereo + appRenderArgs._renderArgs._displayMode = (isHMDMode() ? RenderArgs::STEREO_HMD : RenderArgs::STEREO_MONITOR); + } + } + + // HACK + // load the view frustum + // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering. + // Then we can move this logic into the Avatar::simulate call. + myAvatar->preDisplaySide(&appRenderArgs._renderArgs); + + { + QMutexLocker viewLocker(&_viewMutex); + _myCamera.loadViewFrustum(_displayViewFrustum); + } + + { + QMutexLocker viewLocker(&_viewMutex); + appRenderArgs._renderArgs.setViewFrustum(_displayViewFrustum); + } + }); + AnimDebugDraw::getInstance().update(); DependencyManager::get()->update(); + + // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over + getMain3DScene()->enqueueFrame(); } void Application::sendAvatarViewFrustum() { @@ -5296,7 +5214,6 @@ int Application::sendNackPackets() { } }); - return packetsSent; } @@ -5527,116 +5444,6 @@ void Application::copyDisplayViewFrustum(ViewFrustum& viewOut) const { viewOut = _displayViewFrustum; } -void Application::copyShadowViewFrustum(ViewFrustum& viewOut) const { - QMutexLocker viewLocker(&_viewMutex); - viewOut = _shadowViewFrustum; -} - -// WorldBox Render Data & rendering functions - -class WorldBoxRenderData { -public: - typedef render::Payload Payload; - typedef Payload::DataPointer Pointer; - - int _val = 0; - static render::ItemID _item; // unique WorldBoxRenderData -}; - -render::ItemID WorldBoxRenderData::_item { render::Item::INVALID_ITEM_ID }; - -namespace render { - template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } - template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } - template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { - if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { - PerformanceTimer perfTimer("worldBox"); - - auto& batch = *args->_batch; - DependencyManager::get()->bindSimpleProgram(batch); - renderWorldBox(args, batch); - } - } -} - -void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool selfAvatarOnly) { - - // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering. - // Then we can move this logic into the Avatar::simulate call. - auto myAvatar = getMyAvatar(); - myAvatar->preDisplaySide(renderArgs); - - PROFILE_RANGE(render, __FUNCTION__); - PerformanceTimer perfTimer("display"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()"); - - // load the view frustum - { - QMutexLocker viewLocker(&_viewMutex); - theCamera.loadViewFrustum(_displayViewFrustum); - } - - // TODO fix shadows and make them use the GPU library - - // The pending changes collecting the changes here - render::Transaction transaction; - - // Assuming nothing gets rendered through that - if (!selfAvatarOnly) { - if (DependencyManager::get()->shouldRenderEntities()) { - // render models... - PerformanceTimer perfTimer("entities"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... entities..."); - - RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; - - if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) { - renderDebugFlags = static_cast(renderDebugFlags | - static_cast(RenderArgs::RENDER_DEBUG_HULLS)); - } - renderArgs->_debugFlags = renderDebugFlags; - //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction); - } - } - - // FIXME: Move this out of here!, WorldBox should be driven by the entity content just like the other entities - // Make sure the WorldBox is in the scene - if (!render::Item::isValidID(WorldBoxRenderData::_item)) { - auto worldBoxRenderData = make_shared(); - auto worldBoxRenderPayload = make_shared(worldBoxRenderData); - - WorldBoxRenderData::_item = _main3DScene->allocateID(); - - transaction.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload); - } else { - transaction.updateItem(WorldBoxRenderData::_item, - [](WorldBoxRenderData& payload) { - payload._val++; - }); - } - - { - _main3DScene->enqueueTransaction(transaction); - } - - // For now every frame pass the renderContext - { - PerformanceTimer perfTimer("EngineRun"); - - { - QMutexLocker viewLocker(&_viewMutex); - renderArgs->setViewFrustum(_displayViewFrustum); - } - renderArgs->_cameraMode = (int8_t)theCamera.getMode(); // HACK - renderArgs->_scene = getMain3DScene(); - _renderEngine->getRenderContext()->args = renderArgs; - - // Before the deferred pass, let's try to use the render engine - _renderEngine->run(); - } -} - void Application::resetSensors(bool andReload) { DependencyManager::get()->reset(); DependencyManager::get()->reset(); @@ -7677,5 +7484,4 @@ void Application::setAvatarOverrideUrl(const QUrl& url, bool save) { _avatarOverrideUrl = url; _saveAvatarOverrideUrl = save; } - #include "Application.moc" diff --git a/interface/src/Application.h b/interface/src/Application.h index f3c7f82b4b..478642e2da 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -76,6 +76,7 @@ #include #include #include +#include "FrameTimingsScriptingInterface.h" #include "Sound.h" @@ -147,6 +148,8 @@ public: void initializeGL(); void initializeUi(); + + void updateCamera(RenderArgs& renderArgs); void paintGL(); void resizeGL(); @@ -173,7 +176,6 @@ public: // which might be different from the viewFrustum, i.e. shadowmap // passes, mirror window passes, etc void copyDisplayViewFrustum(ViewFrustum& viewOut) const; - void copyShadowViewFrustum(ViewFrustum& viewOut) const override; const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } QSharedPointer getEntities() const { return DependencyManager::get(); } QUndoStack* getUndoStack() { return &_undoStack; } @@ -467,8 +469,6 @@ private: void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions); - void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed); - int sendNackPackets(); void sendAvatarViewFrustum(); @@ -478,7 +478,7 @@ private: void initializeAcceptedFiles(); - void displaySide(RenderArgs* renderArgs, Camera& whichCamera, bool selfAvatarOnly = false); + void runRenderFrame(RenderArgs* renderArgs/*, Camera& whichCamera, bool selfAvatarOnly = false*/); bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); @@ -535,6 +535,8 @@ private: RateCounter<500> _renderLoopCounter; RateCounter<500> _gameLoopCounter; + FrameTimingsScriptingInterface _frameTimingsScriptingInterface; + QTimer _minimizedWindowTimer; QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; @@ -550,7 +552,6 @@ private: ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels) ViewFrustum _displayViewFrustum; - ViewFrustum _shadowViewFrustum; quint64 _lastQueriedTime; OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers @@ -621,6 +622,24 @@ private: render::EnginePointer _renderEngine{ new render::Engine() }; gpu::ContextPointer _gpuContext; // initialized during window creation + mutable QMutex _renderArgsMutex{ QMutex::Recursive }; + struct AppRenderArgs { + render::Args _renderArgs; + glm::mat4 _eyeToWorld; + glm::mat4 _eyeOffsets[2]; + glm::mat4 _eyeProjections[2]; + glm::mat4 _headPose; + glm::mat4 _sensorToWorld; + float _sensorToWorldScale { 1.0f }; + bool _isStereo{ false }; + }; + AppRenderArgs _appRenderArgs; + + + using RenderArgsEditor = std::function ; + void editRenderArgs(RenderArgsEditor editor); + + Overlays _overlays; ApplicationOverlay _applicationOverlay; OverlayConductor _overlayConductor; diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp new file mode 100644 index 0000000000..bbcd8266d7 --- /dev/null +++ b/interface/src/Application_render.cpp @@ -0,0 +1,240 @@ +// +// Application_render.cpp +// interface/src +// +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Application.h" +#include + +#include +#include +#include "ui/Stats.h" +#include +#include "Util.h" + + +// Statically provided display and input plugins +extern DisplayPluginList getDisplayPlugins(); + +void Application::editRenderArgs(RenderArgsEditor editor) { + QMutexLocker renderLocker(&_renderArgsMutex); + editor(_appRenderArgs); + +} + +void Application::paintGL() { + // Some plugins process message events, allowing paintGL to be called reentrantly. + if (_aboutToQuit || _window->isMinimized()) { + return; + } + + _renderFrameCount++; + _lastTimeRendered.start(); + + auto lastPaintBegin = usecTimestampNow(); + PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_renderFrameCount); + PerformanceTimer perfTimer("paintGL"); + + if (nullptr == _displayPlugin) { + return; + } + + DisplayPluginPointer displayPlugin; + { + PROFILE_RANGE(render, "/getActiveDisplayPlugin"); + displayPlugin = getActiveDisplayPlugin(); + } + + { + 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(_renderFrameCount)) { + updateDisplayMode(); + return; + } + } + + RenderArgs renderArgs; + glm::mat4 HMDSensorPose; + glm::mat4 eyeToWorld; + glm::mat4 sensorToWorld; + + bool isStereo; + glm::mat4 stereoEyeOffsets[2]; + glm::mat4 stereoEyeProjections[2]; + + { + QMutexLocker viewLocker(&_renderArgsMutex); + renderArgs = _appRenderArgs._renderArgs; + HMDSensorPose = _appRenderArgs._headPose; + eyeToWorld = _appRenderArgs._eyeToWorld; + sensorToWorld = _appRenderArgs._sensorToWorld; + isStereo = _appRenderArgs._isStereo; + for_each_eye([&](Eye eye) { + stereoEyeOffsets[eye] = _appRenderArgs._eyeOffsets[eye]; + stereoEyeProjections[eye] = _appRenderArgs._eyeProjections[eye]; + }); + } + + { + PROFILE_RANGE(render, "/gpuContextReset"); + _gpuContext->beginFrame(HMDSensorPose); + // 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 + QSize size = getDeviceSize(); + renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); + _applicationOverlay.renderOverlay(&renderArgs); + } + + { + PROFILE_RANGE(render, "/updateCompositor"); + getApplicationCompositor().setFrameInfo(_renderFrameCount, eyeToWorld, sensorToWorld); + } + + 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(); + } + + { + if (isStereo) { + renderArgs._context->enableStereo(true); + renderArgs._context->setStereoProjections(stereoEyeProjections); + renderArgs._context->setStereoViews(stereoEyeOffsets); + } + + renderArgs._blitFramebuffer = finalFramebuffer; + runRenderFrame(&renderArgs); + } + + gpu::Batch postCompositeBatch; + { + PROFILE_RANGE(render, "/postComposite"); + PerformanceTimer perfTimer("postComposite"); + renderArgs._batch = &postCompositeBatch; + renderArgs._batch->setViewportTransform(ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height())); + renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView()); + _overlays.render3DHUDOverlays(&renderArgs); + } + + auto frame = _gpuContext->endFrame(); + frame->frameIndex = _renderFrameCount; + frame->framebuffer = finalFramebuffer; + frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer) { + DependencyManager::get()->releaseFramebuffer(framebuffer); + }; + frame->overlay = _applicationOverlay.getOverlayTexture(); + frame->postCompositeBatch = postCompositeBatch; + // deliver final scene rendering commands to the display plugin + { + PROFILE_RANGE(render, "/pluginOutput"); + PerformanceTimer perfTimer("pluginOutput"); + _renderLoopCounter.increment(); + displayPlugin->submitFrame(frame); + } + + // Reset the framebuffer and stereo state + renderArgs._blitFramebuffer.reset(); + renderArgs._context->enableStereo(false); + + { + Stats::getInstance()->setRenderDetails(renderArgs._details); + } + + uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; + _frameTimingsScriptingInterface.addValue(lastPaintDuration); +} + + +// WorldBox Render Data & rendering functions + +class WorldBoxRenderData { +public: + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + int _val = 0; + static render::ItemID _item; // unique WorldBoxRenderData +}; + +render::ItemID WorldBoxRenderData::_item{ render::Item::INVALID_ITEM_ID }; + +namespace render { + template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } + template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } + template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { + if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { + PerformanceTimer perfTimer("worldBox"); + + auto& batch = *args->_batch; + DependencyManager::get()->bindSimpleProgram(batch); + renderWorldBox(args, batch); + } + } +} + +void Application::runRenderFrame(RenderArgs* renderArgs) { + PROFILE_RANGE(render, __FUNCTION__); + PerformanceTimer perfTimer("display"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::runRenderFrame()"); + + // The pending changes collecting the changes here + render::Transaction transaction; + + if (DependencyManager::get()->shouldRenderEntities()) { + // render models... + PerformanceTimer perfTimer("entities"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::runRenderFrame() ... entities..."); + + RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; + + if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) { + renderDebugFlags = static_cast(renderDebugFlags | + static_cast(RenderArgs::RENDER_DEBUG_HULLS)); + } + renderArgs->_debugFlags = renderDebugFlags; + } + + // Make sure the WorldBox is in the scene + // For the record, this one RenderItem is the first one we created and added to the scene. + // We could meoee that code elsewhere but you know... + if (!render::Item::isValidID(WorldBoxRenderData::_item)) { + auto worldBoxRenderData = std::make_shared(); + auto worldBoxRenderPayload = std::make_shared(worldBoxRenderData); + + WorldBoxRenderData::_item = _main3DScene->allocateID(); + + transaction.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload); + _main3DScene->enqueueTransaction(transaction); + } + + { + PerformanceTimer perfTimer("EngineRun"); + _renderEngine->getRenderContext()->args = renderArgs; + _renderEngine->run(); + } +} diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 55ddd01123..0e0f13cd6c 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -49,11 +49,13 @@ LaserPointer::~LaserPointer() { } void LaserPointer::enable() { + QWriteLocker lock(getLock()); DependencyManager::get()->enableRayPick(_rayPickUID); _renderingEnabled = true; } void LaserPointer::disable() { + QWriteLocker lock(getLock()); DependencyManager::get()->disableRayPick(_rayPickUID); _renderingEnabled = false; if (!_currentRenderState.empty()) { @@ -67,6 +69,7 @@ void LaserPointer::disable() { } void LaserPointer::setRenderState(const std::string& state) { + QWriteLocker lock(getLock()); if (!_currentRenderState.empty() && state != _currentRenderState) { if (_renderStates.find(_currentRenderState) != _renderStates.end()) { disableRenderState(_renderStates[_currentRenderState]); @@ -79,6 +82,7 @@ void LaserPointer::setRenderState(const std::string& state) { } void LaserPointer::editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) { + QWriteLocker lock(getLock()); updateRenderStateOverlay(_renderStates[state].getStartID(), startProps); updateRenderStateOverlay(_renderStates[state].getPathID(), pathProps); updateRenderStateOverlay(_renderStates[state].getEndID(), endProps); @@ -92,6 +96,11 @@ void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& } } +const RayPickResult LaserPointer::getPrevRayPickResult() { + QReadLocker lock(getLock()); + return DependencyManager::get()->getPrevRayPickResult(_rayPickUID); +} + void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState) { if (!renderState.getStartID().isNull()) { QVariantMap startProps; @@ -183,6 +192,8 @@ void LaserPointer::disableRenderState(const RenderState& renderState) { } void LaserPointer::update() { + // This only needs to be a read lock because update won't change any of the properties that can be modified from scripts + QReadLocker lock(getLock()); RayPickResult prevRayPickResult = DependencyManager::get()->getPrevRayPickResult(_rayPickUID); if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() && (prevRayPickResult.type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) { @@ -198,6 +209,51 @@ void LaserPointer::update() { } } +void LaserPointer::setPrecisionPicking(const bool precisionPicking) { + QWriteLocker lock(getLock()); + DependencyManager::get()->setPrecisionPicking(_rayPickUID, precisionPicking); +} + +void LaserPointer::setLaserLength(const float laserLength) { + QWriteLocker lock(getLock()); + _laserLength = laserLength; +} + +void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay) { + QWriteLocker lock(getLock()); + _objectLockEnd = std::pair(objectID, isOverlay); +} + +void LaserPointer::setIgnoreEntities(const QScriptValue& ignoreEntities) { + QWriteLocker lock(getLock()); + DependencyManager::get()->setIgnoreEntities(_rayPickUID, ignoreEntities); +} + +void LaserPointer::setIncludeEntities(const QScriptValue& includeEntities) { + QWriteLocker lock(getLock()); + DependencyManager::get()->setIncludeEntities(_rayPickUID, includeEntities); +} + +void LaserPointer::setIgnoreOverlays(const QScriptValue& ignoreOverlays) { + QWriteLocker lock(getLock()); + DependencyManager::get()->setIgnoreOverlays(_rayPickUID, ignoreOverlays); +} + +void LaserPointer::setIncludeOverlays(const QScriptValue& includeOverlays) { + QWriteLocker lock(getLock()); + DependencyManager::get()->setIncludeOverlays(_rayPickUID, includeOverlays); +} + +void LaserPointer::setIgnoreAvatars(const QScriptValue& ignoreAvatars) { + QWriteLocker lock(getLock()); + DependencyManager::get()->setIgnoreAvatars(_rayPickUID, ignoreAvatars); +} + +void LaserPointer::setIncludeAvatars(const QScriptValue& includeAvatars) { + QWriteLocker lock(getLock()); + DependencyManager::get()->setIncludeAvatars(_rayPickUID, includeAvatars); +} + RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, const OverlayID& endID) : _startID(startID), _pathID(pathID), _endID(endID) { diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 5467a8233e..01dfe01cfd 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -58,22 +58,24 @@ public: QUuid getRayUID() { return _rayPickUID; } void enable(); void disable(); - const RayPickResult getPrevRayPickResult() { return DependencyManager::get()->getPrevRayPickResult(_rayPickUID); } + const RayPickResult getPrevRayPickResult(); void setRenderState(const std::string& state); // You cannot use editRenderState to change the overlay type of any part of the laser pointer. You can only edit the properties of the existing overlays. void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps); - void setPrecisionPicking(const bool precisionPicking) { DependencyManager::get()->setPrecisionPicking(_rayPickUID, precisionPicking); } - void setLaserLength(const float laserLength) { _laserLength = laserLength; } - void setIgnoreEntities(const QScriptValue& ignoreEntities) { DependencyManager::get()->setIgnoreEntities(_rayPickUID, ignoreEntities); } - void setIncludeEntities(const QScriptValue& includeEntities) { DependencyManager::get()->setIncludeEntities(_rayPickUID, includeEntities); } - void setIgnoreOverlays(const QScriptValue& ignoreOverlays) { DependencyManager::get()->setIgnoreOverlays(_rayPickUID, ignoreOverlays); } - void setIncludeOverlays(const QScriptValue& includeOverlays) { DependencyManager::get()->setIncludeOverlays(_rayPickUID, includeOverlays); } - void setIgnoreAvatars(const QScriptValue& ignoreAvatars) { DependencyManager::get()->setIgnoreAvatars(_rayPickUID, ignoreAvatars); } - void setIncludeAvatars(const QScriptValue& includeAvatars) { DependencyManager::get()->setIncludeAvatars(_rayPickUID, includeAvatars); } + void setPrecisionPicking(const bool precisionPicking); + void setLaserLength(const float laserLength); + void setLockEndUUID(QUuid objectID, const bool isOverlay); - void setLockEndUUID(QUuid objectID, const bool isOverlay) { _objectLockEnd = std::pair(objectID, isOverlay); } + void setIgnoreEntities(const QScriptValue& ignoreEntities); + void setIncludeEntities(const QScriptValue& includeEntities); + void setIgnoreOverlays(const QScriptValue& ignoreOverlays); + void setIncludeOverlays(const QScriptValue& includeOverlays); + void setIgnoreAvatars(const QScriptValue& ignoreAvatars); + void setIncludeAvatars(const QScriptValue& includeAvatars); + + QReadWriteLock* getLock() { return &_lock; } void update(); @@ -89,6 +91,7 @@ private: std::pair _objectLockEnd { std::pair(QUuid(), false)}; QUuid _rayPickUID; + QReadWriteLock _lock; void updateRenderStateOverlay(const OverlayID& id, const QVariant& props); void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState); diff --git a/interface/src/raypick/LaserPointerManager.cpp b/interface/src/raypick/LaserPointerManager.cpp index b19ecc14f0..8615a96c3f 100644 --- a/interface/src/raypick/LaserPointerManager.cpp +++ b/interface/src/raypick/LaserPointerManager.cpp @@ -17,7 +17,6 @@ QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const La QWriteLocker containsLock(&_containsLock); QUuid id = QUuid::createUuid(); _laserPointers[id] = laserPointer; - _laserPointerLocks[id] = std::make_shared(); return id; } return QUuid(); @@ -26,46 +25,45 @@ QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const La void LaserPointerManager::removeLaserPointer(const QUuid uid) { QWriteLocker lock(&_containsLock); _laserPointers.remove(uid); - _laserPointerLocks.remove(uid); } void LaserPointerManager::enableLaserPointer(const QUuid uid) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->enable(); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->enable(); } } void LaserPointerManager::disableLaserPointer(const QUuid uid) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->disable(); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->disable(); } } void LaserPointerManager::setRenderState(QUuid uid, const std::string& renderState) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->setRenderState(renderState); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->setRenderState(renderState); } } void LaserPointerManager::editRenderState(QUuid uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->editRenderState(state, startProps, pathProps, endProps); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->editRenderState(state, startProps, pathProps, endProps); } } const RayPickResult LaserPointerManager::getPrevRayPickResult(const QUuid uid) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QReadLocker laserLock(_laserPointerLocks[uid].get()); - return _laserPointers[uid]->getPrevRayPickResult(); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + return laserPointer.value()->getPrevRayPickResult(); } return RayPickResult(); } @@ -73,80 +71,79 @@ const RayPickResult LaserPointerManager::getPrevRayPickResult(const QUuid uid) { void LaserPointerManager::update() { QReadLocker lock(&_containsLock); for (QUuid& uid : _laserPointers.keys()) { - // This only needs to be a read lock because update won't change any of the properties that can be modified from scripts - QReadLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->update(); + auto laserPointer = _laserPointers.find(uid); + laserPointer.value()->update(); } } void LaserPointerManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->setPrecisionPicking(precisionPicking); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->setPrecisionPicking(precisionPicking); } } void LaserPointerManager::setLaserLength(QUuid uid, const float laserLength) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->setLaserLength(laserLength); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->setLaserLength(laserLength); } } void LaserPointerManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->setIgnoreEntities(ignoreEntities); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->setIgnoreEntities(ignoreEntities); } } void LaserPointerManager::setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->setIncludeEntities(includeEntities); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->setIncludeEntities(includeEntities); } } void LaserPointerManager::setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->setIgnoreOverlays(ignoreOverlays); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->setIgnoreOverlays(ignoreOverlays); } } void LaserPointerManager::setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->setIncludeOverlays(includeOverlays); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->setIncludeOverlays(includeOverlays); } } void LaserPointerManager::setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->setIgnoreAvatars(ignoreAvatars); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->setIgnoreAvatars(ignoreAvatars); } } void LaserPointerManager::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->setIncludeAvatars(includeAvatars); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->setIncludeAvatars(includeAvatars); } } void LaserPointerManager::setLockEndUUID(QUuid uid, QUuid objectID, const bool isOverlay) { QReadLocker lock(&_containsLock); - if (_laserPointers.contains(uid)) { - QWriteLocker laserLock(_laserPointerLocks[uid].get()); - _laserPointers[uid]->setLockEndUUID(objectID, isOverlay); + auto laserPointer = _laserPointers.find(uid); + if (laserPointer != _laserPointers.end()) { + laserPointer.value()->setLockEndUUID(objectID, isOverlay); } } diff --git a/interface/src/raypick/LaserPointerManager.h b/interface/src/raypick/LaserPointerManager.h index 6494bb7056..b841877578 100644 --- a/interface/src/raypick/LaserPointerManager.h +++ b/interface/src/raypick/LaserPointerManager.h @@ -13,7 +13,6 @@ #include #include -#include #include "LaserPointer.h" @@ -46,7 +45,6 @@ public: private: QHash> _laserPointers; - QHash> _laserPointerLocks; QReadWriteLock _containsLock; }; diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index 70170a8f85..a5b1299210 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -16,3 +16,47 @@ RayPick::RayPick(const RayPickFilter& filter, const float maxDistance, const boo _enabled(enabled) { } +void RayPick::enable() { + QWriteLocker lock(getLock()); + _enabled = true; +} + +void RayPick::disable() { + QWriteLocker lock(getLock()); + _enabled = false; +} + +const RayPickResult& RayPick::getPrevRayPickResult() { + QReadLocker lock(getLock()); + return _prevResult; +} + +void RayPick::setIgnoreEntities(const QScriptValue& ignoreEntities) { + QWriteLocker lock(getLock()); + _ignoreEntities = qVectorEntityItemIDFromScriptValue(ignoreEntities); +} + +void RayPick::setIncludeEntities(const QScriptValue& includeEntities) { + QWriteLocker lock(getLock()); + _includeEntities = qVectorEntityItemIDFromScriptValue(includeEntities); +} + +void RayPick::setIgnoreOverlays(const QScriptValue& ignoreOverlays) { + QWriteLocker lock(getLock()); + _ignoreOverlays = qVectorOverlayIDFromScriptValue(ignoreOverlays); +} + +void RayPick::setIncludeOverlays(const QScriptValue& includeOverlays) { + QWriteLocker lock(getLock()); + _includeOverlays = qVectorOverlayIDFromScriptValue(includeOverlays); +} + +void RayPick::setIgnoreAvatars(const QScriptValue& ignoreAvatars) { + QWriteLocker lock(getLock()); + _ignoreAvatars = qVectorEntityItemIDFromScriptValue(ignoreAvatars); +} + +void RayPick::setIncludeAvatars(const QScriptValue& includeAvatars) { + QWriteLocker lock(getLock()); + _includeAvatars = qVectorEntityItemIDFromScriptValue(includeAvatars); +} \ No newline at end of file diff --git a/interface/src/raypick/RayPick.h b/interface/src/raypick/RayPick.h index 0686a05718..6dacc084b4 100644 --- a/interface/src/raypick/RayPick.h +++ b/interface/src/raypick/RayPick.h @@ -16,6 +16,7 @@ #include "EntityItemID.h" #include "ui/overlays/Overlay.h" +#include class RayPickFilter { public: @@ -102,13 +103,13 @@ public: virtual const PickRay getPickRay(bool& valid) const = 0; - void enable() { _enabled = true; } - void disable() { _enabled = false; } + void enable(); + void disable(); const RayPickFilter& getFilter() { return _filter; } float getMaxDistance() { return _maxDistance; } bool isEnabled() { return _enabled; } - const RayPickResult& getPrevRayPickResult() { return _prevResult; } + const RayPickResult& getPrevRayPickResult(); void setPrecisionPicking(bool precisionPicking) { _filter.setFlag(RayPickFilter::PICK_COURSE, !precisionPicking); } @@ -120,12 +121,14 @@ public: const QVector& getIncludeOverlays() { return _includeOverlays; } const QVector& getIgnoreAvatars() { return _ignoreAvatars; } const QVector& getIncludeAvatars() { return _includeAvatars; } - void setIgnoreEntities(const QScriptValue& ignoreEntities) { _ignoreEntities = qVectorEntityItemIDFromScriptValue(ignoreEntities); } - void setIncludeEntities(const QScriptValue& includeEntities) { _includeEntities = qVectorEntityItemIDFromScriptValue(includeEntities); } - void setIgnoreOverlays(const QScriptValue& ignoreOverlays) { _ignoreOverlays = qVectorOverlayIDFromScriptValue(ignoreOverlays); } - void setIncludeOverlays(const QScriptValue& includeOverlays) { _includeOverlays = qVectorOverlayIDFromScriptValue(includeOverlays); } - void setIgnoreAvatars(const QScriptValue& ignoreAvatars) { _ignoreAvatars = qVectorEntityItemIDFromScriptValue(ignoreAvatars); } - void setIncludeAvatars(const QScriptValue& includeAvatars) { _includeAvatars = qVectorEntityItemIDFromScriptValue(includeAvatars); } + void setIgnoreEntities(const QScriptValue& ignoreEntities); + void setIncludeEntities(const QScriptValue& includeEntities); + void setIgnoreOverlays(const QScriptValue& ignoreOverlays); + void setIncludeOverlays(const QScriptValue& includeOverlays); + void setIgnoreAvatars(const QScriptValue& ignoreAvatars); + void setIncludeAvatars(const QScriptValue& includeAvatars); + + QReadWriteLock* getLock() { return &_lock; } private: RayPickFilter _filter; @@ -139,6 +142,8 @@ private: QVector _includeOverlays; QVector _ignoreAvatars; QVector _includeAvatars; + + QReadWriteLock _lock; }; #endif // hifi_RayPick_h diff --git a/interface/src/raypick/RayPickManager.cpp b/interface/src/raypick/RayPickManager.cpp index bfc6e3fcb2..1728ecd01a 100644 --- a/interface/src/raypick/RayPickManager.cpp +++ b/interface/src/raypick/RayPickManager.cpp @@ -47,6 +47,7 @@ void RayPickManager::update() { RayPickCache results; for (auto& uid : _rayPicks.keys()) { std::shared_ptr rayPick = _rayPicks[uid]; + QWriteLocker lock(rayPick->getLock()); if (!rayPick->isEnabled() || rayPick->getFilter().doesPickNothing() || rayPick->getMaxDistance() < 0.0f) { continue; } @@ -114,7 +115,6 @@ void RayPickManager::update() { } } - QWriteLocker lock(_rayPickLocks[uid].get()); if (rayPick->getMaxDistance() == 0.0f || (rayPick->getMaxDistance() > 0.0f && res.distance < rayPick->getMaxDistance())) { rayPick->setRayPickResult(res); } else { @@ -127,7 +127,6 @@ QUuid RayPickManager::createRayPick(const std::string& jointName, const glm::vec QWriteLocker lock(&_containsLock); QUuid id = QUuid::createUuid(); _rayPicks[id] = std::make_shared(jointName, posOffset, dirOffset, filter, maxDistance, enabled); - _rayPickLocks[id] = std::make_shared(); return id; } @@ -135,7 +134,6 @@ QUuid RayPickManager::createRayPick(const RayPickFilter& filter, const float max QWriteLocker lock(&_containsLock); QUuid id = QUuid::createUuid(); _rayPicks[id] = std::make_shared(filter, maxDistance, enabled); - _rayPickLocks[id] = std::make_shared(); return id; } @@ -143,93 +141,91 @@ QUuid RayPickManager::createRayPick(const glm::vec3& position, const glm::vec3& QWriteLocker lock(&_containsLock); QUuid id = QUuid::createUuid(); _rayPicks[id] = std::make_shared(position, direction, filter, maxDistance, enabled); - _rayPickLocks[id] = std::make_shared(); return id; } void RayPickManager::removeRayPick(const QUuid uid) { QWriteLocker lock(&_containsLock); _rayPicks.remove(uid); - _rayPickLocks.remove(uid); } void RayPickManager::enableRayPick(const QUuid uid) { QReadLocker containsLock(&_containsLock); - if (_rayPicks.contains(uid)) { - QWriteLocker rayPickLock(_rayPickLocks[uid].get()); - _rayPicks[uid]->enable(); + auto rayPick = _rayPicks.find(uid); + if (rayPick != _rayPicks.end()) { + rayPick.value()->enable(); } } void RayPickManager::disableRayPick(const QUuid uid) { QReadLocker containsLock(&_containsLock); - if (_rayPicks.contains(uid)) { - QWriteLocker rayPickLock(_rayPickLocks[uid].get()); - _rayPicks[uid]->disable(); + auto rayPick = _rayPicks.find(uid); + if (rayPick != _rayPicks.end()) { + rayPick.value()->disable(); } } const RayPickResult RayPickManager::getPrevRayPickResult(const QUuid uid) { QReadLocker containsLock(&_containsLock); - if (_rayPicks.contains(uid)) { - QReadLocker lock(_rayPickLocks[uid].get()); - return _rayPicks[uid]->getPrevRayPickResult(); + auto rayPick = _rayPicks.find(uid); + if (rayPick != _rayPicks.end()) { + return rayPick.value()->getPrevRayPickResult(); } return RayPickResult(); } void RayPickManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) { QReadLocker containsLock(&_containsLock); - if (_rayPicks.contains(uid)) { - QWriteLocker lock(_rayPickLocks[uid].get()); - _rayPicks[uid]->setPrecisionPicking(precisionPicking); + auto rayPick = _rayPicks.find(uid); + if (rayPick != _rayPicks.end()) { + rayPick.value()->setPrecisionPicking(precisionPicking); } } void RayPickManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) { QReadLocker containsLock(&_containsLock); - if (_rayPicks.contains(uid)) { - QWriteLocker lock(_rayPickLocks[uid].get()); - _rayPicks[uid]->setIgnoreEntities(ignoreEntities); + auto rayPick = _rayPicks.find(uid); + if (rayPick != _rayPicks.end()) { + rayPick.value()->setIgnoreEntities(ignoreEntities); } } void RayPickManager::setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) { QReadLocker containsLock(&_containsLock); - if (_rayPicks.contains(uid)) { - QWriteLocker lock(_rayPickLocks[uid].get()); - _rayPicks[uid]->setIncludeEntities(includeEntities); + auto rayPick = _rayPicks.find(uid); + if (rayPick != _rayPicks.end()) { + rayPick.value()->setIncludeEntities(includeEntities); } } void RayPickManager::setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) { QReadLocker containsLock(&_containsLock); - if (_rayPicks.contains(uid)) { - QWriteLocker lock(_rayPickLocks[uid].get()); - _rayPicks[uid]->setIgnoreOverlays(ignoreOverlays); + auto rayPick = _rayPicks.find(uid); + if (rayPick != _rayPicks.end()) { + rayPick.value()->setIgnoreOverlays(ignoreOverlays); } } void RayPickManager::setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) { QReadLocker containsLock(&_containsLock); - if (_rayPicks.contains(uid)) { - QWriteLocker lock(_rayPickLocks[uid].get()); - _rayPicks[uid]->setIncludeOverlays(includeOverlays); + auto rayPick = _rayPicks.find(uid); + if (rayPick != _rayPicks.end()) { + rayPick.value()->setIncludeOverlays(includeOverlays); } } void RayPickManager::setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) { QReadLocker containsLock(&_containsLock); - if (_rayPicks.contains(uid)) { - QWriteLocker lock(_rayPickLocks[uid].get()); - _rayPicks[uid]->setIgnoreAvatars(ignoreAvatars); + auto rayPick = _rayPicks.find(uid); + if (rayPick != _rayPicks.end()) { + rayPick.value()->setIgnoreAvatars(ignoreAvatars); } } void RayPickManager::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) { QReadLocker containsLock(&_containsLock); - if (_rayPicks.contains(uid)) { - QWriteLocker lock(_rayPickLocks[uid].get()); - _rayPicks[uid]->setIncludeAvatars(includeAvatars); + auto rayPick = _rayPicks.find(uid); + if (rayPick != _rayPicks.end()) { + rayPick.value()->setIncludeAvatars(includeAvatars); } } \ No newline at end of file diff --git a/interface/src/raypick/RayPickManager.h b/interface/src/raypick/RayPickManager.h index 9717767f19..974022eb4d 100644 --- a/interface/src/raypick/RayPickManager.h +++ b/interface/src/raypick/RayPickManager.h @@ -15,7 +15,6 @@ #include #include -#include #include "RegisteredMetaTypes.h" @@ -47,7 +46,6 @@ public: private: QHash> _rayPicks; - QHash> _rayPickLocks; QReadWriteLock _containsLock; typedef QHash, std::unordered_map> RayPickCache; diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 714723e48e..4210e76097 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -265,7 +265,6 @@ void Base3DOverlay::parentDeleted() { } void Base3DOverlay::update(float duration) { - // In Base3DOverlay, if its location or bound changed, the renderTrasnformDirty flag is true. // then the correct transform used for rendering is computed in the update transaction and assigned. if (_renderTransformDirty) { diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 811e169faf..526890b9c1 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -40,6 +40,7 @@ #include "scripting/HMDScriptingInterface.h" #include "scripting/AssetMappingsScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" +#include "scripting/SettingsScriptingInterface.h" #include #include #include "FileDialogHelper.h" @@ -243,6 +244,7 @@ void Web3DOverlay::setupQmlSurface() { _webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); diff --git a/libraries/audio/src/AudioDynamics.h b/libraries/audio/src/AudioDynamics.h index 08daa21348..b7c168bfab 100644 --- a/libraries/audio/src/AudioDynamics.h +++ b/libraries/audio/src/AudioDynamics.h @@ -31,6 +31,9 @@ #define MULQ31(a,b) ((int32_t)(MUL64(a, b) >> 31)) #define MULDIV64(a,b,c) (int32_t)(MUL64(a, b) / (c)) +#define ADDMOD32(a,b) (int32_t)((uint32_t)(a) + (uint32_t)(b)) +#define SUBMOD32(a,b) (int32_t)((uint32_t)(a) - (uint32_t)(b)) + // // on x86 architecture, assume that SSE2 is present // @@ -394,19 +397,21 @@ public: // Fast FIR attack/lowpass filter using a 2-stage CIC filter. // The step response reaches final value after N-1 samples. + // NOTE: CIC integrators intentionally overflow, using modulo arithmetic. + // See E. B. Hogenauer, "An economical class of digital filters for decimation and interpolation" const int32_t CICGAIN = 0xffffffff / (CIC1 * CIC2); // Q32 x = MULHI(x, CICGAIN); _buffer[i] = _acc1; - _acc1 += x; // integrator + _acc1 = ADDMOD32(_acc1, x); // integrator i = (i + CIC1 - 1) & MASK; - x = _acc1 - _buffer[i]; // comb + x = SUBMOD32(_acc1, _buffer[i]); // comb _buffer[i] = _acc2; - _acc2 += x; // integrator + _acc2 = ADDMOD32(_acc2, x); // integrator i = (i + CIC2 - 1) & MASK; - x = _acc2 - _buffer[i]; // comb + x = SUBMOD32(_acc2, _buffer[i]); // comb _index = (i + 1) & MASK; // skip unused tap return x; @@ -459,19 +464,21 @@ public: // Fast FIR attack/lowpass filter using a 2-stage CIC filter. // The step response reaches final value after N-1 samples. + // NOTE: CIC integrators intentionally overflow, using modulo arithmetic. + // See E. B. Hogenauer, "An economical class of digital filters for decimation and interpolation" const int32_t CICGAIN = 0xffffffff / (CIC1 * CIC2); // Q32 x = MULHI(x, CICGAIN); _buffer[i] = _acc1; - _acc1 += x; // integrator + _acc1 = ADDMOD32(_acc1, x); // integrator i = (i + CIC1 - 1) & MASK; - x = _acc1 - _buffer[i]; // comb + x = SUBMOD32(_acc1, _buffer[i]); // comb _buffer[i] = _acc2; - _acc2 += x; // integrator + _acc2 = ADDMOD32(_acc2, x); // integrator i = (i + CIC2 - 1) & MASK; - x = _acc2 - _buffer[i]; // comb + x = SUBMOD32(_acc2, _buffer[i]); // comb _index = (i + 1) & MASK; // skip unused tap return x; diff --git a/libraries/audio/src/AudioGate.cpp b/libraries/audio/src/AudioGate.cpp index 5b2561da07..13c794b923 100644 --- a/libraries/audio/src/AudioGate.cpp +++ b/libraries/audio/src/AudioGate.cpp @@ -6,12 +6,12 @@ // Copyright 2017 High Fidelity, Inc. // -#include "AudioGate.h" - #include +#include #include -#include + #include "AudioDynamics.h" +#include "AudioGate.h" // log2 domain headroom bits above 0dB (int32_t) static const int LOG2_HEADROOM_Q30 = 1; @@ -418,7 +418,7 @@ void GateMono::process(int16_t* input, int16_t* output, int numFrames) { _dc.process(x); // peak detect - int32_t peak = std::abs(x); + int32_t peak = abs(x); // convert to log2 domain peak = fixlog2(peak); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 664ef64bde..89fe270af1 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -381,7 +381,7 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(QStringList jointNames READ getJointNames) - Q_PROPERTY(QUuid sessionUUID READ getSessionUUID) + Q_PROPERTY(QUuid sessionUUID READ getSessionUUID NOTIFY sessionUUIDChanged) Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix) Q_PROPERTY(glm::mat4 controllerLeftHandMatrix READ getControllerLeftHandMatrix) @@ -670,13 +670,19 @@ public: signals: void displayNameChanged(); void lookAtSnappingChanged(bool enabled); + void sessionUUIDChanged(); public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); void setJointMappingsFromNetworkReply(); - void setSessionUUID(const QUuid& sessionUUID) { setID(sessionUUID); } + void setSessionUUID(const QUuid& sessionUUID) { + if (sessionUUID != getID()) { + setID(sessionUUID); + emit sessionUUIDChanged(); + } + } virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; diff --git a/libraries/baking/src/Baker.h b/libraries/baking/src/Baker.h index 2da315c9fc..c1b2ddf959 100644 --- a/libraries/baking/src/Baker.h +++ b/libraries/baking/src/Baker.h @@ -18,6 +18,8 @@ class Baker : public QObject { Q_OBJECT public: + virtual ~Baker() = default; + bool shouldStop(); bool hasErrors() const { return !_errorList.isEmpty(); } diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 3ef291af22..017f11c680 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -56,6 +56,17 @@ FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGet } +FBXBaker::~FBXBaker() { + if (_tempDir.exists()) { + if (!_tempDir.remove(_originalFBXFilePath)) { + qCWarning(model_baking) << "Failed to remove temporary copy of fbx file:" << _originalFBXFilePath; + } + if (!_tempDir.rmdir(".")) { + qCWarning(model_baking) << "Failed to remove temporary directory:" << _tempDir; + } + } +} + void FBXBaker::abort() { Baker::abort(); diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index ad8284bfa8..a6034ee2b7 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -35,6 +35,7 @@ class FBXBaker : public Baker { public: FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, const QString& bakedOutputDir, const QString& originalOutputDir = ""); + ~FBXBaker() override; QUrl getFBXUrl() const { return _fbxURL; } QString getBakedFBXFilePath() const { return _bakedFBXFilePath; } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 0cb25a2e2f..67fcc5cc69 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -260,12 +260,24 @@ void EntityTreeRenderer::update(bool simulate) { } } - auto scene = _viewState->getMain3DScene(); - if (scene) { - render::Transaction transaction; - addPendingEntities(scene, transaction); - updateChangedEntities(scene, transaction); - scene->enqueueTransaction(transaction); + { + PerformanceTimer sceneTimer("scene"); + auto scene = _viewState->getMain3DScene(); + if (scene) { + render::Transaction transaction; + { + PerformanceTimer pt("add"); + addPendingEntities(scene, transaction); + } + { + PerformanceTimer pt("change"); + updateChangedEntities(scene, transaction); + } + { + PerformanceTimer pt("enqueue"); + scene->enqueueTransaction(transaction); + } + } } } } @@ -1078,4 +1090,4 @@ void EntityTreeRenderer::onEntityChanged(const EntityItemID& id) { _changedEntitiesGuard.withWriteLock([&] { _changedEntities.insert(id); }); -} \ No newline at end of file +} diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h index 4570ead9e1..96e9f4d222 100644 --- a/libraries/render-utils/src/AbstractViewStateInterface.h +++ b/libraries/render-utils/src/AbstractViewStateInterface.h @@ -31,9 +31,6 @@ public: /// copies the current view frustum for rendering the view state virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const = 0; - /// copies the shadow view frustum for rendering the view state - virtual void copyShadowViewFrustum(ViewFrustum& viewOut) const = 0; - virtual QThread* getMainThread() = 0; virtual PickRay computePickRay(float x, float y) const = 0; diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 4f7f9ef5c4..c22e99cbbc 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -144,6 +144,7 @@ void AnimDebugDraw::shutdown() { if (scene && _itemID) { render::Transaction transaction; transaction.removeItem(_itemID); + render::Item::clearID(_itemID); scene->enqueueTransaction(transaction); } } @@ -316,7 +317,9 @@ void AnimDebugDraw::update() { if (!scene) { return; } - + if (!render::Item::isValidID(_itemID)) { + return; + } render::Transaction transaction; transaction.updateItem(_itemID, [&](AnimDebugDrawData& data) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index f25cad8a6e..36f12fb1bf 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -241,7 +241,7 @@ void Model::updateRenderItems() { if (model && model->isLoaded()) { // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == model->_deleteGeometryCounter) { - + const Model::MeshState& state = model->getMeshState(data._meshIndex); Transform renderTransform = modelTransform; if (state.clusterMatrices.size() == 1) { diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index d2d3ad6de6..714be2a8c6 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -15,9 +15,6 @@ #include "Logging.h" #include "TransitionStage.h" -// Comment this to disable transitions (fades) -#define SCENE_ENABLE_TRANSITIONS - using namespace render; void Transaction::resetItem(ItemID id, const PayloadPointer& payload) { @@ -101,16 +98,46 @@ void consolidateTransaction(TransactionQueue& queue, Transaction& singleBatch) { queue.pop(); }; } - -void Scene::processTransactionQueue() { + +uint32_t Scene::enqueueFrame() { PROFILE_RANGE(render, __FUNCTION__); Transaction consolidatedTransaction; - { std::unique_lock lock(_transactionQueueMutex); consolidateTransaction(_transactionQueue, consolidatedTransaction); } - + + uint32_t frameNumber = 0; + { + std::unique_lock lock(_transactionFramesMutex); + _transactionFrames.push_back(consolidatedTransaction); + _transactionFrameNumber++; + frameNumber = _transactionFrameNumber; + } + + return frameNumber; +} + + +void Scene::processTransactionQueue() { + PROFILE_RANGE(render, __FUNCTION__); + + TransactionFrames queuedFrames; + { + // capture the queued frames and clear the queue + std::unique_lock lock(_transactionFramesMutex); + queuedFrames = _transactionFrames; + _transactionFrames.clear(); + } + + // go through the queue of frames and process them + for (auto& frame : queuedFrames) { + processTransactionFrame(frame); + } +} + +void Scene::processTransactionFrame(const Transaction& transaction) { + PROFILE_RANGE(render, __FUNCTION__); { std::unique_lock lock(_itemsMutex); // Here we should be able to check the value of last ItemID allocated @@ -123,32 +150,31 @@ void Scene::processTransactionQueue() { // capture anything coming from the transaction // resets and potential NEW items - resetItems(consolidatedTransaction._resetItems); + resetItems(transaction._resetItems); // Update the numItemsAtomic counter AFTER the reset changes went through _numAllocatedItems.exchange(maxID); // updates - updateItems(consolidatedTransaction._updatedItems); + updateItems(transaction._updatedItems); // removes - removeItems(consolidatedTransaction._removedItems); + removeItems(transaction._removedItems); -#ifdef SCENE_ENABLE_TRANSITIONS // add transitions - transitionItems(consolidatedTransaction._addedTransitions); - reApplyTransitions(consolidatedTransaction._reAppliedTransitions); - queryTransitionItems(consolidatedTransaction._queriedTransitions); -#endif + transitionItems(transaction._addedTransitions); + reApplyTransitions(transaction._reAppliedTransitions); + queryTransitionItems(transaction._queriedTransitions); + // Update the numItemsAtomic counter AFTER the pending changes went through _numAllocatedItems.exchange(maxID); } - if (consolidatedTransaction.touchTransactions()) { + if (transaction.touchTransactions()) { std::unique_lock lock(_selectionsMutex); // resets and potential NEW items - resetSelections(consolidatedTransaction._resetSelections); + resetSelections(transaction._resetSelections); } } diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 3b61a20f24..fef2077897 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -117,6 +117,9 @@ public: // Enqueue transaction to the scene void enqueueTransaction(const Transaction& transaction); + // Enqueue end of frame transactions boundary + uint32_t enqueueFrame(); + // Process the pending transactions queued void processTransactionQueue(); @@ -162,6 +165,15 @@ protected: std::mutex _transactionQueueMutex; TransactionQueue _transactionQueue; + + std::mutex _transactionFramesMutex; + using TransactionFrames = std::list; + TransactionFrames _transactionFrames; + uint32_t _transactionFrameNumber{ 0 }; + + // Process one transaction frame + void processTransactionFrame(const Transaction& transaction); + // The actual database // database of items is protected for editing by a mutex std::mutex _itemsMutex; diff --git a/libraries/shared/src/shared/FileLogger.cpp b/libraries/shared/src/shared/FileLogger.cpp index bea28b2b6f..8ceb378574 100644 --- a/libraries/shared/src/shared/FileLogger.cpp +++ b/libraries/shared/src/shared/FileLogger.cpp @@ -26,7 +26,6 @@ class FilePersistThread : public GenericQueueThread < QString > { Q_OBJECT public: FilePersistThread(const FileLogger& logger); - signals: void rollingLogFile(QString newFilename); @@ -42,12 +41,14 @@ private: -static const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt"; +static const QString FILENAME_FORMAT = "hifi-log_%1%2.txt"; static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; static const QString LOGS_DIRECTORY = "Logs"; static const QString IPADDR_WILDCARD = "[0-9]*.[0-9]*.[0-9]*.[0-9]*"; static const QString DATETIME_WILDCARD = "20[0-9][0-9]-[0,1][0-9]-[0-3][0-9]_[0-2][0-9].[0-6][0-9].[0-6][0-9]"; static const QString FILENAME_WILDCARD = "hifi-log_" + IPADDR_WILDCARD + "_" + DATETIME_WILDCARD + ".txt"; +static QUuid SESSION_ID; + // Max log size is 512 KB. We send log files to our crash reporter, so we want to keep this relatively // small so it doesn't go over the 2MB zipped limit for all of the files we send. static const qint64 MAX_LOG_SIZE = 512 * 1024; @@ -62,7 +63,13 @@ QString getLogRollerFilename() { QString result = FileUtils::standardPath(LOGS_DIRECTORY); QHostAddress clientAddress = getGuessedLocalAddress(); QDateTime now = QDateTime::currentDateTime(); - result.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT))); + QString fileSessionID; + + if (!SESSION_ID.isNull()) { + fileSessionID = "_" + SESSION_ID.toString().replace("{", "").replace("}", ""); + } + + result.append(QString(FILENAME_FORMAT).arg(now.toString(DATETIME_FORMAT), fileSessionID)); return result; } @@ -142,6 +149,12 @@ FileLogger::~FileLogger() { _persistThreadInstance->terminate(); } +void FileLogger::setSessionID(const QUuid& message) { + // This is for the output of log files. Once the application is first started, + // this function runs and grabs the AccountManager Session ID and saves it here. + SESSION_ID = message; + } + void FileLogger::addMessage(const QString& message) { _persistThreadInstance->queueItem(message); emit logReceived(message); diff --git a/libraries/shared/src/shared/FileLogger.h b/libraries/shared/src/shared/FileLogger.h index 15d211afe8..d9d7651147 100644 --- a/libraries/shared/src/shared/FileLogger.h +++ b/libraries/shared/src/shared/FileLogger.h @@ -26,6 +26,7 @@ public: QString getFilename() const { return _fileName; } virtual void addMessage(const QString&) override; + virtual void setSessionID(const QUuid&); virtual QString getLogData() override; virtual void locateLog() override; virtual void sync() override; diff --git a/scripts/system/controllers/controllerModules/mouseHMD.js b/scripts/system/controllers/controllerModules/mouseHMD.js index 9ccf4912a1..1d8aeee1f9 100644 --- a/scripts/system/controllers/controllerModules/mouseHMD.js +++ b/scripts/system/controllers/controllerModules/mouseHMD.js @@ -114,8 +114,14 @@ this.run = function(controllerData, deltaTime) { var now = Date.now(); - if (this.mouseActivity.expired(now) || this.triggersPressed(controllerData, now)) { - Reticle.visible = false; + var hmdActive = HMD.active; + if (this.mouseActivity.expired(now) || this.triggersPressed(controllerData, now) || !hmdActive) { + if (!hmdActive) { + Reticle.visible = true; + } else { + Reticle.visible = false; + } + return ControllerDispatcherUtils.makeRunningValues(false, [], []); } this.adjustReticleDepth(controllerData); diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 58eb4d16f9..c70a74cd7f 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -443,10 +443,6 @@ protected: viewOut = _viewFrustum; } - void copyShadowViewFrustum(ViewFrustum& viewOut) const override { - viewOut = _shadowViewFrustum; - } - QThread* getMainThread() override { return QThread::currentThread(); } @@ -1118,7 +1114,6 @@ private: RenderThread _renderThread; QWindowCamera _camera; ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. - ViewFrustum _shadowViewFrustum; // current state of view frustum, perspective, orientation, etc. model::SunSkyStage _sunSkyStage; model::LightPointer _globalLight { std::make_shared() }; bool _ready { false };