From f546524c1f92b7cad454726a8a751ba7bda9c8a0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 23 Sep 2016 09:46:10 -0700 Subject: [PATCH 01/46] don't checkout for user membership in a null list of groups --- domain-server/src/DomainGatekeeper.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index e33cbe1755..bf9d7d04a6 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -771,6 +771,21 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) { // loop through the groups mentioned on the settings page and ask if this user is in each. The replies // will be received asynchronously and permissions will be updated as the answers come in. + QJsonObject json; + QSet groupIDSet; + foreach (QUuid groupID, _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs()) { + groupIDSet += groupID.toString().mid(1,36); + } + + if (groupIDSet.isEmpty()) { + // if no groups are in the permissions settings, don't ask who is in which groups. + return; + } + + QJsonArray groupIDs = QJsonArray::fromStringList(groupIDSet.toList()); + json["groups"] = groupIDs; + + // if we've already asked, wait for the answer before asking again QString lowerUsername = username.toLower(); if (_inFlightGroupMembershipsRequests.contains(lowerUsername)) { @@ -779,13 +794,6 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) { } _inFlightGroupMembershipsRequests += lowerUsername; - QJsonObject json; - QSet groupIDSet; - foreach (QUuid groupID, _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs()) { - groupIDSet += groupID.toString().mid(1,36); - } - QJsonArray groupIDs = QJsonArray::fromStringList(groupIDSet.toList()); - json["groups"] = groupIDs; JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; From 4830b5c2a0d6e354fddbfc422cb299e704ea7ade Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 28 Sep 2016 19:17:24 -0700 Subject: [PATCH 02/46] Backup old domain. --- server-console/src/content-update.css | 62 ++++++++++++++++++++++++++ server-console/src/content-update.html | 22 +++++++++ server-console/src/content-update.js | 12 +++++ server-console/src/main.js | 61 +++++++++++++++---------- 4 files changed, 134 insertions(+), 23 deletions(-) create mode 100644 server-console/src/content-update.css create mode 100644 server-console/src/content-update.html create mode 100644 server-console/src/content-update.js diff --git a/server-console/src/content-update.css b/server-console/src/content-update.css new file mode 100644 index 0000000000..8c9a7c159b --- /dev/null +++ b/server-console/src/content-update.css @@ -0,0 +1,62 @@ +@font-face { + font-family: 'Raleway'; + src: url('vendor/Raleway/Raleway-Regular.ttf'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'Raleway'; + src: url('vendor/Raleway/Raleway-ExtraLight.ttf'); + font-weight: 200; + font-style: normal; +} +@font-face { + font-family: 'Raleway'; + src: url('vendor/Raleway/Raleway-SemiBold.ttf'); + font-weight: bold; + font-style: normal; +} + + +* { + font-family: "Raleway", "Open Sans", Arial, Helvetica, sans-serif; + line-height: 130%; +} + +body { + margin: 0; + padding: 0; + color: #808785; + margin: 0 auto; + text-align: center; + font-size: 13.5pt; + -webkit-touch-callout: none; -webkit-user-select: none; + cursor: default; + overflow: hidden; + font-variant-numeric: lining-nums; + -moz-font-feature-settings: "lnum"; + -webkit-font-feature-settings: "lnum"; + font-feature-settings: "lnum"; +} + +.selectable { + -webkit-touch-callout: text; + -webkit-user-select: text; + cursor: text; +} + +h1 { + font-size: 29pt; + font-weight: normal; +} + +a:link, +a:visited, +a:hover, +a:active { + color: #B4B4B4; +} + +a:hover { + color: #2D88A4; +} diff --git a/server-console/src/content-update.html b/server-console/src/content-update.html new file mode 100644 index 0000000000..7b3d1f343d --- /dev/null +++ b/server-console/src/content-update.html @@ -0,0 +1,22 @@ + + + + Server Backup + + + + +
+

We backed up your old server just in case.
If you wish to restore it, do the following:

+

+ 1) Stop your server.
+ 2) Go to the backup directory bellow.
+ 3) Move the content in the parent directory.
+ 4) Restart your server.
+
+ Backup Directory:
+ +

