From 64ecb06088bc0bbcb57b3a4f698e265e01865bb8 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Sep 2016 10:18:54 -0700 Subject: [PATCH 01/19] 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 c849307b3dede646b8baaa70e71353e7740c8eb3 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 29 Sep 2016 15:22:21 -0700 Subject: [PATCH 02/19] 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 a2da749f52d435b4a57856e4de8a0a9620bb05eb Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 28 Sep 2016 19:17:24 -0700 Subject: [PATCH 03/19] 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 04/19] 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 05/19] 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 6f725e8c02d880ced1fa1aa6d4a4386403c2f492 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Sep 2016 15:49:47 -0700 Subject: [PATCH 06/19] 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 07/19] 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 9590d2eef9b225fc4ab464f28ad59ec234fa082d Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 3 Oct 2016 09:08:32 -0700 Subject: [PATCH 08/19] Fix support for audio when input rate != output rate --- libraries/audio-client/src/AudioClient.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 1250f74061..d867bf8b3c 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -817,6 +817,13 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { return; } + // NOTE: we assume the inputFormat and the outputFormat are the same, since on any modern + // multimedia OS they should be. If there is a device that this is not true for, we can + // add back support to do resampling. + if (_inputFormat.sampleRate() != _outputFormat.sampleRate()) { + return; + } + // if this person wants local loopback add that to the locally injected audio // if there is reverb apply it to local audio and substract the origin samples @@ -833,11 +840,6 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } } - // NOTE: we assume the inputFormat and the outputFormat are the same, since on any modern - // multimedia OS they should be. If there is a device that this is not true for, we can - // add back support to do resampling. - Q_ASSERT(_inputFormat.sampleRate() == _outputFormat.sampleRate()); - static QByteArray loopBackByteArray; int numInputSamples = inputByteArray.size() / AudioConstants::SAMPLE_SIZE; From 573d5898ccc8a010ee6a1728d105ad520d261f46 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 3 Oct 2016 13:46:55 -0700 Subject: [PATCH 09/19] Make static control packets class members --- libraries/networking/src/udt/Connection.cpp | 96 ++++++++++----------- libraries/networking/src/udt/Connection.h | 28 +++--- 2 files changed, 65 insertions(+), 59 deletions(-) diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index af70295840..dcceef0fdc 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -45,6 +45,20 @@ Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::uniq // set the initial RTT and flow window size on congestion control object _congestionControl->setRTT(_rtt); _congestionControl->setMaxCongestionWindowSize(_flowWindowSize); + + // Setup packets + static const int ACK_PACKET_PAYLOAD_BYTES = sizeof(_lastSentACK) + sizeof(_currentACKSubSequenceNumber) + + sizeof(_rtt) + sizeof(int32_t) + sizeof(int32_t) + sizeof(int32_t); + static const int LIGHT_ACK_PACKET_PAYLOAD_BYTES = sizeof(SequenceNumber); + static const int ACK2_PAYLOAD_BYTES = sizeof(SequenceNumber); + static const int NAK_PACKET_PAYLOAD_BYTES = 2 * sizeof(SequenceNumber); + static const int HANDSHAKE_ACK_PAYLOAD_BYTES = sizeof(SequenceNumber); + + _ackPacket = ControlPacket::create(ControlPacket::ACK, ACK_PACKET_PAYLOAD_BYTES); + _lightACKPacket = ControlPacket::create(ControlPacket::LightACK, LIGHT_ACK_PACKET_PAYLOAD_BYTES); + _ack2Packet = ControlPacket::create(ControlPacket::ACK2, ACK2_PAYLOAD_BYTES); + _lossReport = ControlPacket::create(ControlPacket::NAK, NAK_PACKET_PAYLOAD_BYTES); + _handshakeACK = ControlPacket::create(ControlPacket::HandshakeACK, HANDSHAKE_ACK_PAYLOAD_BYTES); } Connection::~Connection() { @@ -279,25 +293,22 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) { // update the last sent ACK _lastSentACK = nextACKNumber; - - // setup the ACK packet, make it static so we can re-use it - static const int ACK_PACKET_PAYLOAD_BYTES = sizeof(_lastSentACK) + sizeof(_currentACKSubSequenceNumber) - + sizeof(_rtt) + sizeof(int32_t) + sizeof(int32_t) + sizeof(int32_t); - static auto ackPacket = ControlPacket::create(ControlPacket::ACK, ACK_PACKET_PAYLOAD_BYTES); - ackPacket->reset(); // We need to reset it every time. + + + _ackPacket->reset(); // We need to reset it every time. // pack in the ACK sub-sequence number - ackPacket->writePrimitive(++_currentACKSubSequenceNumber); + _ackPacket->writePrimitive(++_currentACKSubSequenceNumber); // pack in the ACK number - ackPacket->writePrimitive(nextACKNumber); + _ackPacket->writePrimitive(nextACKNumber); // pack in the RTT and variance - ackPacket->writePrimitive(_rtt); + _ackPacket->writePrimitive(_rtt); // pack the available buffer size, in packets // in our implementation we have no hard limit on receive buffer size, send the default value - ackPacket->writePrimitive((int32_t) udt::CONNECTION_RECEIVE_BUFFER_SIZE_PACKETS); + _ackPacket->writePrimitive((int32_t) udt::CONNECTION_RECEIVE_BUFFER_SIZE_PACKETS); if (wasCausedBySyncTimeout) { // grab the up to date packet receive speed and estimated bandwidth @@ -309,15 +320,15 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) { _stats.recordEstimatedBandwidth(estimatedBandwidth); // pack in the receive speed and estimatedBandwidth - ackPacket->writePrimitive(packetReceiveSpeed); - ackPacket->writePrimitive(estimatedBandwidth); + _ackPacket->writePrimitive(packetReceiveSpeed); + _ackPacket->writePrimitive(estimatedBandwidth); } // record this as the last ACK send time lastACKSendTime = p_high_resolution_clock::now(); // have the socket send off our packet - _parentSocket->writeBasePacket(*ackPacket, _destination); + _parentSocket->writeBasePacket(*_ackPacket, _destination); Q_ASSERT_X(_sentACKs.empty() || _sentACKs.back().first + 1 == _currentACKSubSequenceNumber, "Connection::sendACK", "Adding an invalid ACK to _sentACKs"); @@ -339,35 +350,27 @@ void Connection::sendLightACK() { return; } - // create the light ACK packet, make it static so we can re-use it - static const int LIGHT_ACK_PACKET_PAYLOAD_BYTES = sizeof(SequenceNumber); - static auto lightACKPacket = ControlPacket::create(ControlPacket::LightACK, LIGHT_ACK_PACKET_PAYLOAD_BYTES); - // reset the lightACKPacket before we go to write the ACK to it - lightACKPacket->reset(); + _lightACKPacket->reset(); // pack in the ACK - lightACKPacket->writePrimitive(nextACKNumber); + _lightACKPacket->writePrimitive(nextACKNumber); // have the socket send off our packet immediately - _parentSocket->writeBasePacket(*lightACKPacket, _destination); + _parentSocket->writeBasePacket(*_lightACKPacket, _destination); _stats.record(ConnectionStats::Stats::SentLightACK); } void Connection::sendACK2(SequenceNumber currentACKSubSequenceNumber) { - // setup a static ACK2 packet we will re-use - static const int ACK2_PAYLOAD_BYTES = sizeof(SequenceNumber); - static auto ack2Packet = ControlPacket::create(ControlPacket::ACK2, ACK2_PAYLOAD_BYTES); - // reset the ACK2 Packet before writing the sub-sequence number to it - ack2Packet->reset(); + _ack2Packet->reset(); // write the sub sequence number for this ACK2 - ack2Packet->writePrimitive(currentACKSubSequenceNumber); + _ack2Packet->writePrimitive(currentACKSubSequenceNumber); // send the ACK2 packet - _parentSocket->writeBasePacket(*ack2Packet, _destination); + _parentSocket->writeBasePacket(*_ack2Packet, _destination); // update the last sent ACK2 and the last ACK2 send time _lastSentACK2 = currentACKSubSequenceNumber; @@ -376,19 +379,16 @@ void Connection::sendACK2(SequenceNumber currentACKSubSequenceNumber) { } void Connection::sendNAK(SequenceNumber sequenceNumberRecieved) { - // create the loss report packet, make it static so we can re-use it - static const int NAK_PACKET_PAYLOAD_BYTES = 2 * sizeof(SequenceNumber); - static auto lossReport = ControlPacket::create(ControlPacket::NAK, NAK_PACKET_PAYLOAD_BYTES); - lossReport->reset(); // We need to reset it every time. + _lossReport->reset(); // We need to reset it every time. // pack in the loss report - lossReport->writePrimitive(_lastReceivedSequenceNumber + 1); + _lossReport->writePrimitive(_lastReceivedSequenceNumber + 1); if (_lastReceivedSequenceNumber + 1 != sequenceNumberRecieved - 1) { - lossReport->writePrimitive(sequenceNumberRecieved - 1); + _lossReport->writePrimitive(sequenceNumberRecieved - 1); } // have the parent socket send off our packet immediately - _parentSocket->writeBasePacket(*lossReport, _destination); + _parentSocket->writeBasePacket(*_lossReport, _destination); // record our last NAK time _lastNAKTime = p_high_resolution_clock::now(); @@ -519,7 +519,7 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in return !wasDuplicate; } -void Connection::processControl(std::unique_ptr controlPacket) { +void Connection::processControl(ControlPacketPointer controlPacket) { // Simple dispatch to control packets processing methods based on their type. @@ -577,7 +577,7 @@ void Connection::processControl(std::unique_ptr controlPacket) { } } -void Connection::processACK(std::unique_ptr controlPacket) { +void Connection::processACK(ControlPacketPointer controlPacket) { // read the ACK sub-sequence number SequenceNumber currentACKSubSequenceNumber; controlPacket->readPrimitive(¤tACKSubSequenceNumber); @@ -678,7 +678,7 @@ void Connection::processACK(std::unique_ptr controlPacket) { _stats.record(ConnectionStats::Stats::ProcessedACK); } -void Connection::processLightACK(std::unique_ptr controlPacket) { +void Connection::processLightACK(ControlPacketPointer controlPacket) { // read the ACKed sequence number SequenceNumber ack; controlPacket->readPrimitive(&ack); @@ -702,7 +702,7 @@ void Connection::processLightACK(std::unique_ptr controlPacket) { _stats.record(ConnectionStats::Stats::ReceivedLightACK); } -void Connection::processACK2(std::unique_ptr controlPacket) { +void Connection::processACK2(ControlPacketPointer controlPacket) { // pull the sub sequence number from the packet SequenceNumber subSequenceNumber; controlPacket->readPrimitive(&subSequenceNumber); @@ -742,7 +742,7 @@ void Connection::processACK2(std::unique_ptr controlPacket) { _stats.record(ConnectionStats::Stats::ReceivedACK2); } -void Connection::processNAK(std::unique_ptr controlPacket) { +void Connection::processNAK(ControlPacketPointer controlPacket) { // read the loss report SequenceNumber start, end; controlPacket->readPrimitive(&start); @@ -764,7 +764,7 @@ void Connection::processNAK(std::unique_ptr controlPacket) { _stats.record(ConnectionStats::Stats::ReceivedNAK); } -void Connection::processHandshake(std::unique_ptr controlPacket) { +void Connection::processHandshake(ControlPacketPointer controlPacket) { SequenceNumber initialSequenceNumber; controlPacket->readPrimitive(&initialSequenceNumber); @@ -782,18 +782,16 @@ void Connection::processHandshake(std::unique_ptr controlPacket) _lastReceivedSequenceNumber = initialSequenceNumber - 1; _lastSentACK = initialSequenceNumber - 1; } - - // immediately respond with a handshake ACK - static auto handshakeACK = ControlPacket::create(ControlPacket::HandshakeACK, sizeof(SequenceNumber)); - handshakeACK->seek(0); - handshakeACK->writePrimitive(initialSequenceNumber); - _parentSocket->writeBasePacket(*handshakeACK, _destination); + + _handshakeACK->reset(); + _handshakeACK->writePrimitive(initialSequenceNumber); + _parentSocket->writeBasePacket(*_handshakeACK, _destination); // indicate that handshake has been received _hasReceivedHandshake = true; } -void Connection::processHandshakeACK(std::unique_ptr controlPacket) { +void Connection::processHandshakeACK(ControlPacketPointer controlPacket) { // if we've decided to clean up the send queue then this handshake ACK should be ignored, it's useless if (_sendQueue) { SequenceNumber initialSequenceNumber; @@ -807,7 +805,7 @@ void Connection::processHandshakeACK(std::unique_ptr controlPacke } } -void Connection::processTimeoutNAK(std::unique_ptr controlPacket) { +void Connection::processTimeoutNAK(ControlPacketPointer controlPacket) { // Override SendQueue's LossList with the timeout NAK list getSendQueue().overrideNAKListFromPacket(*controlPacket); @@ -817,7 +815,7 @@ void Connection::processTimeoutNAK(std::unique_ptr controlPacket) _stats.record(ConnectionStats::Stats::ReceivedTimeoutNAK); } -void Connection::processProbeTail(std::unique_ptr controlPacket) { +void Connection::processProbeTail(ControlPacketPointer controlPacket) { if (((uint32_t) _lastReceivedSequenceNumber & 0xF) == 0) { // this is the second packet in a probe set so we can estimate bandwidth // the sender sent this to us in lieu of sending new data (because they didn't have any) diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index 08a2df9b97..d6121d47b2 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -55,6 +55,7 @@ public: using SequenceNumberTimePair = std::pair; using ACKListPair = std::pair; using SentACKList = std::list; + using ControlPacketPointer = std::unique_ptr; Connection(Socket* parentSocket, HifiSockAddr destination, std::unique_ptr congestionControl); ~Connection(); @@ -66,7 +67,7 @@ public: // return indicates if this packet should be processed bool processReceivedSequenceNumber(SequenceNumber sequenceNumber, int packetSize, int payloadSize); - void processControl(std::unique_ptr controlPacket); + void processControl(ControlPacketPointer controlPacket); void queueReceivedMessagePacket(std::unique_ptr packet); @@ -96,14 +97,14 @@ private: void sendNAK(SequenceNumber sequenceNumberRecieved); void sendTimeoutNAK(); - void processACK(std::unique_ptr controlPacket); - void processLightACK(std::unique_ptr controlPacket); - void processACK2(std::unique_ptr controlPacket); - void processNAK(std::unique_ptr controlPacket); - void processTimeoutNAK(std::unique_ptr controlPacket); - void processHandshake(std::unique_ptr controlPacket); - void processHandshakeACK(std::unique_ptr controlPacket); - void processProbeTail(std::unique_ptr controlPacket); + void processACK(ControlPacketPointer controlPacket); + void processLightACK(ControlPacketPointer controlPacket); + void processACK2(ControlPacketPointer controlPacket); + void processNAK(ControlPacketPointer controlPacket); + void processTimeoutNAK(ControlPacketPointer controlPacket); + void processHandshake(ControlPacketPointer controlPacket); + void processHandshakeACK(ControlPacketPointer controlPacket); + void processProbeTail(ControlPacketPointer controlPacket); void resetReceiveState(); void resetRTT(); @@ -171,7 +172,14 @@ private: std::map _pendingReceivedMessages; int _packetsSinceACK { 0 }; // The number of packets that have been received during the current ACK interval - + + // Re-used control packets + ControlPacketPointer _ackPacket; + ControlPacketPointer _lightACKPacket; + ControlPacketPointer _ack2Packet; + ControlPacketPointer _lossReport; + ControlPacketPointer _handshakeACK; + ConnectionStats _stats; }; From dafb5c5bab913ae1b1ad773fe18372ec7cb87109 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 15:11:56 -0700 Subject: [PATCH 10/19] 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 11/19] 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 12/19] 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 13/19] 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; } From 46526a309d7fe4d9987290880170dd69d1e90f57 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 3 Oct 2016 17:46:09 -0700 Subject: [PATCH 14/19] Increase audio input buffering on Windows 7 --- libraries/audio-client/src/AudioClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index d867bf8b3c..16f4c35d21 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1364,7 +1364,7 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) { // proportional to the accelerator ratio. #ifdef Q_OS_WIN -const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 1.0f; +const float AudioClient::CALLBACK_ACCELERATOR_RATIO = IsWindows8OrGreater() ? 1.0f : 0.25f; #endif #ifdef Q_OS_MAC From 372b6314485f4947c51fb85d0be327a850014881 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 3 Oct 2016 19:59:27 -0700 Subject: [PATCH 15/19] Post-build command to use qtaudio_windows.dll for Win7, qtaudio_wasapi.dll for Win8/10, or include both for Installer --- .../PackageLibrariesForDeployment.cmake | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index da0ee35769..beb5c22aa7 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -44,13 +44,25 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) set(QTAUDIO_PATH $/audio) - # if present, replace qtaudio_windows.dll with qtaudio_wasapi.dll - add_custom_command( - 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} ) - ) + if (DEPLOY_PACKAGE) + # copy qtaudio_wasapi.dll alongside qtaudio_windows.dll, and let the installer resolve + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND if exist ${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 copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.dll ${QTAUDIO_PATH} && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.pdb ${QTAUDIO_PATH} ) + ) + elseif (${CMAKE_SYSTEM_VERSION} VERSION_GREATER 6.2) + # replace qtaudio_windows.dll with qtaudio_wasapi.dll on Windows 8/8.1/10 + add_custom_command( + 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} ) + ) + else () + # continue using qtaudio_windows.dll on Windows 7 + endif () endif () endmacro() From eb5b03d00836284102199c0711610af7484a67b8 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 3 Oct 2016 20:52:24 -0700 Subject: [PATCH 16/19] Don't pre-delete qtaudio_windows.dll in Installer --- cmake/templates/NSIS.template.in | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index a80367cee1..65e801d321 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -600,9 +600,6 @@ Section "-Core installation" Delete "$INSTDIR\version" Delete "$INSTDIR\xinput1_3.dll" - ;Delete old Qt files - Delete "$INSTDIR\audio\qtaudio_windows.dll" - ; Delete old desktop shortcuts before they were renamed during Sandbox rename Delete "$DESKTOP\@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@.lnk" Delete "$DESKTOP\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk" From 541f7ebb05b12e8c1ced65cab3c65c65d8847828 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 3 Oct 2016 21:46:55 -0700 Subject: [PATCH 17/19] Select the correct plugin in the Installer, based on Windows version --- cmake/templates/NSIS.template.in | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 65e801d321..5b15795443 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -23,6 +23,11 @@ ;Default installation folder InstallDir "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" +;-------------------------------- +;Include WinVer to get Windows version + + !include "WinVer.nsh" + ;-------------------------------- ;General ; leverage the UAC NSIS plugin to promote uninstaller to elevated privileges @@ -600,6 +605,19 @@ Section "-Core installation" Delete "$INSTDIR\version" Delete "$INSTDIR\xinput1_3.dll" + ; The installer includes two different Qt audio plugins. + ; On Windows 8 and above, only qtaudio_wasapi.dll should be installed. + ; On Windows 7 and below, only qtaudio_windows.dll should be installed. + ${If} ${AtLeastWin8} + Delete "$INSTDIR\audio\qtaudio_windows.dll" + Delete "$INSTDIR\audio\qtaudio_windows.pdb" + MessageBox MB_OK "Detected Windows 8+" + ${Else} + Delete "$INSTDIR\audio\qtaudio_wasapi.dll" + Delete "$INSTDIR\audio\qtaudio_wasapi.pdb" + MessageBox MB_OK "Detected Windows 7-" + ${EndIf} + ; Delete old desktop shortcuts before they were renamed during Sandbox rename Delete "$DESKTOP\@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@.lnk" Delete "$DESKTOP\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk" From 40393bd8efdb368708167d4ef491cc83f2b0c541 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 3 Oct 2016 22:31:43 -0700 Subject: [PATCH 18/19] remove Installer debugging code --- cmake/templates/NSIS.template.in | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 5b15795443..568418afe1 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -611,11 +611,9 @@ Section "-Core installation" ${If} ${AtLeastWin8} Delete "$INSTDIR\audio\qtaudio_windows.dll" Delete "$INSTDIR\audio\qtaudio_windows.pdb" - MessageBox MB_OK "Detected Windows 8+" ${Else} Delete "$INSTDIR\audio\qtaudio_wasapi.dll" Delete "$INSTDIR\audio\qtaudio_wasapi.pdb" - MessageBox MB_OK "Detected Windows 7-" ${EndIf} ; Delete old desktop shortcuts before they were renamed during Sandbox rename From 6838dc0247cebc1c389a68c2be17601d0358a861 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 4 Oct 2016 09:01:43 -0700 Subject: [PATCH 19/19] Inverted the CMAKE logic, to make the version comparison more clear --- cmake/macros/PackageLibrariesForDeployment.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index beb5c22aa7..d8e895b7b0 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -52,7 +52,9 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) COMMAND if exist ${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 copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.dll ${QTAUDIO_PATH} && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.pdb ${QTAUDIO_PATH} ) ) - elseif (${CMAKE_SYSTEM_VERSION} VERSION_GREATER 6.2) + elseif (${CMAKE_SYSTEM_VERSION} VERSION_LESS 6.2) + # continue using qtaudio_windows.dll on Windows 7 + else () # replace qtaudio_windows.dll with qtaudio_wasapi.dll on Windows 8/8.1/10 add_custom_command( TARGET ${TARGET_NAME} @@ -60,8 +62,6 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) 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} ) ) - else () - # continue using qtaudio_windows.dll on Windows 7 endif () endif ()