+
+ + diff --git a/server-console/src/content-update.js b/server-console/src/content-update.js new file mode 100644 index 0000000000..c77cfc92c6 --- /dev/null +++ b/server-console/src/content-update.js @@ -0,0 +1,12 @@ +function ready() { + console.log("Ready"); + + const electron = require('electron'); + window.$ = require('./vendor/jquery/jquery-2.1.4.min.js'); + + electron.ipcRenderer.on('update', function(event, message) { + $('#directory').html(message); + }); + + electron.ipcRenderer.send('ready'); +} diff --git a/server-console/src/main.js b/server-console/src/main.js index 82fe6b6b4d..2835dd338b 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -488,27 +488,51 @@ function updateTrayMenu(serverState) { const httpStatusPort = 60332; -function deleteResourceDirectories() { - const dsResourceDirectory = getDomainServerClientResourcesDirectory(); +function backupResourceDirectories(folder) { try { - fs.removeSync(dsResourceDirectory); - console.log("Deleted directory " + dsResourceDirectory); - } catch (e) { - console.log(e); - } - const acResourceDirectory = getAssignmentClientResourcesDirectory(); - try { - fs.removeSync(acResourceDirectory); - console.log("Deleted directory " + acResourceDirectory); + fs.mkdirSync(folder); + console.log("Created directory " + folder); + + + var dsBackup = path.join(folder, '/domain-server'); + fs.renameSync(getDomainServerClientResourcesDirectory(), dsBackup); + console.log("Moved directory " + getDomainServerClientResourcesDirectory()); + console.log("to " + dsBackup); + + var acBackup = path.join(folder, '/assignment-client'); + fs.renameSync(getAssignmentClientResourcesDirectory(), acBackup); + console.log("Moved directory " + getDomainServerClientResourcesDirectory()); + console.log("to " + acBackup); } catch (e) { console.log(e); } } -function deleteResourceDirectoriesAndRestart() { +function backupResourceDirectoriesAndRestart() { homeServer.stop(); - deleteResourceDirectories(); + + var date = new Date(); + var folder = getRootHifiDataDirectory() + "/Server Backup - " + date; + backupResourceDirectories(folder); maybeInstallDefaultContentSet(onContentLoaded); + + // Explain user how to restore server + var window = new BrowserWindow({ + icon: appIcon, + width: 500, + height: 350, + }); + window.loadURL('file://' + __dirname + '/content-update.html'); + if (!debug) { + window.setMenu(null); + } + window.show(); + + electron.ipcMain.on('ready', function() { + console.log("got ready"); + + window.webContents.send('update', folder); + }); } function checkNewContent() { @@ -537,16 +561,7 @@ function checkNewContent() { message: 'A newer version of the home content set is available.\nDo you wish to update?' }, function(idx) { if (idx === 0) { - dialog.showMessageBox({ - type: 'question', - buttons: ['Yes', 'No'], - title: 'Are you sure?', - message: 'This action will delete your current sandbox content.\nDo you wish to continue?' - }, function(idx) { - if (idx === 0 && homeServer) { - deleteResourceDirectoriesAndRestart(); - } - }); + backupResourceDirectoriesAndRestart(); } else { // They don't want to update, mark content set as current userConfig.set('homeContentLastModified', new Date()); From 468281f92dbeab9b427751dde29963fb7d728d6d Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 29 Sep 2016 06:58:14 -0700 Subject: [PATCH 03/46] Fix the Debug build of Interface on Windows --- cmake/macros/PackageLibrariesForDeployment.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index 7f826c1f8c..da0ee35769 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -49,6 +49,7 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) TARGET ${TARGET_NAME} POST_BUILD COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E remove ${QTAUDIO_PATH}/qtaudio_windows.dll && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.dll ${QTAUDIO_PATH} && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.pdb ${QTAUDIO_PATH} ) + COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windowsd.dll ( ${CMAKE_COMMAND} -E remove ${QTAUDIO_PATH}/qtaudio_windowsd.dll && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.dll ${QTAUDIO_PATH} && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.pdb ${QTAUDIO_PATH} ) ) endif () From 38eccd32a8d5f6ce25181e6bfc4be6e625ba9a16 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Sep 2016 10:18:54 -0700 Subject: [PATCH 04/46] add developer menu support to enable/disable dynamic texture management --- interface/src/Menu.cpp | 39 ++++++++++++++++++- interface/src/Menu.h | 2 + .../src/gpu/gl45/GL45BackendTexture.cpp | 33 +--------------- libraries/gpu/src/gpu/Texture.cpp | 31 +++++++++++++++ libraries/gpu/src/gpu/Texture.h | 9 +++++ 5 files changed, 82 insertions(+), 32 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3c1aa26a4a..74490b6dd1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -355,7 +355,7 @@ Menu::Menu() { //const QString = "1024 MB"; //const QString = "2048 MB"; - // Developer > Render > Resolution + // Developer > Render > Maximum Texture Memory MenuWrapper* textureMenu = renderOptionsMenu->addMenu(MenuOption::RenderMaxTextureMemory); QActionGroup* textureGroup = new QActionGroup(textureMenu); textureGroup->setExclusive(true); @@ -383,6 +383,43 @@ Menu::Menu() { gpu::Texture::setAllowedGPUMemoryUsage(newMaxTextureMemory); }); +#ifdef Q_OS_WIN + #define MIN_CORES_FOR_INCREMENTAL_TEXTURES 5 + bool recommendedIncrementalTransfers = (QThread::idealThreadCount() >= MIN_CORES_FOR_INCREMENTAL_TEXTURES); + bool recommendedSparseTextures = recommendedIncrementalTransfers; + + qDebug() << "[TEXTURE TRANSFER SUPPORT]" + << "\n\tidealThreadCount:" << QThread::idealThreadCount() + << "\n\tRECOMMENDED enableSparseTextures:" << recommendedSparseTextures + << "\n\tRECOMMENDED enableIncrementalTextures:" << recommendedIncrementalTransfers; + + gpu::Texture::setEnableIncrementalTextureTransfers(recommendedIncrementalTransfers); + gpu::Texture::setEnableSparseTextures(recommendedSparseTextures); + + // Developer > Render > Enable Dynamic Texture Management + { + auto action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableDynamicTextureManagement, 0, recommendedSparseTextures); + connect(action, &QAction::triggered, [&](bool checked) { + qDebug() << "[TEXTURE TRANSFER SUPPORT] --- Enable Dynamic Texture Management menu option:" << checked; + gpu::Texture::setEnableSparseTextures(checked); + }); + } + + // Developer > Render > Enable Incremental Texture Transfer + { + auto action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableIncrementalTextureTransfer, 0, recommendedIncrementalTransfers); + connect(action, &QAction::triggered, [&](bool checked) { + qDebug() << "[TEXTURE TRANSFER SUPPORT] --- Enable Incremental Texture Transfer menu option:" << checked; + gpu::Texture::setEnableIncrementalTextureTransfers(checked); + }); + } + +#else + qDebug() << "[TEXTURE TRANSFER SUPPORT] Incremental Texture Transfer and Dynamic Texture Management not supported on this platform."; +#endif + + + // Developer > Render > LOD Tools addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0, dialogsManager.data(), SLOT(lodTools())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b25603caeb..95cd4c5aa6 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -98,6 +98,8 @@ namespace MenuOption { const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoServerAudio = "Echo Server Audio"; const QString EnableCharacterController = "Enable avatar collisions"; + const QString EnableIncrementalTextureTransfer = "Enable Incremental Texture Transfer"; + const QString EnableDynamicTextureManagement = "Enable Dynamic Texture Management"; const QString EnableInverseKinematics = "Enable Inverse Kinematics"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarTiming = "Expand /myAvatar"; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index b9e6a92d8e..3e6e53ffc3 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -18,7 +18,6 @@ #include #include -#include #include "../gl/GLTexelFormat.h" @@ -26,34 +25,6 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -#ifdef Q_OS_WIN -#define MIN_CORES_FOR_INCREMENTAL_TEXTURES 5 -static const QString DEBUG_FLAG_INCREMENTAL("HIFI_DISABLE_INCREMENTAL_TEXTURES"); -static const QString DEBUG_FLAG_SPARSE("HIFI_DISABLE_SPARSE_TEXTURES"); - -static const bool enableIncrementalTextures = (QThread::idealThreadCount() >= MIN_CORES_FOR_INCREMENTAL_TEXTURES) && - !QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG_INCREMENTAL); - -static const bool enableSparseTextures = enableIncrementalTextures && - !QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG_SPARSE); - -class TextureTransferDebug { -public: - TextureTransferDebug() { - qDebug() << "[TEXTURE TRANSFER SUPPORT]" - << "\n\tHIFI_DISABLE_INCREMENTAL_TEXTURES:" << QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG_INCREMENTAL) - << "\n\tHIFI_DISABLE_SPARSE_TEXTURES:" << QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG_SPARSE) - << "\n\tidealThreadCount:" << QThread::idealThreadCount() - << "\n\tenableSparseTextures:" << enableSparseTextures - << "\n\tenableIncrementalTextures:" << enableSparseTextures; - } -}; -TextureTransferDebug sparseTextureDebugInfo; -#else -static bool enableSparseTextures = false; -static bool enableIncrementalTextures = false; -#endif - // Allocate 1 MB of buffer space for paged transfers #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) #define DEFAULT_GL_PIXEL_ALIGNMENT 4 @@ -275,7 +246,7 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) { - if (enableSparseTextures && _transferrable) { + if (_transferrable && Texture::getEnableSparseTextures()) { _sparseInfo.maybeMakeSparse(); } } @@ -374,7 +345,7 @@ void GL45Texture::startTransfer() { } bool GL45Texture::continueTransfer() { - if (!enableIncrementalTextures) { + if (!Texture::getEnableIncrementalTextureTransfers()) { size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1; for (uint8_t face = 0; face < maxFace; ++face) { for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) { diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index b573c8e899..44804abebe 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#include + #include "Texture.h" #include @@ -28,6 +31,34 @@ std::atomic Texture::_textureCPUCount{ 0 }; std::atomic Texture::_textureCPUMemoryUsage{ 0 }; std::atomic Texture::_allowedCPUMemoryUsage { 0 }; +std::atomic Texture::_enableSparseTextures { false }; +std::atomic Texture::_enableIncrementalTextureTransfers { false }; + +void Texture::setEnableSparseTextures(bool enabled) { +#ifdef Q_OS_WIN + qDebug() << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Sparse Textures and Dynamic Texture Management:" << enabled; + _enableSparseTextures = enabled; + if (!_enableIncrementalTextureTransfers && _enableSparseTextures) { + qDebug() << "[TEXTURE TRANSFER SUPPORT] WARNING - Sparse texture management requires incremental texture transfer enabled."; + } +#else + qDebug() << "[TEXTURE TRANSFER SUPPORT] Sparse Textures and Dynamic Texture Management not supported on this platform."; +#endif +} + +void Texture::setEnableIncrementalTextureTransfers(bool enabled) { +#ifdef Q_OS_WIN + qDebug() << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Incremental Texture Transfer:" << enabled; + _enableIncrementalTextureTransfers = enabled; + if (!_enableIncrementalTextureTransfers && _enableSparseTextures) { + qDebug() << "[TEXTURE TRANSFER SUPPORT] WARNING - Sparse texture management requires incremental texture transfer enabled."; + } +#else + qDebug() << "[TEXTURE TRANSFER SUPPORT] Incremental Texture Transfer not supported on this platform."; +#endif +} + + void Texture::updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { if (prevObjectSize == newObjectSize) { return; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index ae1afcafcb..61d03c070c 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -144,6 +144,9 @@ class Texture : public Resource { static std::atomic _textureCPUMemoryUsage; static std::atomic _allowedCPUMemoryUsage; static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); + + static std::atomic _enableSparseTextures; + static std::atomic _enableIncrementalTextureTransfers; public: static uint32_t getTextureCPUCount(); static Size getTextureCPUMemoryUsage(); @@ -154,6 +157,12 @@ public: static Size getAllowedGPUMemoryUsage(); static void setAllowedGPUMemoryUsage(Size size); + static bool getEnableSparseTextures() { return _enableSparseTextures.load(); } + static bool getEnableIncrementalTextureTransfers() { return _enableIncrementalTextureTransfers.load(); } + + static void setEnableSparseTextures(bool enabled); + static void setEnableIncrementalTextureTransfers(bool enabled); + class Usage { public: enum FlagBit { From 701c710857794745300bd371a44d78da358c2864 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Sep 2016 12:15:48 -0700 Subject: [PATCH 05/46] Fix windows odd folder name issue --- server-console/src/main.js | 48 ++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 2835dd338b..b442d28855 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -503,36 +503,44 @@ function backupResourceDirectories(folder) { fs.renameSync(getAssignmentClientResourcesDirectory(), acBackup); console.log("Moved directory " + getDomainServerClientResourcesDirectory()); console.log("to " + acBackup); + return true; } catch (e) { console.log(e); + return false; } } function backupResourceDirectoriesAndRestart() { homeServer.stop(); - var date = new Date(); - var folder = getRootHifiDataDirectory() + "/Server Backup - " + date; - backupResourceDirectories(folder); - maybeInstallDefaultContentSet(onContentLoaded); + var folder = getRootHifiDataDirectory() + "/Server Backup - " + Date.now(); + if (backupResourceDirectories(folder)) { + maybeInstallDefaultContentSet(onContentLoaded); - // Explain user how to restore server - var window = new BrowserWindow({ - icon: appIcon, - width: 500, - height: 350, - }); - window.loadURL('file://' + __dirname + '/content-update.html'); - if (!debug) { - window.setMenu(null); + // Explain user how to restore server + var window = new BrowserWindow({ + icon: appIcon, + width: 500, + height: 350, + }); + window.loadURL('file://' + __dirname + '/content-update.html'); + if (!debug) { + window.setMenu(null); + } + window.show(); + + electron.ipcMain.on('ready', function() { + console.log("got ready"); + window.webContents.send('update', folder); + }); + } else { + dialog.showMessageBox({ + type: 'warning', + buttons: ['Ok'], + title: 'Update Error', + message: 'There was an error updating the content, aborting.' + }, function() {}); } - window.show(); - - electron.ipcMain.on('ready', function() { - console.log("got ready"); - - window.webContents.send('update', folder); - }); } function checkNewContent() { From 858297241b47fffa256c809ac523999d8725381a Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 29 Sep 2016 12:48:21 -0700 Subject: [PATCH 06/46] Even lower audio latency --- libraries/audio-client/src/AudioClient.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 97b5c14512..4ae19c55d4 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -87,7 +87,7 @@ public: AudioOutputIODevice(MixedProcessedAudioStream& receivedAudioStream, AudioClient* audio) : _receivedAudioStream(receivedAudioStream), _audio(audio), _unfulfilledReads(0) {}; - void start() { open(QIODevice::ReadOnly); } + void start() { open(QIODevice::ReadOnly | QIODevice::Unbuffered); } void stop() { close(); } qint64 readData(char * data, qint64 maxSize) override; qint64 writeData(const char * data, qint64 maxSize) override { return 0; } From f42c0dbd7c51a18ddd3e7ad43b3319e156c683ad Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 29 Sep 2016 13:01:15 -0700 Subject: [PATCH 07/46] Fix cmake checksum, for updated qtaudio_wasapi.zip with release and debug plugins --- cmake/externals/wasapi/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 019920df77..1cb63531b8 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -7,7 +7,7 @@ if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi.zip - URL_MD5 11c8a7728d6eda7223df800e10b70723 + URL_MD5 272b27bd6c211c45c0c23d4701b63b5e CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From 3d14a32d1755d6c4ce542508fc646846c116d66d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 29 Sep 2016 22:11:12 +0200 Subject: [PATCH 08/46] fix controller::ScriptingInterface not found warning in AC --- interface/src/Application.cpp | 5 +++++ libraries/script-engine/src/ScriptEngine.cpp | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8b8d1e531b..3890eae4c3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -68,6 +68,7 @@ #include #include #include +#include #include #include #include @@ -4918,6 +4919,10 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Users", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine)); + + auto scriptingInterface = DependencyManager::get(); + scriptEngine->registerGlobalObject("Controller", scriptingInterface.data()); + UserInputMapper::registerControllerTypes(scriptEngine); } bool Application::canAcceptURL(const QString& urlString) const { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0ac2883cb5..e4d1cd7307 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -517,10 +517,6 @@ void ScriptEngine::init() { // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); - auto scriptingInterface = DependencyManager::get(); - registerGlobalObject("Controller", scriptingInterface.data()); - UserInputMapper::registerControllerTypes(this); - auto recordingInterface = DependencyManager::get(); registerGlobalObject("Recording", recordingInterface.data()); From 7e3073e163d5efe592886946ae01179e6bca23de Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 29 Sep 2016 22:13:27 +0200 Subject: [PATCH 09/46] fix spelling bugs --- assignment-client/src/AssignmentClient.cpp | 36 +++++++++---------- .../src/AssignmentClientLogging.cpp | 2 +- .../src/AssignmentClientLogging.h | 2 +- domain-server/src/DomainServer.cpp | 2 +- libraries/script-engine/src/ScriptEngine.h | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 4fc8975262..800f00b352 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -91,7 +91,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri // check for a wallet UUID on the command line or in the config // this would represent where the user running AC wants funds sent to if (!walletUUID.isNull()) { - qCDebug(assigmnentclient) << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); + qCDebug(assignment_client) << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); _requestAssignment.setWalletUUID(walletUUID); } @@ -102,16 +102,16 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri } _assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true); - _assignmentServerSocket.setObjectName("AssigmentServer"); + _assignmentServerSocket.setObjectName("AssignmentServer"); nodeList->setAssignmentServerSocket(_assignmentServerSocket); - qCDebug(assigmnentclient) << "Assignment server socket is" << _assignmentServerSocket; + qCDebug(assignment_client) << "Assignment server socket is" << _assignmentServerSocket; // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required - qCDebug(assigmnentclient) << "Waiting for assignment -" << _requestAssignment; + qCDebug(assignment_client) << "Waiting for assignment -" << _requestAssignment; if (_assignmentServerHostname != "localhost") { - qCDebug(assigmnentclient) << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort(); + qCDebug(assignment_client) << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort(); } connect(&_requestTimer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); @@ -129,7 +129,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri _assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort); _assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor"); - qCDebug(assigmnentclient) << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket; + qCDebug(assignment_client) << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket; // Hook up a timer to send this child's status to the Monitor once per second setUpStatusToMonitor(); @@ -140,7 +140,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri } void AssignmentClient::stopAssignmentClient() { - qCDebug(assigmnentclient) << "Forced stop of assignment-client."; + qCDebug(assignment_client) << "Forced stop of assignment-client."; _requestTimer.stop(); _statsTimerACM.stop(); @@ -218,14 +218,14 @@ void AssignmentClient::sendAssignmentRequest() { quint16 localAssignmentServerPort; if (nodeList->getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, localAssignmentServerPort)) { if (localAssignmentServerPort != _assignmentServerSocket.getPort()) { - qCDebug(assigmnentclient) << "Port for local assignment server read from shared memory is" + qCDebug(assignment_client) << "Port for local assignment server read from shared memory is" << localAssignmentServerPort; _assignmentServerSocket.setPort(localAssignmentServerPort); nodeList->setAssignmentServerSocket(_assignmentServerSocket); } } else { - qCWarning(assigmnentclient) << "Failed to read local assignment server port from shared memory" + qCWarning(assignment_client) << "Failed to read local assignment server port from shared memory" << "- will send assignment request to previous assignment server socket."; } } @@ -235,10 +235,10 @@ void AssignmentClient::sendAssignmentRequest() { } void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer message) { - qCDebug(assigmnentclient) << "Received a PacketType::CreateAssignment - attempting to unpack."; + qCDebug(assignment_client) << "Received a PacketType::CreateAssignment - attempting to unpack."; if (_currentAssignment) { - qCWarning(assigmnentclient) << "Received a PacketType::CreateAssignment while still running an active assignment. Ignoring."; + qCWarning(assignment_client) << "Received a PacketType::CreateAssignment while still running an active assignment. Ignoring."; return; } @@ -246,7 +246,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer(); @@ -256,7 +256,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointergetDomainHandler().setSockAddr(message->getSenderSockAddr(), _assignmentServerHostname); nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID()); - qCDebug(assigmnentclient) << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString(); + qCDebug(assignment_client) << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString(); // start the deployed assignment QThread* workerThread = new QThread; @@ -284,7 +284,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointerstarted() workerThread->start(); } else { - qCWarning(assigmnentclient) << "Received an assignment that could not be unpacked. Re-requesting."; + qCWarning(assignment_client) << "Received an assignment that could not be unpacked. Re-requesting."; } } @@ -294,10 +294,10 @@ void AssignmentClient::handleStopNodePacket(QSharedPointer mess if (senderSockAddr.getAddress() == QHostAddress::LocalHost || senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) { - qCDebug(assigmnentclient) << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketType::StopNode."; + qCDebug(assignment_client) << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketType::StopNode."; QCoreApplication::quit(); } else { - qCWarning(assigmnentclient) << "Got a stop packet from other than localhost."; + qCWarning(assignment_client) << "Got a stop packet from other than localhost."; } } @@ -317,7 +317,7 @@ void AssignmentClient::handleAuthenticationRequest() { // ask the account manager to log us in from the env variables accountManager->requestAccessToken(username, password); } else { - qCWarning(assigmnentclient) << "Authentication was requested against" << qPrintable(accountManager->getAuthURL().toString()) + qCWarning(assignment_client) << "Authentication was requested against" << qPrintable(accountManager->getAuthURL().toString()) << "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV) << "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate."; @@ -335,7 +335,7 @@ void AssignmentClient::assignmentCompleted() { // reset the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - qCDebug(assigmnentclient) << "Assignment finished or never started - waiting for new assignment."; + qCDebug(assignment_client) << "Assignment finished or never started - waiting for new assignment."; auto nodeList = DependencyManager::get(); diff --git a/assignment-client/src/AssignmentClientLogging.cpp b/assignment-client/src/AssignmentClientLogging.cpp index 890187ecaa..9110dccc5f 100644 --- a/assignment-client/src/AssignmentClientLogging.cpp +++ b/assignment-client/src/AssignmentClientLogging.cpp @@ -11,4 +11,4 @@ #include "AssignmentClientLogging.h" -Q_LOGGING_CATEGORY(assigmnentclient, "hifi.assignment-client") \ No newline at end of file +Q_LOGGING_CATEGORY(assignment_client, "hifi.assignment-client") \ No newline at end of file diff --git a/assignment-client/src/AssignmentClientLogging.h b/assignment-client/src/AssignmentClientLogging.h index d6b5ee90e0..88e2add017 100644 --- a/assignment-client/src/AssignmentClientLogging.h +++ b/assignment-client/src/AssignmentClientLogging.h @@ -14,6 +14,6 @@ #include -Q_DECLARE_LOGGING_CATEGORY(assigmnentclient) +Q_DECLARE_LOGGING_CATEGORY(assignment_client) #endif // hifi_AssignmentClientLogging_h \ No newline at end of file diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index c1caa00d24..d7bcaa838e 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1749,7 +1749,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url _ephemeralACScripts[scriptAssignment->getUUID()] = formData[0].second; - // add the script assigment to the assignment queue + // add the script assignment to the assignment queue SharedAssignmentPointer sharedScriptedAssignment(scriptAssignment); _unfulfilledAssignments.enqueue(sharedScriptedAssignment); _allAssignments.insert(sharedScriptedAssignment->getUUID(), sharedScriptedAssignment); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index dd93dc2e03..62db99a431 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -166,7 +166,7 @@ public: void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; } bool isUserLoaded() const { return _isUserLoaded; } - // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety + // NOTE - this is used by the TypedArray implementation. we need to review this for thread safety ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } void setEmitScriptUpdatesFunction(std::function func) { _emitScriptUpdates = func; } From 5025b4a5ed51ea47d9e72b5f3d995b58257bf3d4 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 29 Sep 2016 22:15:53 +0200 Subject: [PATCH 10/46] NodeList set the sessionUUID before emitting the connectedToDomain signal --- libraries/networking/src/NodeList.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 593a79b311..f3934a51f9 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -536,8 +536,13 @@ void NodeList::processDomainServerList(QSharedPointer message) QUuid domainUUID; packetStream >> domainUUID; + // pull our owner UUID from the packet, it's always the first thing + QUuid newUUID; + packetStream >> newUUID; + // if this was the first domain-server list from this domain, we've now connected if (!_domainHandler.isConnected()) { + setSessionUUID(newUUID); _domainHandler.setUUID(domainUUID); _domainHandler.setIsConnected(true); @@ -548,13 +553,10 @@ void NodeList::processDomainServerList(QSharedPointer message) // Recieved packet from different domain. qWarning() << "IGNORING DomainList packet from" << domainUUID << "while connected to" << _domainHandler.getUUID(); return; + } else { + setSessionUUID(newUUID); } - // pull our owner UUID from the packet, it's always the first thing - QUuid newUUID; - packetStream >> newUUID; - setSessionUUID(newUUID); - // pull the permissions/right/privileges for this node out of the stream NodePermissions newPermissions; packetStream >> newPermissions; From d925a22fd4769d16ac00cef169760f3a5cefb9cf Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 29 Sep 2016 14:09:38 -0700 Subject: [PATCH 11/46] Use new filename for the external as a simple form of versioning --- cmake/externals/wasapi/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 1cb63531b8..8ec327600f 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -6,7 +6,7 @@ if (WIN32) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi.zip + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi2.zip URL_MD5 272b27bd6c211c45c0c23d4701b63b5e CONFIGURE_COMMAND "" BUILD_COMMAND "" From 768354e49f915cd29bfa0c4a698b7840099778f9 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 29 Sep 2016 12:06:09 -0700 Subject: [PATCH 12/46] loopback local injectors on server echo --- libraries/audio-client/src/AudioClient.h | 3 ++- libraries/audio/src/AbstractAudioInterface.h | 1 + libraries/audio/src/AudioInjector.cpp | 12 ++++++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 97b5c14512..eeca9fbd02 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -167,7 +167,8 @@ public slots: int setOutputBufferSize(int numFrames, bool persist = true); - virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector) override; + bool outputLocalInjector(bool isStereo, AudioInjector* injector) override; + bool shouldLoopbackInjectors() override { return _shouldEchoToServer; } bool switchInputToAudioDevice(const QString& inputDeviceName); bool switchOutputToAudioDevice(const QString& outputDeviceName); diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index 223421a7ab..ec96462e73 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -33,6 +33,7 @@ public: public slots: virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector) = 0; + virtual bool shouldLoopbackInjectors() { return false; } virtual void setIsStereoInput(bool stereo) = 0; }; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 43701a51d8..6f6534e2d2 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -211,6 +211,7 @@ int64_t AudioInjector::injectNextFrame() { } // if we haven't setup the packet to send then do so now + static int loopbackOptionOffset = -1; static int positionOptionOffset = -1; static int volumeOptionOffset = -1; static int audioDataOffset = -1; @@ -260,10 +261,9 @@ int64_t AudioInjector::injectNextFrame() { // pack the stereo/mono type of the stream audioPacketStream << _options.stereo; - // pack the flag for loopback. Now, we don't loopback - // and _always_ play locally, so loopbackFlag should be - // false always. - uchar loopbackFlag = (uchar)false; + // pack the flag for loopback, if requested + loopbackOptionOffset = _currentPacket->pos(); + uchar loopbackFlag = (_localAudioInterface && _localAudioInterface->shouldLoopbackInjectors()); audioPacketStream << loopbackFlag; // pack the position for injected audio @@ -293,6 +293,7 @@ int64_t AudioInjector::injectNextFrame() { return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } } + if (!_frameTimer->isValid()) { // in the case where we have been restarted, the frame timer will be invalid and we need to start it back over here _frameTimer->restart(); @@ -317,6 +318,9 @@ int64_t AudioInjector::injectNextFrame() { // pack the sequence number _currentPacket->writePrimitive(_outgoingSequenceNumber); + _currentPacket->seek(loopbackOptionOffset); + _currentPacket->writePrimitive((uchar)(_localAudioInterface && _localAudioInterface->shouldLoopbackInjectors())); + _currentPacket->seek(positionOptionOffset); _currentPacket->writePrimitive(_options.position); _currentPacket->writePrimitive(_options.orientation); From 43c40aa09017c71e289280707cb311886087c446 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 29 Sep 2016 15:22:21 -0700 Subject: [PATCH 13/46] avoid repetition with fade on injectors --- assignment-client/src/audio/AudioMixer.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 2d2f9c267e..ccd80a4a11 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -269,19 +269,17 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& bool forceSilentBlock = true; if (!streamToAdd.getLastPopOutput().isNull()) { + bool isInjector = dynamic_cast(&streamToAdd); - // reptition with fade is enabled, and we do have a valid previous frame to repeat - // so we mix the previously-mixed block - - // this is preferable to not mixing it at all to avoid the harsh jump to silence + // in an injector, just go silent - the injector has likely ended + // in other inputs (microphone, &c.), repeat with fade to avoid the harsh jump to silence // we'll repeat the last block until it has a block to mix // and we'll gradually fade that repeated block into silence. // calculate its fade factor, which depends on how many times it's already been repeated. - repeatedFrameFadeFactor = calculateRepeatedFrameFadeFactor(streamToAdd.getConsecutiveNotMixedCount() - 1); - if (repeatedFrameFadeFactor > 0.0f) { + if (!isInjector && repeatedFrameFadeFactor > 0.0f) { // apply the repeatedFrameFadeFactor to the gain gain *= repeatedFrameFadeFactor; From 165e609851b1561a8c838e63524a0570484f9825 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Sep 2016 15:42:25 -0700 Subject: [PATCH 14/46] Update instructions UI --- server-console/src/content-update.css | 122 +++++++++++++++++++++++-- server-console/src/content-update.html | 71 ++++++++++---- server-console/src/images/step1.jpg | Bin 0 -> 10485 bytes server-console/src/images/step2.jpg | Bin 0 -> 3403 bytes server-console/src/images/step3.jpg | Bin 0 -> 6329 bytes server-console/src/images/step4.jpg | Bin 0 -> 10065 bytes server-console/src/main.js | 45 +++++---- 7 files changed, 197 insertions(+), 41 deletions(-) create mode 100644 server-console/src/images/step1.jpg create mode 100644 server-console/src/images/step2.jpg create mode 100644 server-console/src/images/step3.jpg create mode 100644 server-console/src/images/step4.jpg diff --git a/server-console/src/content-update.css b/server-console/src/content-update.css index 8c9a7c159b..ebfd8aeba0 100644 --- a/server-console/src/content-update.css +++ b/server-console/src/content-update.css @@ -28,7 +28,7 @@ body { padding: 0; color: #808785; margin: 0 auto; - text-align: center; + text-align: left; font-size: 13.5pt; -webkit-touch-callout: none; -webkit-user-select: none; cursor: default; @@ -45,11 +45,6 @@ body { cursor: text; } -h1 { - font-size: 29pt; - font-weight: normal; -} - a:link, a:visited, a:hover, @@ -60,3 +55,118 @@ a:active { a:hover { color: #2D88A4; } + +.header{ + width: 95%; + left: 2.5% +} +.colmask{ + width: 95%; + left: 2.5% +} +.colmid{ right: 25% } +.colin{ right: 25% } +.colleft{ right: 25% } +.col1{ + width: 23%; + left: 101% +} +.col2{ + width: 23%; + left: 53% +} +.col3{ + width: 23%; + left: 80% +} +.col4{ + width: 23%; + left: 82% +} +.footer{ + width: 95%; + left: 2.5% +} +.header{ + clear: both; + float: left; + position: relative; + border-bottom: #000 1px solid; + background-color: #b4d2f7 +} +.colmask{ + clear: both; + float: left; + overflow: hidden; + position: relative; + +} +.colmid{ + float: left; + width: 100%; + position: relative; + +} +.colin{ + float: left; + width: 100%; + position: relative; + +} +.colleft{ + float: left; + width: 100%; + position: relative; + +} +.col1{ + padding: 0px 0px 1em 0px; + overflow: hidden; + float: left; + position: relative; + +} +.col2{ + padding: 0px 0px 1em 0px; + overflow: hidden; + float: left; + position: relative; + +} +.col3{ + padding: 0px 0px 1em 0px; + overflow: hidden; + float: left; + position: relative; + +} +.col4{ + padding: 0px 0px 1em 0px; + overflow: hidden; + float: left; + position: relative; + +} +.footer{ + clear: both; + float: left; + position: relative; + padding-top: 8px; + border-top: #000 1px solid; + +} +.bottom{ + clear: both; + width: 100%; + float: left; + position: relative; + +} +body { + border-width: 0px; + padding: 0px; + margin: 0px; + font-size: 90%; + width: 100%; + min-width: 600px; +} diff --git a/server-console/src/content-update.html b/server-console/src/content-update.html index 7b3d1f343d..3c0eff2fa0 100644 --- a/server-console/src/content-update.html +++ b/server-console/src/content-update.html @@ -1,22 +1,57 @@ - - Server Backup - - - - -
-

We backed up your old server just in case.
If you wish to restore it, do the following:

-

- 1) Stop your server.
- 2) Go to the backup directory bellow.
- 3) Move the content in the parent directory.
- 4) Restart your server.
-
- Backup Directory:
- -

+ + Server Backup + + + + +
+

We backed up your old Sandbox content, just in case.

+

To restore it, follow these steps: + +

+
+ +
+ +
+ + Step 1 +

1. Stop your Sandbox server. + +

+ +
+ + Step 2 +

2. Go to your backup directory: + + +

+ +
+ + Step 3 +

3. Copy the backed up content and paste it into the parent directory. +

+ +
+ + Step 4 +

4. Restart your Sandbox server. +

+ +
+ +
+
- + + +
+ diff --git a/server-console/src/images/step1.jpg b/server-console/src/images/step1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cd80ae553782fb968216d5b0dc5494cc0fa63485 GIT binary patch literal 10485 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 z�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 literal 0 HcmV?d00001 diff --git a/server-console/src/images/step2.jpg b/server-console/src/images/step2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6bc285ac472f867528f5ecc1b4e6f77be574e32d GIT binary patch literal 3403 zcmcgsc|6p4AOFqec<7LdZ9As1>15`I!3<+6L#b$+8F#5M2ZK4xjLZn=ihb@2xFb_pPphF6x`%3@_00#ho0sx9`80ae!@$qOhS7=OQ@VuGEbRGvSrt#6B zF$N7-6UBTQ-JdB!dNY04TmovS^gas7W)M)WSVxQ_-UN10tSnKnjKVd zaIkT(sWDIBg9dRp92#SSHZd_0Fjz)H30FiD8*zmOa|$G;kS<{JMQk1ysZ^wS^MXVK z6l!h=9R3&CuX^>jKu5=aAIjl;i57~;%=vo1DlDW*_)IjJDdYtS=u9$`WuQ#O$J+{+ zG!aigRdbC*lcxd3>a;t;#jzCPx~b%~dvTR5Vo%v3jovE$4g)PR;#A2r<#%zvD;?aXROs;5-%b4fdF343b z2#zu{i78+oWispqJPuMda6EhAT5t>M&2kwF*Ro?lF4_iZjaE+Y-==%6iA=IretucJ zHpuzq&*U-)C;^ISqZkAhtClRl_zeI6h@u_%8Gu3{5GVu+g+gJ<8wOK8;4mfN2)GIe zgqoVVni@hwLsL^jLt9HrOM4zlDjW{iKp-@&L)devY*Xc};O4I4JI)?`>>*}px|gH%^5;*zH{A6(;~M@t&ZqKHhP`HFVqev$ zZF5=R{U#UvByT(>jDNRbV8}nFx;kvQu%taay^nq1MTJIEYW~f3?9&iu+bMmb{Ze(IlhTpFD zBV1CxWmwA062H~c(^VJU-N)lWOM|Yp3czQ7Qxm^d&%@JW+)7&WjRJT)SzfL?JU#^; zqad(UGPq*(nKOZGox-Su?N73QM*RJ{qVirpc*FL{;>p}4KfcL28|}Tj1q_R%U#KPq zwRQ%~e2(Z`)&A~YdsrN+?(UOcjocYIU0DCMlklUs};qp2;{eZRTrO5rD4UcO7d{`%pZ-v~_0(23zdl@-xVbsxvXaK+D@hxz zhE}ybrIgf8)b-z7okzQo@g_{}>1os%>h|%W0+1Z~Jn8HVA1*q1^Ma*Z(&BR4Q>{(f zR+R%jJ+PIPgj=JV=@%HDgI$%?ZkOs(S1_}$C~bQ z!>&LMww~R2UdzWUtn%fo|^JKTO)2?M@%F!$Q4!+(iwse!ukO;j?!ZLlnY1+0i zZtWVcW}&Y~iC$o~+uiTG$^Lxh!FB3`-m>Em-r(sYqoO`K1u@sw*(a@)$m{=t3H#B(bQ@B9D&fPTdKz)efnjwyhs=1~EQ!W2wALRTdJ$^Lk@ zDsSAmw=OGw!vSTW3XWD8!L22{xQuxVF3zYd@GLF7XS$4KNlofhM$)EENUv-UZJ9Ck ztbds3826}aTg7ML)yzN8i|@3$UfPo87H2=J2|&ySW4c&<6)xu|5(wj$lYOYSQz##U zLfxNSJGx1`-*JiVQ3E#c-vf{;?-cBha z`zB%>o|hf1Z;G0dysI{hjGdNUlixX)8!%%w_D3w~;g3$LUmy8A)Awynt^DZ%LS5T@ zXasRay~&Z-Z1F&ao2)YA{obe+QB>r?tQl<(mmRmC+SZ-=Ns?3JjhPHIT-0FI|I(ta z0X3pt(O4MuHg;lxiRy@huICG+4^!KbO();nzfxxtstefnFoWuvm{tD zbnSgd68a*^hpxO0Z)Ee-3r=2rZ+~_;vp#xCzq6W>{$}eV_gJU1=SM&EKJRhO7{MEG zQm``~iysT)dh#A-l12yLDBFFbi^VOUe62v4Yy2I<%esxFsgyXcm^AZN`Qe@`{k1qR zkB%hKey71OC+86R)9pUd5%E72uI`ce3)uApt9xTO=e||{cjO-g@A=tZ1P4 znYG=(?Heg+OhIMSosQ$l=wBMkYad&nV48iM^4@P_AU}@|1&%EG{fEks;JcqrzTQqF zSalq$5+E@tmj`WV_m=AKcbZs-={T*uF*Ed+1TINUcUC})V>mQnCP=@qayaz7zE`cw e*p_oAvZA~&xOII{&H!Lw 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 literal 0 HcmV?d00001 diff --git a/server-console/src/images/step4.jpg b/server-console/src/images/step4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..27694aee7437ea9acfabfab87112f58bb46910a0 GIT binary patch literal 10065 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 literal 0 HcmV?d00001 diff --git a/server-console/src/main.js b/server-console/src/main.js index b442d28855..ea8ba6337e 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -408,6 +408,13 @@ var labels = { logWindow.open(); } }, + restoreBackup: { + label: 'Restore Backup Instructions', + click: function() { + var folder = getRootHifiDataDirectory() + "/Server Backup"; + openBackupInstructions(folder); + } + }, share: { label: 'Share', click: function() { @@ -443,6 +450,7 @@ function buildMenuArray(serverState) { menuArray.push(labels.stopServer); menuArray.push(labels.settings); menuArray.push(labels.viewLogs); + menuArray.push(labels.restoreBackup); menuArray.push(separator); menuArray.push(labels.share); menuArray.push(separator); @@ -510,29 +518,32 @@ function backupResourceDirectories(folder) { } } +function openBackupInstructions(folder) { + // Explain user how to restore server + var window = new BrowserWindow({ + icon: appIcon, + width: 800, + height: 520, + }); + window.loadURL('file://' + __dirname + '/content-update.html'); + if (!debug) { + window.setMenu(null); + } + window.show(); + + electron.ipcMain.on('ready', function() { + console.log("got ready"); + window.webContents.send('update', folder); + }); + +} function backupResourceDirectoriesAndRestart() { homeServer.stop(); var folder = getRootHifiDataDirectory() + "/Server Backup - " + Date.now(); if (backupResourceDirectories(folder)) { maybeInstallDefaultContentSet(onContentLoaded); - - // Explain user how to restore server - var window = new BrowserWindow({ - icon: appIcon, - width: 500, - height: 350, - }); - window.loadURL('file://' + __dirname + '/content-update.html'); - if (!debug) { - window.setMenu(null); - } - window.show(); - - electron.ipcMain.on('ready', function() { - console.log("got ready"); - window.webContents.send('update', folder); - }); + openBackupInstructions(folder); } else { dialog.showMessageBox({ type: 'warning', From 64ecb06088bc0bbcb57b3a4f698e265e01865bb8 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Sep 2016 10:18:54 -0700 Subject: [PATCH 15/46] add developer menu support to enable/disable dynamic texture management --- interface/src/Menu.cpp | 39 ++++++++++++++++++- interface/src/Menu.h | 2 + .../src/gpu/gl45/GL45BackendTexture.cpp | 33 +--------------- libraries/gpu/src/gpu/Texture.cpp | 31 +++++++++++++++ libraries/gpu/src/gpu/Texture.h | 9 +++++ 5 files changed, 82 insertions(+), 32 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3c1aa26a4a..74490b6dd1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -355,7 +355,7 @@ Menu::Menu() { //const QString = "1024 MB"; //const QString = "2048 MB"; - // Developer > Render > Resolution + // Developer > Render > Maximum Texture Memory MenuWrapper* textureMenu = renderOptionsMenu->addMenu(MenuOption::RenderMaxTextureMemory); QActionGroup* textureGroup = new QActionGroup(textureMenu); textureGroup->setExclusive(true); @@ -383,6 +383,43 @@ Menu::Menu() { gpu::Texture::setAllowedGPUMemoryUsage(newMaxTextureMemory); }); +#ifdef Q_OS_WIN + #define MIN_CORES_FOR_INCREMENTAL_TEXTURES 5 + bool recommendedIncrementalTransfers = (QThread::idealThreadCount() >= MIN_CORES_FOR_INCREMENTAL_TEXTURES); + bool recommendedSparseTextures = recommendedIncrementalTransfers; + + qDebug() << "[TEXTURE TRANSFER SUPPORT]" + << "\n\tidealThreadCount:" << QThread::idealThreadCount() + << "\n\tRECOMMENDED enableSparseTextures:" << recommendedSparseTextures + << "\n\tRECOMMENDED enableIncrementalTextures:" << recommendedIncrementalTransfers; + + gpu::Texture::setEnableIncrementalTextureTransfers(recommendedIncrementalTransfers); + gpu::Texture::setEnableSparseTextures(recommendedSparseTextures); + + // Developer > Render > Enable Dynamic Texture Management + { + auto action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableDynamicTextureManagement, 0, recommendedSparseTextures); + connect(action, &QAction::triggered, [&](bool checked) { + qDebug() << "[TEXTURE TRANSFER SUPPORT] --- Enable Dynamic Texture Management menu option:" << checked; + gpu::Texture::setEnableSparseTextures(checked); + }); + } + + // Developer > Render > Enable Incremental Texture Transfer + { + auto action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableIncrementalTextureTransfer, 0, recommendedIncrementalTransfers); + connect(action, &QAction::triggered, [&](bool checked) { + qDebug() << "[TEXTURE TRANSFER SUPPORT] --- Enable Incremental Texture Transfer menu option:" << checked; + gpu::Texture::setEnableIncrementalTextureTransfers(checked); + }); + } + +#else + qDebug() << "[TEXTURE TRANSFER SUPPORT] Incremental Texture Transfer and Dynamic Texture Management not supported on this platform."; +#endif + + + // Developer > Render > LOD Tools addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0, dialogsManager.data(), SLOT(lodTools())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b25603caeb..95cd4c5aa6 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -98,6 +98,8 @@ namespace MenuOption { const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoServerAudio = "Echo Server Audio"; const QString EnableCharacterController = "Enable avatar collisions"; + const QString EnableIncrementalTextureTransfer = "Enable Incremental Texture Transfer"; + const QString EnableDynamicTextureManagement = "Enable Dynamic Texture Management"; const QString EnableInverseKinematics = "Enable Inverse Kinematics"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarTiming = "Expand /myAvatar"; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 275b1d54ab..8aa6633f9c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -17,7 +17,6 @@ #include #include -#include #include "../gl/GLTexelFormat.h" @@ -25,34 +24,6 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -#ifdef Q_OS_WIN -#define MIN_CORES_FOR_INCREMENTAL_TEXTURES 5 -static const QString DEBUG_FLAG_INCREMENTAL("HIFI_DISABLE_INCREMENTAL_TEXTURES"); -static const QString DEBUG_FLAG_SPARSE("HIFI_DISABLE_SPARSE_TEXTURES"); - -static const bool enableIncrementalTextures = (QThread::idealThreadCount() >= MIN_CORES_FOR_INCREMENTAL_TEXTURES) && - !QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG_INCREMENTAL); - -static const bool enableSparseTextures = enableIncrementalTextures && - !QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG_SPARSE); - -class TextureTransferDebug { -public: - TextureTransferDebug() { - qDebug() << "[TEXTURE TRANSFER SUPPORT]" - << "\n\tHIFI_DISABLE_INCREMENTAL_TEXTURES:" << QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG_INCREMENTAL) - << "\n\tHIFI_DISABLE_SPARSE_TEXTURES:" << QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG_SPARSE) - << "\n\tidealThreadCount:" << QThread::idealThreadCount() - << "\n\tenableSparseTextures:" << enableSparseTextures - << "\n\tenableIncrementalTextures:" << enableSparseTextures; - } -}; -TextureTransferDebug sparseTextureDebugInfo; -#else -static bool enableSparseTextures = false; -static bool enableIncrementalTextures = false; -#endif - // Allocate 1 MB of buffer space for paged transfers #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) #define DEFAULT_GL_PIXEL_ALIGNMENT 4 @@ -274,7 +245,7 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) { - if (enableSparseTextures && _transferrable) { + if (_transferrable && Texture::getEnableSparseTextures()) { _sparseInfo.maybeMakeSparse(); } } @@ -373,7 +344,7 @@ void GL45Texture::startTransfer() { } bool GL45Texture::continueTransfer() { - if (!enableIncrementalTextures) { + if (!Texture::getEnableIncrementalTextureTransfers()) { size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1; for (uint8_t face = 0; face < maxFace; ++face) { for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) { diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index b573c8e899..44804abebe 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#include + #include "Texture.h" #include @@ -28,6 +31,34 @@ std::atomic Texture::_textureCPUCount{ 0 }; std::atomic Texture::_textureCPUMemoryUsage{ 0 }; std::atomic Texture::_allowedCPUMemoryUsage { 0 }; +std::atomic Texture::_enableSparseTextures { false }; +std::atomic Texture::_enableIncrementalTextureTransfers { false }; + +void Texture::setEnableSparseTextures(bool enabled) { +#ifdef Q_OS_WIN + qDebug() << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Sparse Textures and Dynamic Texture Management:" << enabled; + _enableSparseTextures = enabled; + if (!_enableIncrementalTextureTransfers && _enableSparseTextures) { + qDebug() << "[TEXTURE TRANSFER SUPPORT] WARNING - Sparse texture management requires incremental texture transfer enabled."; + } +#else + qDebug() << "[TEXTURE TRANSFER SUPPORT] Sparse Textures and Dynamic Texture Management not supported on this platform."; +#endif +} + +void Texture::setEnableIncrementalTextureTransfers(bool enabled) { +#ifdef Q_OS_WIN + qDebug() << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Incremental Texture Transfer:" << enabled; + _enableIncrementalTextureTransfers = enabled; + if (!_enableIncrementalTextureTransfers && _enableSparseTextures) { + qDebug() << "[TEXTURE TRANSFER SUPPORT] WARNING - Sparse texture management requires incremental texture transfer enabled."; + } +#else + qDebug() << "[TEXTURE TRANSFER SUPPORT] Incremental Texture Transfer not supported on this platform."; +#endif +} + + void Texture::updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { if (prevObjectSize == newObjectSize) { return; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index ae1afcafcb..61d03c070c 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -144,6 +144,9 @@ class Texture : public Resource { static std::atomic _textureCPUMemoryUsage; static std::atomic _allowedCPUMemoryUsage; static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); + + static std::atomic _enableSparseTextures; + static std::atomic _enableIncrementalTextureTransfers; public: static uint32_t getTextureCPUCount(); static Size getTextureCPUMemoryUsage(); @@ -154,6 +157,12 @@ public: static Size getAllowedGPUMemoryUsage(); static void setAllowedGPUMemoryUsage(Size size); + static bool getEnableSparseTextures() { return _enableSparseTextures.load(); } + static bool getEnableIncrementalTextureTransfers() { return _enableIncrementalTextureTransfers.load(); } + + static void setEnableSparseTextures(bool enabled); + static void setEnableIncrementalTextureTransfers(bool enabled); + class Usage { public: enum FlagBit { From b83243d65d5f85453eed1849d15878f08b24a79b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Sep 2016 15:49:47 -0700 Subject: [PATCH 16/46] Fixed CSS madness --- server-console/src/content-update.html | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/server-console/src/content-update.html b/server-console/src/content-update.html index 3c0eff2fa0..c4ed14473a 100644 --- a/server-console/src/content-update.html +++ b/server-console/src/content-update.html @@ -15,29 +15,25 @@
+ +
- - Step 1 -

1. Stop your Sandbox server. - + Step 2 +

2. Go to your backup directory: +

- - Step 2 -

2. Go to your backup directory: - - + Step 1 +

1. Stop your Sandbox server.

- Step 3

3. Copy the backed up content and paste it into the parent directory.

- Step 4

4. Restart your Sandbox server.

From c849307b3dede646b8baaa70e71353e7740c8eb3 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 29 Sep 2016 15:22:21 -0700 Subject: [PATCH 17/46] avoid repetition with fade on injectors --- assignment-client/src/audio/AudioMixer.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 2d2f9c267e..ccd80a4a11 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -269,19 +269,17 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& bool forceSilentBlock = true; if (!streamToAdd.getLastPopOutput().isNull()) { + bool isInjector = dynamic_cast(&streamToAdd); - // reptition with fade is enabled, and we do have a valid previous frame to repeat - // so we mix the previously-mixed block - - // this is preferable to not mixing it at all to avoid the harsh jump to silence + // in an injector, just go silent - the injector has likely ended + // in other inputs (microphone, &c.), repeat with fade to avoid the harsh jump to silence // we'll repeat the last block until it has a block to mix // and we'll gradually fade that repeated block into silence. // calculate its fade factor, which depends on how many times it's already been repeated. - repeatedFrameFadeFactor = calculateRepeatedFrameFadeFactor(streamToAdd.getConsecutiveNotMixedCount() - 1); - if (repeatedFrameFadeFactor > 0.0f) { + if (!isInjector && repeatedFrameFadeFactor > 0.0f) { // apply the repeatedFrameFadeFactor to the gain gain *= repeatedFrameFadeFactor; From f17a062399c9551ff213ab788211b2c81a4c421c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Sep 2016 17:08:34 -0700 Subject: [PATCH 18/46] CR --- server-console/src/content-update.css | 42 ++++++++++++------------ server-console/src/main.js | 46 +++++++++++++-------------- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/server-console/src/content-update.css b/server-console/src/content-update.css index ebfd8aeba0..9303e450b5 100644 --- a/server-console/src/content-update.css +++ b/server-console/src/content-update.css @@ -56,98 +56,98 @@ a:hover { color: #2D88A4; } -.header{ +.header { width: 95%; left: 2.5% } -.colmask{ +.colmask { width: 95%; left: 2.5% } -.colmid{ right: 25% } -.colin{ right: 25% } -.colleft{ right: 25% } -.col1{ +.colmid { right: 25% } +.colin { right: 25% } +.colleft { right: 25% } +.col1 { width: 23%; left: 101% } -.col2{ +.col2 { width: 23%; left: 53% } -.col3{ +.col3 { width: 23%; left: 80% } -.col4{ +.col4 { width: 23%; left: 82% } -.footer{ +.footer { width: 95%; left: 2.5% } -.header{ +.header { clear: both; float: left; position: relative; border-bottom: #000 1px solid; background-color: #b4d2f7 } -.colmask{ +.colmask { clear: both; float: left; overflow: hidden; position: relative; } -.colmid{ +.colmid { float: left; width: 100%; position: relative; } -.colin{ +.colin { float: left; width: 100%; position: relative; } -.colleft{ +.colleft { float: left; width: 100%; position: relative; } -.col1{ +.col1 { padding: 0px 0px 1em 0px; overflow: hidden; float: left; position: relative; } -.col2{ +.col2 { padding: 0px 0px 1em 0px; overflow: hidden; float: left; position: relative; } -.col3{ +.col3 { padding: 0px 0px 1em 0px; overflow: hidden; float: left; position: relative; } -.col4{ +.col4 { padding: 0px 0px 1em 0px; overflow: hidden; float: left; position: relative; } -.footer{ +.footer { clear: both; float: left; position: relative; @@ -155,7 +155,7 @@ a:hover { border-top: #000 1px solid; } -.bottom{ +.bottom { clear: both; width: 100%; float: left; diff --git a/server-console/src/main.js b/server-console/src/main.js index ea8ba6337e..e297ca3276 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -501,16 +501,15 @@ function backupResourceDirectories(folder) { fs.mkdirSync(folder); console.log("Created directory " + folder); - var dsBackup = path.join(folder, '/domain-server'); - fs.renameSync(getDomainServerClientResourcesDirectory(), dsBackup); - console.log("Moved directory " + getDomainServerClientResourcesDirectory()); - console.log("to " + dsBackup); - var acBackup = path.join(folder, '/assignment-client'); - fs.renameSync(getAssignmentClientResourcesDirectory(), acBackup); - console.log("Moved directory " + getDomainServerClientResourcesDirectory()); - console.log("to " + acBackup); + + fs.copySync(getDomainServerClientResourcesDirectory(), dsBackup); + fs.copySync(getAssignmentClientResourcesDirectory(), acBackup); + + fs.removeSync(getDomainServerClientResourcesDirectory()); + fs.removeSync(getAssignmentClientResourcesDirectory()); + return true; } catch (e) { console.log(e); @@ -519,23 +518,22 @@ function backupResourceDirectories(folder) { } function openBackupInstructions(folder) { - // Explain user how to restore server - var window = new BrowserWindow({ - icon: appIcon, - width: 800, - height: 520, - }); - window.loadURL('file://' + __dirname + '/content-update.html'); - if (!debug) { - window.setMenu(null); - } - window.show(); - - electron.ipcMain.on('ready', function() { - console.log("got ready"); - window.webContents.send('update', folder); - }); + // Explain user how to restore server + var window = new BrowserWindow({ + icon: appIcon, + width: 800, + height: 520, + }); + window.loadURL('file://' + __dirname + '/content-update.html'); + if (!debug) { + window.setMenu(null); + } + window.show(); + electron.ipcMain.on('ready', function() { + console.log("got ready"); + window.webContents.send('update', folder); + }); } function backupResourceDirectoriesAndRestart() { homeServer.stop(); From a2da749f52d435b4a57856e4de8a0a9620bb05eb Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 28 Sep 2016 19:17:24 -0700 Subject: [PATCH 19/46] Backup old domain. --- server-console/src/content-update.css | 62 ++++++++++++++++++++++++++ server-console/src/content-update.html | 22 +++++++++ server-console/src/content-update.js | 12 +++++ server-console/src/main.js | 61 +++++++++++++++---------- 4 files changed, 134 insertions(+), 23 deletions(-) create mode 100644 server-console/src/content-update.css create mode 100644 server-console/src/content-update.html create mode 100644 server-console/src/content-update.js diff --git a/server-console/src/content-update.css b/server-console/src/content-update.css new file mode 100644 index 0000000000..8c9a7c159b --- /dev/null +++ b/server-console/src/content-update.css @@ -0,0 +1,62 @@ +@font-face { + font-family: 'Raleway'; + src: url('vendor/Raleway/Raleway-Regular.ttf'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'Raleway'; + src: url('vendor/Raleway/Raleway-ExtraLight.ttf'); + font-weight: 200; + font-style: normal; +} +@font-face { + font-family: 'Raleway'; + src: url('vendor/Raleway/Raleway-SemiBold.ttf'); + font-weight: bold; + font-style: normal; +} + + +* { + font-family: "Raleway", "Open Sans", Arial, Helvetica, sans-serif; + line-height: 130%; +} + +body { + margin: 0; + padding: 0; + color: #808785; + margin: 0 auto; + text-align: center; + font-size: 13.5pt; + -webkit-touch-callout: none; -webkit-user-select: none; + cursor: default; + overflow: hidden; + font-variant-numeric: lining-nums; + -moz-font-feature-settings: "lnum"; + -webkit-font-feature-settings: "lnum"; + font-feature-settings: "lnum"; +} + +.selectable { + -webkit-touch-callout: text; + -webkit-user-select: text; + cursor: text; +} + +h1 { + font-size: 29pt; + font-weight: normal; +} + +a:link, +a:visited, +a:hover, +a:active { + color: #B4B4B4; +} + +a:hover { + color: #2D88A4; +} diff --git a/server-console/src/content-update.html b/server-console/src/content-update.html new file mode 100644 index 0000000000..7b3d1f343d --- /dev/null +++ b/server-console/src/content-update.html @@ -0,0 +1,22 @@ + + + + Server Backup + + + + +
+

We backed up your old server just in case.
If you wish to restore it, do the following:

+

+ 1) Stop your server.
+ 2) Go to the backup directory bellow.
+ 3) Move the content in the parent directory.
+ 4) Restart your server.
+
+ Backup Directory:
+ +

+
+ + diff --git a/server-console/src/content-update.js b/server-console/src/content-update.js new file mode 100644 index 0000000000..c77cfc92c6 --- /dev/null +++ b/server-console/src/content-update.js @@ -0,0 +1,12 @@ +function ready() { + console.log("Ready"); + + const electron = require('electron'); + window.$ = require('./vendor/jquery/jquery-2.1.4.min.js'); + + electron.ipcRenderer.on('update', function(event, message) { + $('#directory').html(message); + }); + + electron.ipcRenderer.send('ready'); +} diff --git a/server-console/src/main.js b/server-console/src/main.js index 82fe6b6b4d..2835dd338b 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -488,27 +488,51 @@ function updateTrayMenu(serverState) { const httpStatusPort = 60332; -function deleteResourceDirectories() { - const dsResourceDirectory = getDomainServerClientResourcesDirectory(); +function backupResourceDirectories(folder) { try { - fs.removeSync(dsResourceDirectory); - console.log("Deleted directory " + dsResourceDirectory); - } catch (e) { - console.log(e); - } - const acResourceDirectory = getAssignmentClientResourcesDirectory(); - try { - fs.removeSync(acResourceDirectory); - console.log("Deleted directory " + acResourceDirectory); + fs.mkdirSync(folder); + console.log("Created directory " + folder); + + + var dsBackup = path.join(folder, '/domain-server'); + fs.renameSync(getDomainServerClientResourcesDirectory(), dsBackup); + console.log("Moved directory " + getDomainServerClientResourcesDirectory()); + console.log("to " + dsBackup); + + var acBackup = path.join(folder, '/assignment-client'); + fs.renameSync(getAssignmentClientResourcesDirectory(), acBackup); + console.log("Moved directory " + getDomainServerClientResourcesDirectory()); + console.log("to " + acBackup); } catch (e) { console.log(e); } } -function deleteResourceDirectoriesAndRestart() { +function backupResourceDirectoriesAndRestart() { homeServer.stop(); - deleteResourceDirectories(); + + var date = new Date(); + var folder = getRootHifiDataDirectory() + "/Server Backup - " + date; + backupResourceDirectories(folder); maybeInstallDefaultContentSet(onContentLoaded); + + // Explain user how to restore server + var window = new BrowserWindow({ + icon: appIcon, + width: 500, + height: 350, + }); + window.loadURL('file://' + __dirname + '/content-update.html'); + if (!debug) { + window.setMenu(null); + } + window.show(); + + electron.ipcMain.on('ready', function() { + console.log("got ready"); + + window.webContents.send('update', folder); + }); } function checkNewContent() { @@ -537,16 +561,7 @@ function checkNewContent() { message: 'A newer version of the home content set is available.\nDo you wish to update?' }, function(idx) { if (idx === 0) { - dialog.showMessageBox({ - type: 'question', - buttons: ['Yes', 'No'], - title: 'Are you sure?', - message: 'This action will delete your current sandbox content.\nDo you wish to continue?' - }, function(idx) { - if (idx === 0 && homeServer) { - deleteResourceDirectoriesAndRestart(); - } - }); + backupResourceDirectoriesAndRestart(); } else { // They don't want to update, mark content set as current userConfig.set('homeContentLastModified', new Date()); From 23c1469df77b7874df49a098f0fb026116724be4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Sep 2016 12:15:48 -0700 Subject: [PATCH 20/46] Fix windows odd folder name issue --- server-console/src/main.js | 48 ++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 2835dd338b..b442d28855 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -503,36 +503,44 @@ function backupResourceDirectories(folder) { fs.renameSync(getAssignmentClientResourcesDirectory(), acBackup); console.log("Moved directory " + getDomainServerClientResourcesDirectory()); console.log("to " + acBackup); + return true; } catch (e) { console.log(e); + return false; } } function backupResourceDirectoriesAndRestart() { homeServer.stop(); - var date = new Date(); - var folder = getRootHifiDataDirectory() + "/Server Backup - " + date; - backupResourceDirectories(folder); - maybeInstallDefaultContentSet(onContentLoaded); + var folder = getRootHifiDataDirectory() + "/Server Backup - " + Date.now(); + if (backupResourceDirectories(folder)) { + maybeInstallDefaultContentSet(onContentLoaded); - // Explain user how to restore server - var window = new BrowserWindow({ - icon: appIcon, - width: 500, - height: 350, - }); - window.loadURL('file://' + __dirname + '/content-update.html'); - if (!debug) { - window.setMenu(null); + // Explain user how to restore server + var window = new BrowserWindow({ + icon: appIcon, + width: 500, + height: 350, + }); + window.loadURL('file://' + __dirname + '/content-update.html'); + if (!debug) { + window.setMenu(null); + } + window.show(); + + electron.ipcMain.on('ready', function() { + console.log("got ready"); + window.webContents.send('update', folder); + }); + } else { + dialog.showMessageBox({ + type: 'warning', + buttons: ['Ok'], + title: 'Update Error', + message: 'There was an error updating the content, aborting.' + }, function() {}); } - window.show(); - - electron.ipcMain.on('ready', function() { - console.log("got ready"); - - window.webContents.send('update', folder); - }); } function checkNewContent() { From 4c066bf82187d5d239ca03a5167ac34d30551e58 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Sep 2016 15:42:25 -0700 Subject: [PATCH 21/46] Update instructions UI --- server-console/src/content-update.css | 122 +++++++++++++++++++++++-- server-console/src/content-update.html | 71 ++++++++++---- server-console/src/images/step1.jpg | Bin 0 -> 10485 bytes server-console/src/images/step2.jpg | Bin 0 -> 3403 bytes server-console/src/images/step3.jpg | Bin 0 -> 6329 bytes server-console/src/images/step4.jpg | Bin 0 -> 10065 bytes server-console/src/main.js | 45 +++++---- 7 files changed, 197 insertions(+), 41 deletions(-) create mode 100644 server-console/src/images/step1.jpg create mode 100644 server-console/src/images/step2.jpg create mode 100644 server-console/src/images/step3.jpg create mode 100644 server-console/src/images/step4.jpg diff --git a/server-console/src/content-update.css b/server-console/src/content-update.css index 8c9a7c159b..ebfd8aeba0 100644 --- a/server-console/src/content-update.css +++ b/server-console/src/content-update.css @@ -28,7 +28,7 @@ body { padding: 0; color: #808785; margin: 0 auto; - text-align: center; + text-align: left; font-size: 13.5pt; -webkit-touch-callout: none; -webkit-user-select: none; cursor: default; @@ -45,11 +45,6 @@ body { cursor: text; } -h1 { - font-size: 29pt; - font-weight: normal; -} - a:link, a:visited, a:hover, @@ -60,3 +55,118 @@ a:active { a:hover { color: #2D88A4; } + +.header{ + width: 95%; + left: 2.5% +} +.colmask{ + width: 95%; + left: 2.5% +} +.colmid{ right: 25% } +.colin{ right: 25% } +.colleft{ right: 25% } +.col1{ + width: 23%; + left: 101% +} +.col2{ + width: 23%; + left: 53% +} +.col3{ + width: 23%; + left: 80% +} +.col4{ + width: 23%; + left: 82% +} +.footer{ + width: 95%; + left: 2.5% +} +.header{ + clear: both; + float: left; + position: relative; + border-bottom: #000 1px solid; + background-color: #b4d2f7 +} +.colmask{ + clear: both; + float: left; + overflow: hidden; + position: relative; + +} +.colmid{ + float: left; + width: 100%; + position: relative; + +} +.colin{ + float: left; + width: 100%; + position: relative; + +} +.colleft{ + float: left; + width: 100%; + position: relative; + +} +.col1{ + padding: 0px 0px 1em 0px; + overflow: hidden; + float: left; + position: relative; + +} +.col2{ + padding: 0px 0px 1em 0px; + overflow: hidden; + float: left; + position: relative; + +} +.col3{ + padding: 0px 0px 1em 0px; + overflow: hidden; + float: left; + position: relative; + +} +.col4{ + padding: 0px 0px 1em 0px; + overflow: hidden; + float: left; + position: relative; + +} +.footer{ + clear: both; + float: left; + position: relative; + padding-top: 8px; + border-top: #000 1px solid; + +} +.bottom{ + clear: both; + width: 100%; + float: left; + position: relative; + +} +body { + border-width: 0px; + padding: 0px; + margin: 0px; + font-size: 90%; + width: 100%; + min-width: 600px; +} diff --git a/server-console/src/content-update.html b/server-console/src/content-update.html index 7b3d1f343d..3c0eff2fa0 100644 --- a/server-console/src/content-update.html +++ b/server-console/src/content-update.html @@ -1,22 +1,57 @@ - - Server Backup - - - - -
-

We backed up your old server just in case.
If you wish to restore it, do the following:

-

- 1) Stop your server.
- 2) Go to the backup directory bellow.
- 3) Move the content in the parent directory.
- 4) Restart your server.
-
- Backup Directory:
- -

+ + Server Backup + + + + +
+

We backed up your old Sandbox content, just in case.

+

To restore it, follow these steps: + +

+
+ +
+ +
+ + Step 1 +

1. Stop your Sandbox server. + +

+ +
+ + Step 2 +

2. Go to your backup directory: + + +

+ +
+ + Step 3 +

3. Copy the backed up content and paste it into the parent directory. +

+ +
+ + Step 4 +

4. Restart your Sandbox server. +

+ +
+ +
+
- + + +
+ diff --git a/server-console/src/images/step1.jpg b/server-console/src/images/step1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cd80ae553782fb968216d5b0dc5494cc0fa63485 GIT binary patch literal 10485 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 z�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 literal 0 HcmV?d00001 diff --git a/server-console/src/images/step2.jpg b/server-console/src/images/step2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6bc285ac472f867528f5ecc1b4e6f77be574e32d GIT binary patch literal 3403 zcmcgsc|6p4AOFqec<7LdZ9As1>15`I!3<+6L#b$+8F#5M2ZK4xjLZn=ihb@2xFb_pPphF6x`%3@_00#ho0sx9`80ae!@$qOhS7=OQ@VuGEbRGvSrt#6B zF$N7-6UBTQ-JdB!dNY04TmovS^gas7W)M)WSVxQ_-UN10tSnKnjKVd zaIkT(sWDIBg9dRp92#SSHZd_0Fjz)H30FiD8*zmOa|$G;kS<{JMQk1ysZ^wS^MXVK z6l!h=9R3&CuX^>jKu5=aAIjl;i57~;%=vo1DlDW*_)IjJDdYtS=u9$`WuQ#O$J+{+ zG!aigRdbC*lcxd3>a;t;#jzCPx~b%~dvTR5Vo%v3jovE$4g)PR;#A2r<#%zvD;?aXROs;5-%b4fdF343b z2#zu{i78+oWispqJPuMda6EhAT5t>M&2kwF*Ro?lF4_iZjaE+Y-==%6iA=IretucJ zHpuzq&*U-)C;^ISqZkAhtClRl_zeI6h@u_%8Gu3{5GVu+g+gJ<8wOK8;4mfN2)GIe zgqoVVni@hwLsL^jLt9HrOM4zlDjW{iKp-@&L)devY*Xc};O4I4JI)?`>>*}px|gH%^5;*zH{A6(;~M@t&ZqKHhP`HFVqev$ zZF5=R{U#UvByT(>jDNRbV8}nFx;kvQu%taay^nq1MTJIEYW~f3?9&iu+bMmb{Ze(IlhTpFD zBV1CxWmwA062H~c(^VJU-N)lWOM|Yp3czQ7Qxm^d&%@JW+)7&WjRJT)SzfL?JU#^; zqad(UGPq*(nKOZGox-Su?N73QM*RJ{qVirpc*FL{;>p}4KfcL28|}Tj1q_R%U#KPq zwRQ%~e2(Z`)&A~YdsrN+?(UOcjocYIU0DCMlklUs};qp2;{eZRTrO5rD4UcO7d{`%pZ-v~_0(23zdl@-xVbsxvXaK+D@hxz zhE}ybrIgf8)b-z7okzQo@g_{}>1os%>h|%W0+1Z~Jn8HVA1*q1^Ma*Z(&BR4Q>{(f zR+R%jJ+PIPgj=JV=@%HDgI$%?ZkOs(S1_}$C~bQ z!>&LMww~R2UdzWUtn%fo|^JKTO)2?M@%F!$Q4!+(iwse!ukO;j?!ZLlnY1+0i zZtWVcW}&Y~iC$o~+uiTG$^Lxh!FB3`-m>Em-r(sYqoO`K1u@sw*(a@)$m{=t3H#B(bQ@B9D&fPTdKz)efnjwyhs=1~EQ!W2wALRTdJ$^Lk@ zDsSAmw=OGw!vSTW3XWD8!L22{xQuxVF3zYd@GLF7XS$4KNlofhM$)EENUv-UZJ9Ck ztbds3826}aTg7ML)yzN8i|@3$UfPo87H2=J2|&ySW4c&<6)xu|5(wj$lYOYSQz##U zLfxNSJGx1`-*JiVQ3E#c-vf{;?-cBha z`zB%>o|hf1Z;G0dysI{hjGdNUlixX)8!%%w_D3w~;g3$LUmy8A)Awynt^DZ%LS5T@ zXasRay~&Z-Z1F&ao2)YA{obe+QB>r?tQl<(mmRmC+SZ-=Ns?3JjhPHIT-0FI|I(ta z0X3pt(O4MuHg;lxiRy@huICG+4^!KbO();nzfxxtstefnFoWuvm{tD zbnSgd68a*^hpxO0Z)Ee-3r=2rZ+~_;vp#xCzq6W>{$}eV_gJU1=SM&EKJRhO7{MEG zQm``~iysT)dh#A-l12yLDBFFbi^VOUe62v4Yy2I<%esxFsgyXcm^AZN`Qe@`{k1qR zkB%hKey71OC+86R)9pUd5%E72uI`ce3)uApt9xTO=e||{cjO-g@A=tZ1P4 znYG=(?Heg+OhIMSosQ$l=wBMkYad&nV48iM^4@P_AU}@|1&%EG{fEks;JcqrzTQqF zSalq$5+E@tmj`WV_m=AKcbZs-={T*uF*Ed+1TINUcUC})V>mQnCP=@qayaz7zE`cw e*p_oAvZA~&xOII{&H!Lw zAOSD{f62izz^>zkK$8IwKm!1PIRG3?QbS#dM4XD0lm`Lg=!Ct2gdngODIZ6i6ci#Y z1*mEI;2aTXBvIf7(go$AF8HacPEY{lq%H`The^Y5+DKQ_Z@ze>xv#MW!WWHDb`sRo z5K!|`@xkCQNTQ>F560btpyH!0cw}6~gP?LaEF~y#q(Vfi3u+yD6|ja~5zxlskpl7% z1uy~%g$gJtL!k0XvNH0L0y5H2c`0dSDX2798ma=7R*{hw_$h)K0%~|CXB9IYy`R4D zPR{CrKNm$Jksu^l2o~=m1yxp7mXel{l92)9ot(i0vIo)82kb!*`ei`}NkHIHI3fz` zA#iBX@dnn5s4gh@YY7EC0y^siVc z4FNT&!^iu#$Ng)_Oh@nV_D}L^2>eBVq=%EbAYM>Y<6sqFJ)$h&^a=m~(7`Ye3{ZhU zASw_Q6%`fr;YCe-*w9cP3JopIkff~dPP_cuk*+B=5Kp6l6Ks3}K&|jMX0~HM& zJ&2Z(`WOfVfN1|d1<--0X=v#gsEz|5YAR}KIwm?=YT8362%w^-p=GC|KX-xwYU=su zB!`wDzEIXYy8Dzox1*57W5#2*Q)Miz7a@zI^M!mg9%@^qY@f~V-YZl@N!?vycGyEM5TB_ylOA$%|dFjIp7Dt2miKoi(m z2QD6`0sTKWAdC+sJ-eg_W@^>KjX;FOM5%{42{9|SW~}R-^=QKP@Mc6=`VG(K?g%j` z8`?fkaF}OR;ubOK;@vYym5+C*(-?v5gbtu>Sc&YVXBIRh%NxJkdNjWp6ha@!e`EBC zhpe4hTT$pY$v4t4zXbEFgvqMqX`y(&d+{OfW3A?#2^Hrh>^sj>Qu7?b+ir$C8Ww3h zbUE{#A*1EEOKwT+3XFx<%th>~Zac|bIp1$=V!P^-_HP;*YBMWMaYKEnN#uLZCxWme zfhF+GZBLGNccvY0cl@Kak?_W}D$Bi5ajW%w)$;zE)#$*Sglq9HdIs?-yrYEH)q)Xm zG#Z&s&|dw$JM1jX{I)AN`tdS&9yNG3Jf#>jNaCgF`L3hjdv>D^?HVUV(aU~=F{n%5 zR<;RdzI9`#t19H^dQN+@7@t#fOEt;%Fnd ziyz%rwalAqX+usk+J|#ASuZr*Oo z*P^@vh$Ulo=CKq^-c7I8m&wqRuC~{dp&ANrNw!dKMc0`ohqh(4-o!S1_g#qLV@tbA zC`DuA?D@H{2!yEQY;H^ui(5;$Ioy~9zS@k-nr}3YG+pSiy>05l+(OW0E*RC|CvnM; zb)erH5HeE^B;07Dk0oYVvZpAJA$5NVW0va)hoRDgGe61AU}iW2;F`NbCbp4YosKY! zk}IUBy#nQIS1<^HKP?hlAaKCIe97sBu~t1M)?HsNvKKV{QH-cw2G)M3%xr78K$Yu9 z&|)GpFJFUIjE42wY}7KVNKz(Nio)q!dP}pR9ZBjhST-bW8;Jiwc~FQaqgDoot*T#; z#yegcgY|~7&qk6j3n=dEPmI3_tMT{9l1L~e+L8gndwqHOm;)g0jH_QnC9cz4d(Mx1 zdIqAe(bd|Cwk)a432ojndps(`o5X4xDK?Z|#HsRuk53k(CmJs(hk1}j)f{CMGRJ+w z`)*JIiEmIfr8d8IXnNpN?ida9v2A6zqI(+q8`Q7Q0q z36yo6fl;{(!0Gp5mig;I;|g5V+sweA>@pYC1lX>CL1Fq-Vv;?F#Ln2BSbA&UF|^$R zY5_Ho7G1m|Hhnkj_Gp0!Y+;DncwLn5slP-~owAWl>1^FqT<3!g>;<>uJz-`K^BE-& z*{pnB@rCVnQH@}{-C~M6v-^#a1tBl7@F$!_1wsVStyhi(md+6Ik2&g?uhY#L8HKS) zweOnT%o2(%vbN;T+TD$P59gJ9o)MDyxJzGN_56b6?2b(tdCa|J&i|>*FuRuhz7+N@`h!vNcY-YQ2 zz3-M?6*qtO9}HB$ErFv(`uolI30^n4m`3{*hW*ANolU1JhHF`3zE_=<BEQ7bXuFzU zt~=e}_gULLm;=1D_sS*If&>aRn|7;dfO{fJp;CeSKF<@m+f^a$x5>3GwpdC^{P z%KLdqH?z9Wd!H;~w6k~^iZXi)yPi`KGB+8J7k}<(`v>LcaYmdy-Pp8Uc*m7B!w8j) zT&3$VTpUKTJ}e@nLYChM>pr07RrH)4uBjoZZTkxe@AKF%P2^A_R+A2Z>W9cY_ok9n zPU3Q8N4oTf#SG%JDnRoK=E_+=zIjkM+G5QBqYq+noc zLqL^J<$I6+mBlg7?AAmR+=gA8L?Z4PrNpe?8p0eNO#fS}?pislwMD{Ox#9|cD$eL@ z3=YuCca>7!IX;;tNoilX1z*-_sS&r2l5&<_r=qTySeh_*cNl7CW<}}I zt8m3GLmXAJ*%++LR-V0YQpp&~KLFT3t8Z>jubS;G67ffEf2_>4=1+k+>mOmM-}=A87p#3ZHj-M(`Zts3p-^gZ&9F}`O-iPG*4Wijs{J<*7t8J z%)ZgJJS&RS92#~5uXgKc_C?vus+XW;1B{mii&)IU{EEUotLYfaRmyeJy6?q|5LfW)arHCG3&5DXBv(f6ghQ;(Z(@=c^~=}nH` zXEEr?`zX)XeQ|~Lrk-uh2i3amRe9qR+NOwLu37~nevy1}X|!+yF%HXhd&yfU2^8-3 zCIZFp|1L(Eq%xc9;Qq3YSpvi5#tk_YsdSxh(73-?2~p7CI!gVzCJitpy2C~5io<8zmqq;&)_!AieH}qf47hgy-e|=tBjvji;*Yp1T52_*(%ItGC zhrEa>BBrJzvU2W@C8i_%3yZ?!N2yc#@@=X@V?uAZ6F~x!-{YC9Uo<(}g;VG-hXHwC8N|DRF-sAYIsV}F^teGj{ z?L)SF_i4Uxg12ZDbKqwx;V?xB7i3bi2V}~SXlbO23Rk-DN0rsXS(%h7F&3UiE2KM7 zMq8*s$dh&^5nxswH~`Mqd~U(2V<7wwg*5O{5$`LA&p z-YO?=t=Or4xsa8@;JClO=FHWi{ay8Vi!V%f1Gn5i)e1c+k&=?p ztBGo9F22)W5<$cMR_j>QV7FViNaQojLsKtUR>Wt>d2GR(Gp(aI7H*b&QI{dBl3j;} z10Y{ykUT71x0*6$xuyQ(0H}p;rF_lK%p-a7m$&T{oi(qB+?yrYm>5~FPQm+jBeoGs zBYuij>|LEWB2i;OVLV8uGgArBtw`vhJ@i`lMakxz1-Z?kgZsEaP*)}h^ z-n@3UqHd;FdFi8Hd{e^FG&LE1;iCcDYp0!Cb<<{w+mflg73gY&uAyj2uW#w1Z zd|Dq(R&1nvu9P{KBK?hjM>P9{O8yc#_{RbLa(k-)X3Kxexs?0Z3LWg#X=H(B%AMw-nifibN}_vIQ`FRu$ZM=x-8Ww6 zr_4Lj3OG)bF-3*+1~;tO((~bzK^{{e76+|I{?33e)+5#D8obDH~u> z;O9(95g)PY3hgu7`QDe1o2dw`fBNpa!&2$0iYckCaZ#d(x1b9XqIzQ_rc;ez zR{NhAdCP~ojpPHc#m+Wvxk7m_OMP-sh!pP#t3F4bwocBZDbRox583k-_yCAf2%dZz z{y0Wt)E-(dr7d^>$dtmWDo#7w#g-ID8SK5VH8sv%>p*!8eo*=QS1L%bTx-|D;c7DZ z>V>>ig=%Sk3Vr>^Na3&zz6qId-eBk4VutN zrdd-mo;-_tq3jdWZlWieUF+bZo(wnENzUE`FPKI-Tm7gWe^I%1S0~v{Z7pNWc>RKZ z{=;*1($#;`_-CE@lg1qNTrFvzXEs_U9q{WH5|hJBO#G{VkXPMg!LB1+xQ+6o($m#X zC0(MDUcdSIfm$hf(x5tN%4*l1Q#-)XM}5`ASlG+HXX8J937ohPEES_W`))p_zIt{F zuB^_ToV-h(*e2U6pYToTC`;8dh_^i|d#OSrFRcdCI8N=&%8=bR>Z8f3zh1u|ach`& zX#7?dasaG_xAQrYPG3z|`@jTyzOUb1=`9%LY>mxA1VA&z5tSHQz?FgH2h^CjNxi8uft zJP3lm%{9{sEC2RhjzB@JwVh%gh%qle zaD0XHR0XEXNE^BluWIwqti)U5N>uu_{>}TCM0{q2flw$x#{@7A*P$35Ibs%Tla`)m+5P}uIfX+tte9Uz?Ob@P2|W%b{}6S zB_%v?_KzLZM{wrY2BP=uf6>Qkp|@3U=))i$ON(70;Tx(9Pm4117!Ly>AbQ0&HxMMX z#jOXA8jP>xq?Ok5k9Gmg&OBB@&?gnX9<$9h>XI?DGO>l*({4Q$jvP91Tl>!Y!8f~o zIihyWjwnifk6Dj_H5!VvbsK1P=t^>|b>O?U@10|j*?woA zVXtT%W^0ROi9tdmX*nRZMvL|^OFfFu1fG2*6?_fZqTx8q&Xuu0?xZh{H81h>96w6@ wp@}%o6`CS#Y24j(wwD4q3A4+&FkNo+89Ih5t3%S<0?Pk|e{aCWz=Og60rRikcK`qY literal 0 HcmV?d00001 diff --git a/server-console/src/images/step4.jpg b/server-console/src/images/step4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..27694aee7437ea9acfabfab87112f58bb46910a0 GIT binary patch literal 10065 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 literal 0 HcmV?d00001 diff --git a/server-console/src/main.js b/server-console/src/main.js index b442d28855..ea8ba6337e 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -408,6 +408,13 @@ var labels = { logWindow.open(); } }, + restoreBackup: { + label: 'Restore Backup Instructions', + click: function() { + var folder = getRootHifiDataDirectory() + "/Server Backup"; + openBackupInstructions(folder); + } + }, share: { label: 'Share', click: function() { @@ -443,6 +450,7 @@ function buildMenuArray(serverState) { menuArray.push(labels.stopServer); menuArray.push(labels.settings); menuArray.push(labels.viewLogs); + menuArray.push(labels.restoreBackup); menuArray.push(separator); menuArray.push(labels.share); menuArray.push(separator); @@ -510,29 +518,32 @@ function backupResourceDirectories(folder) { } } +function openBackupInstructions(folder) { + // Explain user how to restore server + var window = new BrowserWindow({ + icon: appIcon, + width: 800, + height: 520, + }); + window.loadURL('file://' + __dirname + '/content-update.html'); + if (!debug) { + window.setMenu(null); + } + window.show(); + + electron.ipcMain.on('ready', function() { + console.log("got ready"); + window.webContents.send('update', folder); + }); + +} function backupResourceDirectoriesAndRestart() { homeServer.stop(); var folder = getRootHifiDataDirectory() + "/Server Backup - " + Date.now(); if (backupResourceDirectories(folder)) { maybeInstallDefaultContentSet(onContentLoaded); - - // Explain user how to restore server - var window = new BrowserWindow({ - icon: appIcon, - width: 500, - height: 350, - }); - window.loadURL('file://' + __dirname + '/content-update.html'); - if (!debug) { - window.setMenu(null); - } - window.show(); - - electron.ipcMain.on('ready', function() { - console.log("got ready"); - window.webContents.send('update', folder); - }); + openBackupInstructions(folder); } else { dialog.showMessageBox({ type: 'warning', From 6f725e8c02d880ced1fa1aa6d4a4386403c2f492 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Sep 2016 15:49:47 -0700 Subject: [PATCH 22/46] Fixed CSS madness --- server-console/src/content-update.html | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/server-console/src/content-update.html b/server-console/src/content-update.html index 3c0eff2fa0..c4ed14473a 100644 --- a/server-console/src/content-update.html +++ b/server-console/src/content-update.html @@ -15,29 +15,25 @@
+ +
- - Step 1 -

1. Stop your Sandbox server. - + Step 2 +

2. Go to your backup directory: +

- - Step 2 -

2. Go to your backup directory: - - + Step 1 +

1. Stop your Sandbox server.

- Step 3

3. Copy the backed up content and paste it into the parent directory.

- Step 4

4. Restart your Sandbox server.

From 2db8158f4b70b8fc1bbd47a04b7aabdea2da189f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Sep 2016 17:08:34 -0700 Subject: [PATCH 23/46] CR --- server-console/src/content-update.css | 42 ++++++++++++------------ server-console/src/main.js | 46 +++++++++++++-------------- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/server-console/src/content-update.css b/server-console/src/content-update.css index ebfd8aeba0..9303e450b5 100644 --- a/server-console/src/content-update.css +++ b/server-console/src/content-update.css @@ -56,98 +56,98 @@ a:hover { color: #2D88A4; } -.header{ +.header { width: 95%; left: 2.5% } -.colmask{ +.colmask { width: 95%; left: 2.5% } -.colmid{ right: 25% } -.colin{ right: 25% } -.colleft{ right: 25% } -.col1{ +.colmid { right: 25% } +.colin { right: 25% } +.colleft { right: 25% } +.col1 { width: 23%; left: 101% } -.col2{ +.col2 { width: 23%; left: 53% } -.col3{ +.col3 { width: 23%; left: 80% } -.col4{ +.col4 { width: 23%; left: 82% } -.footer{ +.footer { width: 95%; left: 2.5% } -.header{ +.header { clear: both; float: left; position: relative; border-bottom: #000 1px solid; background-color: #b4d2f7 } -.colmask{ +.colmask { clear: both; float: left; overflow: hidden; position: relative; } -.colmid{ +.colmid { float: left; width: 100%; position: relative; } -.colin{ +.colin { float: left; width: 100%; position: relative; } -.colleft{ +.colleft { float: left; width: 100%; position: relative; } -.col1{ +.col1 { padding: 0px 0px 1em 0px; overflow: hidden; float: left; position: relative; } -.col2{ +.col2 { padding: 0px 0px 1em 0px; overflow: hidden; float: left; position: relative; } -.col3{ +.col3 { padding: 0px 0px 1em 0px; overflow: hidden; float: left; position: relative; } -.col4{ +.col4 { padding: 0px 0px 1em 0px; overflow: hidden; float: left; position: relative; } -.footer{ +.footer { clear: both; float: left; position: relative; @@ -155,7 +155,7 @@ a:hover { border-top: #000 1px solid; } -.bottom{ +.bottom { clear: both; width: 100%; float: left; diff --git a/server-console/src/main.js b/server-console/src/main.js index ea8ba6337e..e297ca3276 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -501,16 +501,15 @@ function backupResourceDirectories(folder) { fs.mkdirSync(folder); console.log("Created directory " + folder); - var dsBackup = path.join(folder, '/domain-server'); - fs.renameSync(getDomainServerClientResourcesDirectory(), dsBackup); - console.log("Moved directory " + getDomainServerClientResourcesDirectory()); - console.log("to " + dsBackup); - var acBackup = path.join(folder, '/assignment-client'); - fs.renameSync(getAssignmentClientResourcesDirectory(), acBackup); - console.log("Moved directory " + getDomainServerClientResourcesDirectory()); - console.log("to " + acBackup); + + fs.copySync(getDomainServerClientResourcesDirectory(), dsBackup); + fs.copySync(getAssignmentClientResourcesDirectory(), acBackup); + + fs.removeSync(getDomainServerClientResourcesDirectory()); + fs.removeSync(getAssignmentClientResourcesDirectory()); + return true; } catch (e) { console.log(e); @@ -519,23 +518,22 @@ function backupResourceDirectories(folder) { } function openBackupInstructions(folder) { - // Explain user how to restore server - var window = new BrowserWindow({ - icon: appIcon, - width: 800, - height: 520, - }); - window.loadURL('file://' + __dirname + '/content-update.html'); - if (!debug) { - window.setMenu(null); - } - window.show(); - - electron.ipcMain.on('ready', function() { - console.log("got ready"); - window.webContents.send('update', folder); - }); + // Explain user how to restore server + var window = new BrowserWindow({ + icon: appIcon, + width: 800, + height: 520, + }); + window.loadURL('file://' + __dirname + '/content-update.html'); + if (!debug) { + window.setMenu(null); + } + window.show(); + electron.ipcMain.on('ready', function() { + console.log("got ready"); + window.webContents.send('update', folder); + }); } function backupResourceDirectoriesAndRestart() { homeServer.stop(); From 4f7fdfc3451426dad666be48187d34244aad87a4 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 30 Sep 2016 03:31:35 +0200 Subject: [PATCH 24/46] eliminated `else`, this might look cleaner --- libraries/networking/src/NodeList.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index f3934a51f9..617ba85bad 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -536,25 +536,25 @@ void NodeList::processDomainServerList(QSharedPointer message) QUuid domainUUID; packetStream >> domainUUID; + if (_domainHandler.isConnected() && _domainHandler.getUUID() != domainUUID) { + // Recieved packet from different domain. + qWarning() << "IGNORING DomainList packet from" << domainUUID << "while connected to" << _domainHandler.getUUID(); + return; + } + // pull our owner UUID from the packet, it's always the first thing QUuid newUUID; packetStream >> newUUID; + setSessionUUID(newUUID); // if this was the first domain-server list from this domain, we've now connected if (!_domainHandler.isConnected()) { - setSessionUUID(newUUID); _domainHandler.setUUID(domainUUID); _domainHandler.setIsConnected(true); // in case we didn't use a place name to get to this domain, // give the address manager a chance to lookup a default one now DependencyManager::get()->lookupShareableNameForDomainID(domainUUID); - } else if (_domainHandler.getUUID() != domainUUID) { - // Recieved packet from different domain. - qWarning() << "IGNORING DomainList packet from" << domainUUID << "while connected to" << _domainHandler.getUUID(); - return; - } else { - setSessionUUID(newUUID); } // pull the permissions/right/privileges for this node out of the stream From 42e28fa010a4dd4615e430b3aa1e4a51000e9f23 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 30 Sep 2016 15:48:10 -0700 Subject: [PATCH 25/46] Fix crash in initializing texture transfer thread --- libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 13 ++++++++++++- libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h | 12 ++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp index 75af08d9a3..45403f4d4d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp @@ -18,7 +18,6 @@ std::unordered_map _map; #endif -//#define TEXTURE_TRANSFER_PBOS #ifdef TEXTURE_TRANSFER_PBOS #define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024) @@ -62,11 +61,16 @@ void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texture void GLTextureTransferHelper::setup() { #ifdef THREADED_TEXTURE_TRANSFER _context.makeCurrent(); + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // FIXME don't use opengl 4.5 DSA functionality without verifying it's present glCreateRenderbuffers(1, &_drawRenderbuffer); glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128); glCreateFramebuffers(1, &_drawFramebuffer); glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer); glCreateFramebuffers(1, &_readFramebuffer); +#endif + #ifdef TEXTURE_TRANSFER_PBOS std::array pbos; glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]); @@ -84,7 +88,9 @@ void GLTextureTransferHelper::setup() { void GLTextureTransferHelper::shutdown() { #ifdef THREADED_TEXTURE_TRANSFER _context.makeCurrent(); +#endif +#ifdef TEXTURE_TRANSFER_FORCE_DRAW glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); glDeleteFramebuffers(1, &_drawFramebuffer); _drawFramebuffer = 0; @@ -165,6 +171,11 @@ bool GLTextureTransferHelper::process() { } gltexture->finishTransfer(); + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use +#endif + #ifdef THREADED_TEXTURE_TRANSFER clientWait(); #endif diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h index 289aec40bb..a23c282fd4 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h @@ -21,6 +21,14 @@ #define THREADED_TEXTURE_TRANSFER #endif +#ifdef THREADED_TEXTURE_TRANSFER +// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread +// also, the current draw code is implicitly using OpenGL 4.5 functionality +//#define TEXTURE_TRANSFER_FORCE_DRAW +// FIXME PBO's increase the complexity and don't seem to work reliably +//#define TEXTURE_TRANSFER_PBOS +#endif + namespace gpu { namespace gl { using TextureList = std::list; @@ -43,11 +51,15 @@ public: private: #ifdef THREADED_TEXTURE_TRANSFER ::gl::OffscreenContext _context; +#endif + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread GLuint _drawRenderbuffer { 0 }; GLuint _drawFramebuffer { 0 }; GLuint _readFramebuffer { 0 }; #endif + // A mutex for protecting items access on the render and transfer threads Mutex _mutex; // Commands that have been submitted for execution on the texture transfer thread From c806f6cad2166518466eb30cb0b9089cfa067cc8 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 30 Sep 2016 16:05:39 -0700 Subject: [PATCH 26/46] better shutdown check for timer firings --- libraries/script-engine/src/ScriptEngine.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e4d1cd7307..160ad77197 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1017,9 +1017,12 @@ void ScriptEngine::updateMemoryCost(const qint64& deltaSize) { } void ScriptEngine::timerFired() { - if (DependencyManager::get()->isStopped()) { - qCDebug(scriptengine) << "Script.timerFired() while shutting down is ignored... parent script:" << getFilename(); - return; // bail early + { + auto engine = DependencyManager::get(); + if (!engine || engine->isStopped()) { + qCDebug(scriptengine) << "Script.timerFired() while shutting down is ignored... parent script:" << getFilename(); + return; // bail early + } } QTimer* callingTimer = reinterpret_cast(sender()); From 0f06d0e4cc9f974fe4ec90d80a65cd0fb86283e2 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 1 Oct 2016 10:40:31 -0700 Subject: [PATCH 27/46] Don't use GL functions before making the context current --- libraries/gl/src/gl/GLWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gl/src/gl/GLWidget.cpp b/libraries/gl/src/gl/GLWidget.cpp index f2b823a65e..23bdcff640 100644 --- a/libraries/gl/src/gl/GLWidget.cpp +++ b/libraries/gl/src/gl/GLWidget.cpp @@ -67,8 +67,8 @@ void GLWidget::createContext() { _context = new gl::Context(); _context->setWindow(windowHandle()); _context->create(); - _context->clear(); _context->makeCurrent(); + _context->clear(); } bool GLWidget::makeCurrent() { From 2cba3db2b6471d9e9b3adb5f9b3d5dfa4c15365b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Sep 2016 17:43:26 -0700 Subject: [PATCH 28/46] fix crash when bouncing to escotology, which may or may not be related --- .../src/model-networking/ModelCache.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 306a19c308..6eacbab46a 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -377,8 +377,10 @@ void GeometryResource::deleter() { } void GeometryResource::setTextures() { - for (const FBXMaterial& material : _fbxGeometry->materials) { - _materials.push_back(std::make_shared(material, _textureBaseUrl)); + if (_fbxGeometry) { + for (const FBXMaterial& material : _fbxGeometry->materials) { + _materials.push_back(std::make_shared(material, _textureBaseUrl)); + } } } @@ -457,7 +459,9 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, c _textures[channel] = Texture { fbxTexture.name, texture }; auto map = std::make_shared(); - map->setTextureSource(texture->_textureSource); + if (texture) { + map->setTextureSource(texture->_textureSource); + } map->setTextureTransform(fbxTexture.transform); return map; From 265996156c5376c6c9846ea65ac4d81b7f1ca0e9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 27 Sep 2016 11:44:51 -0700 Subject: [PATCH 29/46] Remove unused header and typedef --- .../display-plugins/src/display-plugins/OpenGLDisplayPlugin.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index ef15861843..74f8cdbc10 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -8,6 +8,7 @@ #pragma once #include "DisplayPlugin.h" +#include #include #include @@ -18,7 +19,6 @@ #include #include -#include #include namespace gpu { @@ -35,7 +35,6 @@ protected: using Mutex = std::mutex; using Lock = std::unique_lock; using Condition = std::condition_variable; - using TextureEscrow = GLEscrow; public: // These must be final to ensure proper ordering of operations // between the main thread and the presentation thread From 933388bc61c136312f4bd31fa655b0813fc5361b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 27 Sep 2016 11:45:35 -0700 Subject: [PATCH 30/46] Remove oglplus usage from offscreen UI --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 194 +++++++++++++++----- 1 file changed, 143 insertions(+), 51 deletions(-) diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index bdfa359b8b..d1c884f264 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -6,7 +6,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "OffscreenQmlSurface.h" -#include "OglplusHelpers.h" +#include "Config.h" + +#include +#include +#include #include #include @@ -116,6 +120,108 @@ static const QEvent::Type RENDER = QEvent::Type(QEvent::User + 2); static const QEvent::Type RESIZE = QEvent::Type(QEvent::User + 3); static const QEvent::Type STOP = QEvent::Type(QEvent::User + 4); +class RawTextureRecycler { +public: + using TexturePtr = GLuint; + RawTextureRecycler(bool useMipmaps) : _useMipmaps(useMipmaps) {} + void setSize(const uvec2& size); + void clear(); + TexturePtr getNextTexture(); + void recycleTexture(GLuint texture); + +private: + + struct TexInfo { + TexturePtr _tex { 0 }; + uvec2 _size; + bool _active { false }; + + TexInfo() {} + TexInfo(TexturePtr tex, const uvec2& size) : _tex(tex), _size(size) {} + }; + + using Map = std::map; + using Queue = std::queue; + + Map _allTextures; + Queue _readyTextures; + uvec2 _size { 1920, 1080 }; + bool _useMipmaps; +}; + + +void RawTextureRecycler::setSize(const uvec2& size) { + if (size == _size) { + return; + } + _size = size; + while (!_readyTextures.empty()) { + _readyTextures.pop(); + } + std::set toDelete; + std::for_each(_allTextures.begin(), _allTextures.end(), [&](Map::const_reference item) { + if (!item.second._active && item.second._size != _size) { + toDelete.insert(item.first); + } + }); + std::for_each(toDelete.begin(), toDelete.end(), [&](Map::key_type key) { + _allTextures.erase(key); + }); +} + +void RawTextureRecycler::clear() { + while (!_readyTextures.empty()) { + _readyTextures.pop(); + } + _allTextures.clear(); +} + +RawTextureRecycler::TexturePtr RawTextureRecycler::getNextTexture() { + if (_readyTextures.empty()) { + TexturePtr newTexture; + glGenTextures(1, &newTexture); + + glBindTexture(GL_TEXTURE_2D, newTexture); + if (_useMipmaps) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.2f); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _size.x, _size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + _allTextures[newTexture] = TexInfo { newTexture, _size }; + _readyTextures.push(newTexture); + } + + TexturePtr result = _readyTextures.front(); + _readyTextures.pop(); + auto& item = _allTextures[result]; + item._active = true; + return result; +} + +void RawTextureRecycler::recycleTexture(GLuint texture) { + Q_ASSERT(_allTextures.count(texture)); + auto& item = _allTextures[texture]; + Q_ASSERT(item._active); + item._active = false; + if (item._size != _size) { + // Buh-bye + _allTextures.erase(texture); + return; + } + + _readyTextures.push(item._tex); +} + + class OffscreenQmlRenderThread : public QThread { public: OffscreenQmlRenderThread(OffscreenQmlSurface* surface, QOpenGLContext* shareContext); @@ -165,9 +271,9 @@ private: OffscreenQmlSurface* _surface{ nullptr }; QQuickWindow* _quickWindow{ nullptr }; QMyQuickRenderControl* _renderControl{ nullptr }; - FramebufferPtr _fbo; - RenderbufferPtr _depthStencil; - TextureRecycler _textures { true }; + GLuint _fbo { 0 }; + GLuint _depthStencil { 0 }; + RawTextureRecycler _textures { true }; GLTextureEscrow _escrow; uint64_t _lastRenderTime{ 0 }; @@ -253,24 +359,23 @@ bool OffscreenQmlRenderThread::event(QEvent *e) { } void OffscreenQmlRenderThread::setupFbo() { - using namespace oglplus; _textures.setSize(_size); - - try { - _depthStencil.reset(new Renderbuffer()); - Context::Bound(Renderbuffer::Target::Renderbuffer, *_depthStencil) - .Storage( - PixelDataInternalFormat::DepthComponent, - _size.x, _size.y); - - _fbo.reset(new Framebuffer()); - _fbo->Bind(Framebuffer::Target::Draw); - _fbo->AttachRenderbuffer(Framebuffer::Target::Draw, - FramebufferAttachment::Depth, *_depthStencil); - DefaultFramebuffer().Bind(Framebuffer::Target::Draw); - } catch (oglplus::Error& error) { - qWarning() << "OpenGL error in QML render setup: " << error.what(); + if (_depthStencil) { + glDeleteRenderbuffers(1, &_depthStencil); + _depthStencil = 0; } + glGenRenderbuffers(1, &_depthStencil); + glBindRenderbuffer(GL_RENDERBUFFER, _depthStencil); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _size.x, _size.y); + + if (_fbo) { + glDeleteFramebuffers(1, &_fbo); + _fbo = 0; + } + glGenFramebuffers(1, &_fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencil); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } QJsonObject OffscreenQmlRenderThread::getGLContextData() { @@ -309,8 +414,15 @@ void OffscreenQmlRenderThread::init() { void OffscreenQmlRenderThread::cleanup() { _renderControl->invalidate(); - _fbo.reset(); - _depthStencil.reset(); + if (_depthStencil) { + glDeleteRenderbuffers(1, &_depthStencil); + _depthStencil = 0; + } + if (_fbo) { + glDeleteFramebuffers(1, &_fbo); + _fbo = 0; + } + _textures.clear(); _canvas.doneCurrent(); @@ -371,42 +483,22 @@ void OffscreenQmlRenderThread::render() { releaseMainThread.trigger(); } - using namespace oglplus; - - _quickWindow->setRenderTarget(GetName(*_fbo), QSize(_size.x, _size.y)); + _quickWindow->setRenderTarget(_fbo, QSize(_size.x, _size.y)); try { - PROFILE_RANGE("qml_render") - - TexturePtr texture = _textures.getNextTexture(); - - try { - _fbo->Bind(Framebuffer::Target::Draw); - _fbo->AttachTexture(Framebuffer::Target::Draw, FramebufferAttachment::Color, *texture, 0); - _fbo->Complete(Framebuffer::Target::Draw); - } catch (oglplus::Error& error) { - qWarning() << "OpenGL error in QML render: " << error.what(); - - // In case we are failing from a failed setupFbo, reset fbo before next render - setupFbo(); - throw; - } - - { + GLuint texture = _textures.getNextTexture(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0); PROFILE_RANGE("qml_render->rendercontrol") _renderControl->render(); - // FIXME The web browsers seem to be leaving GL in an error state. - // Need a debug context with sync logging to figure out why. - // for now just clear the errors - glGetError(); - } - Context::Bound(oglplus::Texture::Target::_2D, *texture).GenerateMipmap(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, texture); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); - // FIXME probably unecessary - DefaultFramebuffer().Bind(Framebuffer::Target::Draw); _quickWindow->resetOpenGLState(); - _escrow.submit(GetName(*texture)); + _escrow.submit(texture); _lastRenderTime = usecTimestampNow(); } catch (std::runtime_error& error) { qWarning() << "Failed to render QML: " << error.what(); From 04f654794069b451039f04bd139af994758e0103 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 1 Oct 2016 12:15:03 -0700 Subject: [PATCH 31/46] add guards to getShapeKey --- libraries/render-utils/src/MeshPartPayload.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 50c0c869ff..2644bc6096 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -429,7 +429,12 @@ ItemKey ModelMeshPartPayload::getKey() const { } ShapeKey ModelMeshPartPayload::getShapeKey() const { - assert(_model->isLoaded()); + + // guard against partially loaded meshes + if (!_model || !_model->isLoaded() || !_model->getGeometry()) { + return ShapeKey::Builder::invalid(); + } + const FBXGeometry& geometry = _model->getFBXGeometry(); const auto& networkMeshes = _model->getGeometry()->getMeshes(); From a79485f8c2bf84f0140b479959ffea2362a72b88 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Sat, 1 Oct 2016 13:37:42 -0700 Subject: [PATCH 32/46] Fix for crash bug in web entities Guard against nullptrs in RenderableWebEntitItem and WebEntityAPIHelper. These pointers can go null if the webEntity is deleted on the main thread. Also, the backing offscreen qml surface can be destroyed if the webEntity has not been rendered for 30 seconds due to frustum culling. --- .../src/RenderableWebEntityItem.cpp | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 1c177cffc4..86bce87ba2 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -58,9 +58,13 @@ void WebEntityAPIHelper::emitWebEvent(const QVariant& message) { } else { // special case to handle raising and lowering the virtual keyboard if (message.type() == QVariant::String && message.toString() == "_RAISE_KEYBOARD" && _renderableWebEntityItem) { - _renderableWebEntityItem->setKeyboardRaised(true); + if (_renderableWebEntityItem) { + _renderableWebEntityItem->setKeyboardRaised(true); + } } else if (message.type() == QVariant::String && message.toString() == "_LOWER_KEYBOARD" && _renderableWebEntityItem) { - _renderableWebEntityItem->setKeyboardRaised(false); + if (_renderableWebEntityItem) { + _renderableWebEntityItem->setKeyboardRaised(false); + } } else { emit webEventReceived(message); } @@ -343,7 +347,7 @@ void RenderableWebEntityItem::destroyWebSurface() { // The lifetime of the QML surface MUST be managed by the main thread // Additionally, we MUST use local variables copied by value, rather than - // member variables, since they would implicitly refer to a this that + // member variables, since they would implicitly refer to a this that // is no longer valid auto webSurface = _webSurface; AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] { @@ -388,35 +392,40 @@ static bool equals(const QByteArray& byteArray, const uint8_t* ptr) { } void RenderableWebEntityItem::synthesizeKeyPress(QString key) { - auto utf8Key = key.toUtf8(); + auto eventHandler = getEventHandler(); + if (eventHandler) { + auto utf8Key = key.toUtf8(); - int scanCode = (int)utf8Key[0]; - QString keyString = key; - if (equals(utf8Key, UPWARDS_WHITE_ARROW_FROM_BAR) || equals(utf8Key, ASTERISIM) || - equals(utf8Key, (uint8_t*)PUNCTUATION_STRING) || equals(utf8Key, (uint8_t*)ALPHABET_STRING)) { - return; // ignore - } else if (equals(utf8Key, LEFT_ARROW)) { - scanCode = Qt::Key_Backspace; - keyString = "\x08"; - } else if (equals(utf8Key, RETURN_SYMBOL)) { - scanCode = Qt::Key_Return; - keyString = "\x0d"; - } else if (equals(utf8Key, LEFTWARD_WHITE_ARROW)) { - scanCode = Qt::Key_Left; - keyString = ""; - } else if (equals(utf8Key, RIGHTWARD_WHITE_ARROW)) { - scanCode = Qt::Key_Right; - keyString = ""; + int scanCode = (int)utf8Key[0]; + QString keyString = key; + if (equals(utf8Key, UPWARDS_WHITE_ARROW_FROM_BAR) || equals(utf8Key, ASTERISIM) || + equals(utf8Key, (uint8_t*)PUNCTUATION_STRING) || equals(utf8Key, (uint8_t*)ALPHABET_STRING)) { + return; // ignore + } else if (equals(utf8Key, LEFT_ARROW)) { + scanCode = Qt::Key_Backspace; + keyString = "\x08"; + } else if (equals(utf8Key, RETURN_SYMBOL)) { + scanCode = Qt::Key_Return; + keyString = "\x0d"; + } else if (equals(utf8Key, LEFTWARD_WHITE_ARROW)) { + scanCode = Qt::Key_Left; + keyString = ""; + } else if (equals(utf8Key, RIGHTWARD_WHITE_ARROW)) { + scanCode = Qt::Key_Right; + keyString = ""; + } + + QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString); + QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString); + QCoreApplication::postEvent(eventHandler, pressEvent); + QCoreApplication::postEvent(eventHandler, releaseEvent); } - - QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString); - QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString); - QCoreApplication::postEvent(getEventHandler(), pressEvent); - QCoreApplication::postEvent(getEventHandler(), releaseEvent); } void RenderableWebEntityItem::emitScriptEvent(const QVariant& message) { - _webEntityAPIHelper->emitScriptEvent(message); + if (_webEntityAPIHelper) { + _webEntityAPIHelper->emitScriptEvent(message); + } } void RenderableWebEntityItem::setKeyboardRaised(bool raised) { @@ -424,5 +433,10 @@ void RenderableWebEntityItem::setKeyboardRaised(bool raised) { // raise the keyboard only while in HMD mode and it's being requested. bool value = AbstractViewStateInterface::instance()->isHMDMode() && raised; - _webSurface->getRootItem()->setProperty("keyboardRaised", QVariant(value)); + if (_webSurface) { + auto rootItem = _webSurface->getRootItem(); + if (rootItem) { + rootItem->setProperty("keyboardRaised", QVariant(value)); + } + } } From 46c0e8312717eb304a9bb861eba7cd37631b2086 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 30 Sep 2016 10:30:21 -0700 Subject: [PATCH 33/46] Restore "Are you sure" popup --- server-console/src/main.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index e297ca3276..8cabb5aaa4 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -577,12 +577,21 @@ function checkNewContent() { title: 'New home content', message: 'A newer version of the home content set is available.\nDo you wish to update?' }, function(idx) { - if (idx === 0) { - backupResourceDirectoriesAndRestart(); - } else { - // They don't want to update, mark content set as current - userConfig.set('homeContentLastModified', new Date()); - } + if (idx === 0) { + dialog.showMessageBox({ + type: 'warning', + buttons: ['Yes', 'No'], + title: 'Are you sure?', + message: 'Updating with the new content will remove all your current content and settings and place them in a backup folder.\nAre you sure?' + }, function(idx) { + if (idx === 0) { + backupResourceDirectoriesAndRestart(); + } + }); + } else { + // They don't want to update, mark content set as current + userConfig.set('homeContentLastModified', new Date()); + } }); } } From 12ecc02899d00655b09771806aad009caa740447 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Sat, 1 Oct 2016 13:40:31 -0700 Subject: [PATCH 34/46] Default popup choices to "No" --- server-console/src/main.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server-console/src/main.js b/server-console/src/main.js index 8cabb5aaa4..c090640ada 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -574,6 +574,7 @@ function checkNewContent() { dialog.showMessageBox({ type: 'question', buttons: ['Yes', 'No'], + defaultId: 1, title: 'New home content', message: 'A newer version of the home content set is available.\nDo you wish to update?' }, function(idx) { @@ -581,6 +582,7 @@ function checkNewContent() { dialog.showMessageBox({ type: 'warning', buttons: ['Yes', 'No'], + defaultId: 1, title: 'Are you sure?', message: 'Updating with the new content will remove all your current content and settings and place them in a backup folder.\nAre you sure?' }, function(idx) { From 2d87e3eb1ac32f0effc67781f9bc501a7dd87a21 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Sat, 1 Oct 2016 14:07:11 -0700 Subject: [PATCH 35/46] Fix CSS to wrap folder path --- server-console/src/content-update.css | 1 + 1 file changed, 1 insertion(+) diff --git a/server-console/src/content-update.css b/server-console/src/content-update.css index 9303e450b5..1ac32bd220 100644 --- a/server-console/src/content-update.css +++ b/server-console/src/content-update.css @@ -124,6 +124,7 @@ a:hover { overflow: hidden; float: left; position: relative; + word-wrap: break-word; } .col2 { From 7fe16442faea5bac5dc4107ad20763c914ad91c1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 1 Oct 2016 15:45:59 -0700 Subject: [PATCH 36/46] make calculateTextureInfo thread safe --- libraries/model/src/model/Material.cpp | 42 +++++++++++++++++++ libraries/model/src/model/Material.h | 17 +++++++- .../render-utils/src/MeshPartPayload.cpp | 32 -------------- libraries/render-utils/src/MeshPartPayload.h | 10 ++--- libraries/render-utils/src/Model.cpp | 3 +- 5 files changed, 62 insertions(+), 42 deletions(-) diff --git a/libraries/model/src/model/Material.cpp b/libraries/model/src/model/Material.cpp index 6e7968a571..4e01c4b866 100755 --- a/libraries/model/src/model/Material.cpp +++ b/libraries/model/src/model/Material.cpp @@ -44,8 +44,11 @@ Material::Material(const Material& material) : } Material& Material::operator= (const Material& material) { + QMutexLocker locker(&_textureMapsMutex); + _key = (material._key); _textureMaps = (material._textureMaps); + _hasCalculatedTextureInfo = false; // copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer Schema schema; @@ -112,6 +115,8 @@ void Material::setScattering(float scattering) { } void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) { + QMutexLocker locker(&_textureMapsMutex); + if (textureMap) { _key.setMapChannel(channel, (true)); _textureMaps[channel] = textureMap; @@ -119,6 +124,7 @@ void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textur _key.setMapChannel(channel, (false)); _textureMaps.erase(channel); } + _hasCalculatedTextureInfo = false; _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); @@ -173,6 +179,8 @@ void Material::resetOpacityMap() const { const TextureMapPointer Material::getTextureMap(MapChannel channel) const { + QMutexLocker locker(&_textureMapsMutex); + auto result = _textureMaps.find(channel); if (result != _textureMaps.end()) { return (result->second); @@ -180,3 +188,37 @@ const TextureMapPointer Material::getTextureMap(MapChannel channel) const { return TextureMapPointer(); } } + + +bool Material::calculateMaterialInfo() const { + if (!_hasCalculatedTextureInfo) { + QMutexLocker locker(&_textureMapsMutex); + + bool allTextures = true; // assume we got this... + _textureSize = 0; + _textureCount = 0; + + for (auto const &textureMapItem : _textureMaps) { + auto textureMap = textureMapItem.second; + if (textureMap) { + auto textureSoure = textureMap->getTextureSource(); + if (textureSoure) { + auto texture = textureSoure->getGPUTexture(); + if (texture) { + auto size = texture->getSize(); + _textureSize += size; + _textureCount++; + } else { + allTextures = false; + } + } else { + allTextures = false; + } + } else { + allTextures = false; + } + } + _hasCalculatedTextureInfo = allTextures; + } + return _hasCalculatedTextureInfo; +} diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index 304ef2e93b..8851ef4ce9 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -11,6 +11,8 @@ #ifndef hifi_model_Material_h #define hifi_model_Material_h +#include + #include #include @@ -324,7 +326,7 @@ public: // The texture map to channel association void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap); - const TextureMaps& getTextureMaps() const { return _textureMaps; } + const TextureMaps& getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe... const TextureMapPointer getTextureMap(MapChannel channel) const; // Albedo maps cannot have opacity detected until they are loaded @@ -344,12 +346,25 @@ public: }; const UniformBufferView& getTexMapArrayBuffer() const { return _texMapArrayBuffer; } + + int getTextureCount() const { calculateMaterialInfo(); return _textureCount; } + size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; } + bool hasTextureInfo() const { return _hasCalculatedTextureInfo; } + private: mutable MaterialKey _key; mutable UniformBufferView _schemaBuffer; mutable UniformBufferView _texMapArrayBuffer; TextureMaps _textureMaps; + + mutable QMutex _textureMapsMutex { QMutex::Recursive }; + mutable size_t _textureSize { 0 }; + mutable int _textureCount { 0 }; + mutable bool _hasCalculatedTextureInfo { false }; + bool calculateMaterialInfo() const; + + }; typedef std::shared_ptr< Material > MaterialPointer; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 50c0c869ff..2994c7fa5b 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -71,39 +71,8 @@ void MeshPartPayload::updateTransform(const Transform& transform, const Transfor void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) { _drawMaterial = drawMaterial; - calculateMaterialSize(); } -bool MeshPartPayload::calculateMaterialSize() { - bool allTextures = true; // assume we got this... - _materialTextureSize = 0; - _materialTextureCount = 0; - auto textureMaps = _drawMaterial->getTextureMaps(); - for (auto const &textureMapItem : textureMaps) { - auto textureMap = textureMapItem.second; - if (textureMap) { - auto textureSoure = textureMap->getTextureSource(); - if (textureSoure) { - auto texture = textureSoure->getGPUTexture(); - if (texture) { - //auto storedSize = texture->getStoredSize(); - auto size = texture->getSize(); - _materialTextureSize += size; - _materialTextureCount++; - } else { - allTextures = false; - } - } else { - allTextures = false; - } - } else { - allTextures = false; - } - } - return allTextures; -} - - ItemKey MeshPartPayload::getKey() const { ItemKey::Builder builder; builder.withTypeShape(); @@ -378,7 +347,6 @@ void ModelMeshPartPayload::initCache() { auto networkMaterial = _model->getGeometry()->getShapeMaterial(_shapeID); if (networkMaterial) { _drawMaterial = networkMaterial; - calculateMaterialSize(); } } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 3ecd8da03e..04b63874cd 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -66,13 +66,9 @@ public: bool _hasColorAttrib = false; size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } - size_t getMaterialTextureSize() { return _materialTextureSize; } - int getMaterialTextureCount() { return _materialTextureCount; } - bool calculateMaterialSize(); - -protected: - size_t _materialTextureSize { 0 }; - int _materialTextureCount { 0 }; + size_t getMaterialTextureSize() { return _drawMaterial ? _drawMaterial->getTextureSize() : 0; } + int getMaterialTextureCount() { return _drawMaterial ? _drawMaterial->getTextureCount() : 0; } + bool hasTextureInfo() const { return _drawMaterial ? _drawMaterial->hasTextureInfo() : false; } }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index f6caa7c3d3..28a1c3d579 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -168,10 +168,9 @@ void Model::calculateTextureInfo() { bool allTexturesLoaded = true; foreach(auto renderItem, _modelMeshRenderItemsSet) { auto meshPart = renderItem.get(); - bool allTexturesForThisMesh = meshPart->calculateMaterialSize(); - allTexturesLoaded = allTexturesLoaded & allTexturesForThisMesh; textureSize += meshPart->getMaterialTextureSize(); textureCount += meshPart->getMaterialTextureCount(); + allTexturesLoaded = allTexturesLoaded & meshPart->hasTextureInfo(); } _renderInfoTextureSize = textureSize; _renderInfoTextureCount = textureCount; From 546d4ff986da4e8d47a395a1d4ff8ce00e01e2bf Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 1 Oct 2016 22:23:56 -0700 Subject: [PATCH 37/46] remove mic and window resize notifications --- scripts/system/notifications.js | 41 ++++----------------------------- 1 file changed, 5 insertions(+), 36 deletions(-) diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index f3ba466342..d89b532f31 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -17,11 +17,9 @@ // keystroke: // // CTRL/s for snapshot. -// CTRL/m for mic mute and unmute. // System generated notifications: -// If Screen is resized. -// If mic is muted for any reason. +// Connection refused. // // To add a new System notification type: // @@ -92,16 +90,12 @@ var lodTextID = false; var NotificationType = { UNKNOWN: 0, - MUTE_TOGGLE: 1, - SNAPSHOT: 2, - WINDOW_RESIZE: 3, - LOD_WARNING: 4, - CONNECTION_REFUSED: 5, - EDIT_ERROR: 6, + SNAPSHOT: 1, + LOD_WARNING: 2, + CONNECTION_REFUSED: 3, + EDIT_ERROR: 4, properties: [ - { text: "Mute Toggle" }, { text: "Snapshot" }, - { text: "Window Resize" }, { text: "Level of Detail" }, { text: "Connection Refused" }, { text: "Edit error" } @@ -446,19 +440,6 @@ function wordWrap(str) { return stringDivider(str, 43.0, "\n"); } -// This fires a notification on window resize -function checkSize() { - if ((Window.innerWidth !== ourWidth) || (Window.innerHeight !== ourHeight)) { - var windowResize = "Window has been resized"; - ourWidth = Window.innerWidth; - ourHeight = Window.innerHeight; - windowDimensions = Controller.getViewportDimensions(); - overlayLocationX = (windowDimensions.x - (width + 60.0)); - buttonLocationX = overlayLocationX + (width - 35.0); - createNotification(windowResize, NotificationType.WINDOW_RESIZE); - } -} - function update() { var nextOverlay, noticeOut, @@ -480,7 +461,6 @@ function update() { frame += 1; if ((frame % 60.0) === 0) { // only update once a second - checkSize(); // checks for size change to trigger windowResize notification locationY = 20.0; for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); @@ -533,16 +513,6 @@ function isStartingUp() { return startingUp; } -// Triggers mic mute notification -function onMuteStateChanged() { - var muteState, - muteString; - - muteState = AudioDevice.getMuted() ? "muted" : "unmuted"; - muteString = "Microphone is now " + muteState; - createNotification(muteString, NotificationType.MUTE_TOGGLE); -} - function onDomainConnectionRefused(reason) { createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED); } @@ -653,7 +623,6 @@ LODManager.LODDecreased.connect(function() { } }); -AudioDevice.muteToggled.connect(onMuteStateChanged); Controller.keyPressEvent.connect(keyPressEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); From fab0f8019daf35b9b559b8c1852937833c241747 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Sun, 2 Oct 2016 17:33:07 -0700 Subject: [PATCH 38/46] Ask windows to not try to be helpful --- server-console/src/main.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index c090640ada..b8e661ffbd 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -575,16 +575,20 @@ function checkNewContent() { type: 'question', buttons: ['Yes', 'No'], defaultId: 1, + cancelId: 1, title: 'New home content', - message: 'A newer version of the home content set is available.\nDo you wish to update?' + message: 'A newer version of the home content set is available.\nDo you wish to update?', + noLink: true, }, function(idx) { if (idx === 0) { dialog.showMessageBox({ type: 'warning', buttons: ['Yes', 'No'], defaultId: 1, + cancelId: 1, title: 'Are you sure?', - message: 'Updating with the new content will remove all your current content and settings and place them in a backup folder.\nAre you sure?' + message: 'Updating with the new content will remove all your current content and settings and place them in a backup folder.\nAre you sure?', + noLink: true, }, function(idx) { if (idx === 0) { backupResourceDirectoriesAndRestart(); From 0df69b8e15a97c28682d8015182f35c7374fdb6e Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 3 Oct 2016 10:06:58 -0700 Subject: [PATCH 39/46] Protecting the usage of the defaultSkyboxTexture in case its creation has failed to avoid crash on the first usage, removing an uncorrect assert in GL45BackendTexture that would trigger in debug --- interface/src/Application.cpp | 8 ++++++-- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 2 -- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3890eae4c3..894471a251 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4361,8 +4361,12 @@ namespace render { auto scene = DependencyManager::get()->getStage(); auto sceneKeyLight = scene->getKeyLight(); auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture(); - sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance()); - sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); + if (defaultSkyboxAmbientTexture) { + sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance()); + sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); + } else { + qWarning() << "Failed to get a valid Default SKybox Ambient Texture ? probably because it couldn't be find during initialization step"; + } // fall through: render defaults skybox } else { break; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 3e6e53ffc3..36b7b4886f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -511,9 +511,7 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { _minMip = newMinMip; // Re-sync the sampler to force access to the new mip level syncSampler(); - size_t oldSize = _size; updateSize(); - Q_ASSERT(_size > oldSize); // Re-insert into the texture-by-mips map if appropriate From ee801f4cc2ab13176392872dd75684896274726a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 3 Oct 2016 10:29:33 -0700 Subject: [PATCH 40/46] initial domain-check script --- .../tests/performance/domain-check.js | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 scripts/developer/tests/performance/domain-check.js diff --git a/scripts/developer/tests/performance/domain-check.js b/scripts/developer/tests/performance/domain-check.js new file mode 100644 index 0000000000..eceffa278b --- /dev/null +++ b/scripts/developer/tests/performance/domain-check.js @@ -0,0 +1,201 @@ +"use strict"; +/*jslint vars: true, plusplus: true*/ +/*globals Script, MyAvatar, Quat, Render, ScriptDiscoveryService, Window, LODManager, Entities, print*/ +// +// loadedMachine.js +// scripts/developer/tests/ +// +// Created by Howard Stearns on 6/6/16. +// Copyright 2016 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 +// +// Confirms that the specified domain is operating within specified constraints. + +var MINIMUM_DESKTOP_FRAMERATE = 57; // frames per second +var MINIMUM_HMD_FRAMERATE = 86; +var EXPECTED_DESKTOP_FRAMERATE = 60; +var EXPECTED_HMD_FRAMERATE = 90; +var MAXIMUM_LOAD_TIME = 60; // seconds +var MINIMUM_AVATARS = 25; // FIXME: not implemented yet. Requires agent scripts. Idea is to have them organize themselves to the right number. + +var version = 1; +function debug() { + print.apply(null, [].concat.apply(['hrs fixme', version], [].map.call(arguments, JSON.stringify))); +} + +var emptyishPlace = 'empty'; +var cachePlaces = ['localhost', 'Welcome']; +var isInCachePlace = cachePlaces.indexOf(location.hostname) >= 0; +var defaultPlace = isInCachePlace ? 'Playa' : location.hostname; +var prompt = "domain-check.js version " + version + "\n\nWhat place should we enter?"; +debug(cachePlaces, isInCachePlace, defaultPlace, prompt); +var entryPlace = Window.prompt(prompt, defaultPlace); + +var fail = false, results = ""; +function addResult(label, actual, minimum, maximum) { + if ((minimum !== undefined) && (actual < minimum)) { + fail = true; + } + if ((maximum !== undefined) && (actual > maximum)) { + fail = true; + } + results += "\n" + label + ": " + actual + " (" + ((100 * actual) / (maximum || minimum)).toFixed(0) + "%)"; +} +function giveReport() { + Window.alert(entryPlace + (fail ? " FAILED" : " OK") + "\n" + results); +} + +// Tests are performed domain-wide, at full LOD +var initialLodIsAutomatic = LODManager.getAutomaticLODAdjust(); +var LOD = 32768 * 400; +LODManager.setAutomaticLODAdjust(false); +LODManager.setOctreeSizeScale(LOD); +Script.scriptEnding.connect(function () { LODManager.setAutomaticLODAdjust(initialLodIsAutomatic); }); + +function startTwirl(targetRotation, degreesPerUpdate, interval, strafeDistance, optionalCallback) { + var initialRotation = Quat.safeEulerAngles(MyAvatar.orientation).y; + var accumulatedRotation = 0; + function tick() { + MyAvatar.orientation = Quat.fromPitchYawRollDegrees(0, accumulatedRotation + initialRotation, 0); + if (strafeDistance) { + MyAvatar.position = Vec3.sum(MyAvatar.position, Vec3.multiply(strafeDistance, Quat.getRight(MyAvatar.orientation))); + } + accumulatedRotation += degreesPerUpdate; + if (accumulatedRotation >= targetRotation) { + return optionalCallback && optionalCallback(); + } + Script.setTimeout(tick, interval); + } + tick(); +} + +function doLoad(place, continuationWithLoadTime) { // Go to place and call continuationWithLoadTime(loadTimeInSeconds) + var start = Date.now(), timeout, onDownloadUpdate, finishedTwirl = false, loadTime; + function clearHandlers() { + debug('clearHandlers'); + Stats.downloadsPendingChanged.disconnect(onDownloadUpdate); + Stats.downloadsChanged.disconnect(onDownloadUpdate); + } + function waitForLoad(flag) { + debug('entry', place, 'initial downloads/pending', Stats.downloads, Stats.downloadsPending); + location.hostChanged.disconnect(waitForLoad); + timeout = Script.setTimeout(function () { + debug('downloads timeout', Date()); + clearHandlers(); + Window.alert("Timeout during " + place + " load. FAILED"); + Script.stop(); + }, MAXIMUM_LOAD_TIME * 1000); + startTwirl(360, 6, 90, null, function () { + finishedTwirl = true; + if (loadTime) { + continuationWithLoadTime(loadTime); + } + }); + Stats.downloadsPendingChanged.connect(onDownloadUpdate); + Stats.downloadsChanged.connect(onDownloadUpdate); + } + function isLoading() { + // FIXME: This tells us when download are completed, but it doesn't tell us when the objects are parsed and loaded. + // We really want something like _physicsEnabled, but that isn't signalled. + return Stats.downloads || Stats.downloadsPending; + } + onDownloadUpdate = function onDownloadUpdate() { + debug('update downloads/pending', Stats.downloads, Stats.downloadsPending); + if (isLoading()) { + return; + } + Script.clearTimeout(timeout); + clearHandlers(); + loadTime = (Date.now() - start) / 1000; + if (finishedTwirl) { + continuationWithLoadTime(loadTime); + } + }; + + function doit() { + debug('go', place); + location.hostChanged.connect(waitForLoad); + location.handleLookupString(place); + } + if (location.placename.toLowerCase() === place.toLowerCase()) { + location.handleLookupString(emptyishPlace); + Script.setTimeout(doit, 1000); + } else { + doit(); + } +} + +var config = Render.getConfig("Stats"); +function doRender(continuation) { + var start = Date.now(), frames = 0; + function onNewStats() { // Accumulates frames on signal during load test + frames++; + } + config.newStats.connect(onNewStats); + startTwirl(720, 1, 15, 0.08, function () { + var end = Date.now(); + config.newStats.disconnect(onNewStats); + addResult('frame rate', 1000 * frames / (end - start), + HMD.active ? MINIMUM_HMD_FRAMERATE : MINIMUM_DESKTOP_FRAMERATE, + HMD.active ? EXPECTED_HMD_FRAMERATE : EXPECTED_DESKTOP_FRAMERATE); + continuation(); + }); +} + +function maybePrepareCache(continuation) { + var prepareCache = Window.confirm("Prepare cache?\n\n\ +Should we start with all and only those items cached that are encountered when visiting:\n" + cachePlaces.join(', ') + "\n\ +If 'yes', cache will be cleared and we will visit these two, with a turn in each, and wait for everything to be loaded.\n\ +You would want to say 'no' (and make other preparations) if you were testing these places."); + + if (prepareCache) { + location.handleLookupString(emptyishPlace); + Window.alert("Please do menu Edit->Reload Content (Clears all caches) and THEN press 'ok'."); + function loadNext() { + var place = cachePlaces.shift(); + doLoad(place, function (prepTime) { + debug(place, 'ready', prepTime); + if (cachePlaces.length) { + loadNext(); + } else { + continuation(); + } + }); + } + loadNext(); + } else { + continuation(); + } +} + +function maybeRunTribbles(continuation) { + if (Window.confirm("Run tribbles?\n\n\ +At most, only one participant should say yes.")) { + Script.load('http://howard-stearns.github.io/models/scripts/tests/performance/tribbles.js'); // FIXME: replace with AWS + Script.setTimeout(continuation, 3000); + } else { + continuation(); + } +} + +if (!entryPlace) { + Window.alert("domain-check.js cancelled"); + Script.stop(); +} else { + maybePrepareCache(function (prepTime) { + debug('cache ready', prepTime); + doLoad(entryPlace, function (loadTime) { + addResult("load time", loadTime, undefined, MAXIMUM_LOAD_TIME); + maybeRunTribbles(function () { + doRender(function () { + giveReport(); + Script.stop(); + }); + }); + }); + }); +} + +Script.scriptEnding.connect(function () { print("domain-check completed"); }); From 5adedbbb8bfaec1177b406bb0b477a0eb9759534 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 3 Oct 2016 11:27:15 -0700 Subject: [PATCH 41/46] replace the simple log by a unique log --- interface/src/Application.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 894471a251..e11f5159d8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4365,7 +4365,9 @@ namespace render { sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance()); sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); } else { - qWarning() << "Failed to get a valid Default SKybox Ambient Texture ? probably because it couldn't be find during initialization step"; + static QString repeatedMessage + = LogHandler::getInstance().addRepeatedMessageRegex( + "Failed to get a valid Default Skybox Ambient Texture ? probably because it couldn't be find during initialization step"); } // fall through: render defaults skybox } else { From 0189e3aea7a08e66be47103472f8dfcf62f95012 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 3 Oct 2016 11:29:17 -0700 Subject: [PATCH 42/46] Github is borked ? --- interface/src/Application.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e11f5159d8..ec452cb260 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2170,7 +2170,7 @@ bool Application::event(QEvent* event) { // handle custom URL if (event->type() == QEvent::FileOpen) { - QFileOpenEvent* fileEvent = static_cast(event); + QFileOpenEvent* fileEvent = static_cast(event); QUrl url = fileEvent->url(); @@ -4365,8 +4365,7 @@ namespace render { sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance()); sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); } else { - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex( + static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex( "Failed to get a valid Default Skybox Ambient Texture ? probably because it couldn't be find during initialization step"); } // fall through: render defaults skybox From dafb5c5bab913ae1b1ad773fe18372ec7cb87109 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 15:11:56 -0700 Subject: [PATCH 43/46] Add processor information to launch user activity event --- interface/src/Application.cpp | 13 ++- libraries/shared/src/SharedUtil.cpp | 146 +++++++++++++++++++++++++++- libraries/shared/src/SharedUtil.h | 11 +++ 3 files changed, 165 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ec452cb260..c170e38a27 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -815,7 +815,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : { "gl_version", glContextData["version"] }, { "gl_vender", glContextData["vendor"] }, { "gl_sl_version", glContextData["slVersion"] }, - { "gl_renderer", glContextData["renderer"] } + { "gl_renderer", glContextData["renderer"] }, + { "ideal_thread_count", QThread::idealThreadCount() } }; auto macVersion = QSysInfo::macVersion(); if (macVersion != QSysInfo::MV_None) { @@ -825,6 +826,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : if (windowsVersion != QSysInfo::WV_None) { properties["os_win_version"] = QSysInfo::windowsVersion(); } + + ProcessorInfo procInfo; + if (getProcessorInfo(procInfo)) { + properties["processor_core_count"] = procInfo.numProcessorCores; + properties["logical_processor_count"] = procInfo.numLogicalProcessors; + properties["processor_l1_cache_count"] = procInfo.numProcessorCachesL1; + properties["processor_l2_cache_count"] = procInfo.numProcessorCachesL2; + properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3; + } + UserActivityLogger::getInstance().logAction("launch", properties); _connectionMonitor.init(); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index bb5d326851..efb032ce75 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -22,11 +22,8 @@ #include -#ifdef _WIN32 -#include -#endif - #ifdef Q_OS_WIN +#include #include "CPUIdent.h" #include #endif @@ -877,3 +874,144 @@ bool getMemoryInfo(MemoryInfo& info) { return false; } + +// Largely taken from: https://msdn.microsoft.com/en-us/library/windows/desktop/ms683194(v=vs.85).aspx + +#ifdef Q_OS_WIN +using LPFN_GLPI = BOOL(WINAPI*)( + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, + PDWORD); + +DWORD CountSetBits(ULONG_PTR bitMask) +{ + DWORD LSHIFT = sizeof(ULONG_PTR) * 8 - 1; + DWORD bitSetCount = 0; + ULONG_PTR bitTest = (ULONG_PTR)1 << LSHIFT; + DWORD i; + + for (i = 0; i <= LSHIFT; ++i) + { + bitSetCount += ((bitMask & bitTest) ? 1 : 0); + bitTest /= 2; + } + + return bitSetCount; +} +#endif + +bool getProcessorInfo(ProcessorInfo& info) { + +#ifdef Q_OS_WIN + LPFN_GLPI glpi; + bool done = false; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL; + DWORD returnLength = 0; + DWORD logicalProcessorCount = 0; + DWORD numaNodeCount = 0; + DWORD processorCoreCount = 0; + DWORD processorL1CacheCount = 0; + DWORD processorL2CacheCount = 0; + DWORD processorL3CacheCount = 0; + DWORD processorPackageCount = 0; + DWORD byteOffset = 0; + PCACHE_DESCRIPTOR Cache; + + glpi = (LPFN_GLPI)GetProcAddress( + GetModuleHandle(TEXT("kernel32")), + "GetLogicalProcessorInformation"); + if (nullptr == glpi) { + qDebug() << "GetLogicalProcessorInformation is not supported."; + return false; + } + + while (!done) { + DWORD rc = glpi(buffer, &returnLength); + + if (FALSE == rc) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + if (buffer) { + free(buffer); + } + + buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc( + returnLength); + + if (NULL == buffer) { + qDebug() << "Error: Allocation failure"; + return (2); + } + } else { + qDebug() << "Error " << GetLastError(); + return (3); + } + } else { + done = true; + } + } + + ptr = buffer; + + while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) { + switch (ptr->Relationship) { + case RelationNumaNode: + // Non-NUMA systems report a single record of this type. + numaNodeCount++; + break; + + case RelationProcessorCore: + processorCoreCount++; + + // A hyperthreaded core supplies more than one logical processor. + logicalProcessorCount += CountSetBits(ptr->ProcessorMask); + break; + + case RelationCache: + // Cache data is in ptr->Cache, one CACHE_DESCRIPTOR structure for each cache. + Cache = &ptr->Cache; + if (Cache->Level == 1) { + processorL1CacheCount++; + } else if (Cache->Level == 2) { + processorL2CacheCount++; + } else if (Cache->Level == 3) { + processorL3CacheCount++; + } + break; + + case RelationProcessorPackage: + // Logical processors share a physical package. + processorPackageCount++; + break; + + default: + qDebug() << "\nError: Unsupported LOGICAL_PROCESSOR_RELATIONSHIP value.\n"; + break; + } + byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + ptr++; + } + + qDebug() << "GetLogicalProcessorInformation results:"; + qDebug() << "Number of NUMA nodes:" << numaNodeCount; + qDebug() << "Number of physical processor packages:" << processorPackageCount; + qDebug() << "Number of processor cores:" << processorCoreCount; + qDebug() << "Number of logical processors:" << logicalProcessorCount; + qDebug() << "Number of processor L1/L2/L3 caches:" + << processorL1CacheCount + << "/" << processorL2CacheCount + << "/" << processorL3CacheCount; + + info.numPhysicalProcessorPackages = processorPackageCount; + info.numProcessorCores = processorCoreCount; + info.numLogicalProcessors = logicalProcessorCount; + info.numProcessorCachesL1 = processorL1CacheCount; + info.numProcessorCachesL2 = processorL2CacheCount; + info.numProcessorCachesL3 = processorL3CacheCount; + + free(buffer); + + return true; +#endif + + return false; +} \ No newline at end of file diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index f3e5625484..863c4d6dc5 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -214,4 +214,15 @@ struct MemoryInfo { bool getMemoryInfo(MemoryInfo& info); +struct ProcessorInfo { + int32_t numPhysicalProcessorPackages; + int32_t numProcessorCores; + int32_t numLogicalProcessors; + int32_t numProcessorCachesL1; + int32_t numProcessorCachesL2; + int32_t numProcessorCachesL3; +}; + +bool getProcessorInfo(ProcessorInfo& info); + #endif // hifi_SharedUtil_h From b5eb943c1d906017045024c899b8d9309d9694de Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 15:12:14 -0700 Subject: [PATCH 44/46] Add hmd stats to stats event --- interface/src/Application.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c170e38a27..ec6eed1c05 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1134,6 +1134,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : auto displayPlugin = qApp->getActiveDisplayPlugin(); properties["fps"] = _frameCounter.rate(); + properties["target_frame_rate"] = getTargetFrameRate(); properties["present_rate"] = displayPlugin->presentRate(); properties["new_frame_present_rate"] = displayPlugin->newFramePresentRate(); properties["dropped_frame_rate"] = displayPlugin->droppedFrameRate(); @@ -1175,6 +1176,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : properties["deleted_entity_cnt"] = entityActivityTracking.deletedEntityCount; properties["edited_entity_cnt"] = entityActivityTracking.editedEntityCount; + properties["active_display_plugin"] = getActiveDisplayPlugin()->getName(); + properties["using_hmd"] = isHMDMode(); + auto hmdHeadPose = getHMDSensorPose(); properties["hmd_head_pose_changed"] = isHMDMode() && (hmdHeadPose != lastHMDHeadPose); lastHMDHeadPose = hmdHeadPose; From e213a478141197aae5218d381eabbba5d52332ce Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 15:19:03 -0700 Subject: [PATCH 45/46] Fix incorrect return values in getProcessorInfo --- libraries/shared/src/SharedUtil.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index efb032ce75..c440acdea2 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -939,11 +939,11 @@ bool getProcessorInfo(ProcessorInfo& info) { if (NULL == buffer) { qDebug() << "Error: Allocation failure"; - return (2); + return false; } } else { qDebug() << "Error " << GetLastError(); - return (3); + return false; } } else { done = true; From 0b2def6013156cc8fdf23acbfe02df63a0f42004 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 15:23:39 -0700 Subject: [PATCH 46/46] Fix style error --- libraries/shared/src/SharedUtil.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index c440acdea2..287c15e3a2 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -889,8 +889,7 @@ DWORD CountSetBits(ULONG_PTR bitMask) ULONG_PTR bitTest = (ULONG_PTR)1 << LSHIFT; DWORD i; - for (i = 0; i <= LSHIFT; ++i) - { + for (i = 0; i <= LSHIFT; ++i) { bitSetCount += ((bitMask & bitTest) ? 1 : 0); bitTest /= 2; }