diff --git a/CMakeLists.txt b/CMakeLists.txt index 93b784b462..69ea0b7fd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,7 @@ add_subdirectory(tools) if (BUILD_TESTS) add_subdirectory(tests) + add_subdirectory(tests-manual) endif() if (BUILD_INSTALLER) diff --git a/cmake/macros/SetupHifiTestCase.cmake b/cmake/macros/SetupHifiTestCase.cmake index 6c7d38e19c..b0edb41e36 100644 --- a/cmake/macros/SetupHifiTestCase.cmake +++ b/cmake/macros/SetupHifiTestCase.cmake @@ -124,15 +124,14 @@ macro(SETUP_HIFI_TESTCASE) # This target will also build + run the other test targets using ctest when built. add_custom_target(${TEST_TARGET} - COMMAND ctest . SOURCES ${TEST_PROJ_SRC_FILES} # display source files under the testcase target DEPENDS ${${TEST_PROJ_NAME}_TARGETS}) + set_target_properties(${TEST_TARGET} PROPERTIES + FOLDER "Tests" EXCLUDE_FROM_DEFAULT_BUILD TRUE EXCLUDE_FROM_ALL TRUE) - set_target_properties(${TEST_TARGET} PROPERTIES FOLDER "Tests") - list (APPEND ALL_TEST_TARGETS ${TEST_TARGET}) set(ALL_TEST_TARGETS "${ALL_TEST_TARGETS}" PARENT_SCOPE) else () diff --git a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml index d49e1e11be..c3e842bc2f 100644 --- a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml +++ b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml @@ -20,15 +20,10 @@ import "../fileDialog" Item { // Set from OffscreenUi::assetDialog() property alias dir: assetTableModel.folder - property alias filter: selectionType.filtersString // FIXME: Currently only supports simple filters, "*.xxx". - property int options // Not used. + property alias filter: selectionType.filtersString + property int options property bool selectDirectory: false - - // Not implemented. - //property bool saveDialog: false; - //property bool multiSelect: false; - property bool singleClickNavigate: false HifiConstants { id: hifi } @@ -85,7 +80,6 @@ Item { size: 28 width: height enabled: destination !== "" - //onClicked: d.navigateHome(); onClicked: assetTableModel.folder = destination; } } @@ -228,7 +222,9 @@ Item { function onGetAllMappings(error, map) { var mappings, - fileTypeFilter, + fileTypeFilters = [], + filterListStart, + filterListEnd, index, path, fileName, @@ -249,7 +245,16 @@ Item { if (error === "") { mappings = Object.keys(map); - fileTypeFilter = filter.replace("*", "").toLowerCase(); + filter = filter.replace(/\s/g, ''); + filterListStart = filter.indexOf("("); + filterListEnd = filter.indexOf(")"); + if (filterListStart !== -1 && filterListEnd !== -1) { + var FIRST_EXTENSION_OFFSET = 2; + fileTypeFilters = filter.substring(filterListStart + FIRST_EXTENSION_OFFSET + , filterListEnd).toLowerCase().split("*"); + } else if (filter !== "") { + fileTypeFilters[0] = filter.replace("*", "").toLowerCase(); + } for (i = 0, length = mappings.length; i < length; i++) { index = mappings[i].lastIndexOf("/"); @@ -260,7 +265,24 @@ Item { fileIsDir = false; isValid = false; - if (fileType.toLowerCase() === fileTypeFilter) { + if (fileTypeFilters.length > 1) { + if (fileTypeFilters.indexOf(fileType.toLowerCase()) !== -1) { + if (path === folder) { + isValid = !selectDirectory; + } else if (path.length > folder.length) { + subDirectory = path.slice(folder.length); + index = subDirectory.indexOf("/"); + if (index === subDirectory.lastIndexOf("/")) { + fileName = subDirectory.slice(0, index); + if (subDirectories.indexOf(fileName) === -1) { + fileIsDir = true; + isValid = true; + subDirectories.push(fileName); + } + } + } + } + } else if (fileType.toLowerCase() === fileTypeFilters[0] || fileTypeFilters.length === 0) { if (path === folder) { isValid = !selectDirectory; } else if (path.length > folder.length) { diff --git a/interface/resources/shaders/splashSkybox.frag b/interface/resources/shaders/splashSkybox.frag new file mode 100644 index 0000000000..38c89b4d26 --- /dev/null +++ b/interface/resources/shaders/splashSkybox.frag @@ -0,0 +1,47 @@ +const vec3 COLOR = vec3(0x00, 0xD8, 0x02) / vec3(0xFF); +const float CUTOFF = 0.65; +const float NOISE_MULT = 8.0; +const float NOISE_POWER = 1.0; + +float noise4D(vec4 p) { + return fract(sin(dot(p ,vec4(12.9898,78.233,126.7235, 593.2241))) * 43758.5453); +} + +float worley4D(vec4 p) { + float r = 3.0; + vec4 f = floor(p); + vec4 x = fract(p); + for(int i = -1; i<=1; i++) + { + for(int j = -1; j<=1; j++) + { + for(int k = -1; k<=1; k++) + { + for (int l = -1; l <= 1; l++) { + vec4 q = vec4(float(i),float(j),float(k), float(l)); + vec4 v = q + vec4(noise4D((q+f)*1.11), noise4D((q+f)*1.14), noise4D((q+f)*1.17), noise4D((q+f)*1.20)) - x; + float d = dot(v, v); + r = min(r, d); + } + } + } + } + return sqrt(r); +} + + +vec3 mainColor(vec3 direction) { + float n = worley4D(vec4(direction * NOISE_MULT, iGlobalTime / 3.0)); + n = 1.0 - n; + n = pow(n, NOISE_POWER); + if (n < CUTOFF) { + return vec3(0.0); + } + + n = (n - CUTOFF) / (1.0 - CUTOFF); + return COLOR * (1.0 - n); +} + +vec3 getSkyboxColor() { + return mainColor(normalize(_normal)); +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 20efe73f08..42a6cbc790 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -144,6 +144,7 @@ #include #include #include +#include #include "AudioClient.h" #include "audio/AudioScope.h" @@ -375,7 +376,7 @@ Setting::Handle maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTRE static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com"; static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; - +static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin"; static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; const std::vector> Application::_acceptedExtensions { @@ -1349,10 +1350,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QCoreApplication::processEvents(); _glWidget->createContext(); - // Create the main thread context, the GPU backend, and the display plugins + // Create the main thread context, the GPU backend initializeGL(); - DependencyManager::get()->setGPUContext(_gpuContext); - qCDebug(interfaceapp, "Initialized Display."); + qCDebug(interfaceapp, "Initialized GL"); + + // Initialize the display plugin architecture + initializeDisplayPlugins(); + qCDebug(interfaceapp, "Initialized Display"); + // Create the rendering engine. This can be slow on some machines due to lots of // GPU pipeline creation. initializeRenderEngine(); @@ -2253,8 +2258,11 @@ void Application::updateVerboseLogging() { } bool enable = menu->isOptionChecked(MenuOption::VerboseLogging); - QString rules = "*.debug=%1\n" - "*.info=%1"; + QString rules = + "hifi.*.debug=%1\n" + "hifi.*.info=%1\n" + "hifi.audio-stream.debug=false\n" + "hifi.audio-stream.info=false"; rules = rules.arg(enable ? "true" : "false"); QLoggingCategory::setFilterRules(rules); } @@ -2369,6 +2377,10 @@ void Application::onAboutToQuit() { } } + // The active display plugin needs to be loaded before the menu system is active, + // so its persisted explicitly here + Setting::Handle{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME }.set(getActiveDisplayPlugin()->getName()); + getActiveDisplayPlugin()->deactivate(); if (_autoSwitchDisplayModeSupportedHMDPlugin && _autoSwitchDisplayModeSupportedHMDPlugin->isSessionActive()) { @@ -2608,10 +2620,84 @@ void Application::initializeGL() { _glWidget->makeCurrent(); _gpuContext = std::make_shared(); + DependencyManager::get()->setGPUContext(_gpuContext); + // Restore the default main thread context _offscreenContext->makeCurrent(); +} - updateDisplayMode(); +static const QString SPLASH_SKYBOX{ "{\"ProceduralEntity\":{ \"version\":2, \"shaderUrl\":\"qrc:///shaders/splashSkybox.frag\" } }" }; + +void Application::initializeDisplayPlugins() { + auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); + Setting::Handle activeDisplayPluginSetting{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME, displayPlugins.at(0)->getName() }; + auto lastActiveDisplayPluginName = activeDisplayPluginSetting.get(); + + auto defaultDisplayPlugin = displayPlugins.at(0); + // Once time initialization code + DisplayPluginPointer targetDisplayPlugin; + foreach(auto displayPlugin, displayPlugins) { + displayPlugin->setContext(_gpuContext); + if (displayPlugin->getName() == lastActiveDisplayPluginName) { + targetDisplayPlugin = displayPlugin; + } + QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, + [this](const QSize& size) { resizeGL(); }); + QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset); + } + + // The default display plugin needs to be activated first, otherwise the display plugin thread + // may be launched by an external plugin, which is bad + setDisplayPlugin(defaultDisplayPlugin); + + // Now set the desired plugin if it's not the same as the default plugin + if (targetDisplayPlugin != defaultDisplayPlugin) { + setDisplayPlugin(targetDisplayPlugin); + } + + // Submit a default frame to render until the engine starts up + updateRenderArgs(0.0f); + + _offscreenContext->makeCurrent(); + +#define ENABLE_SPLASH_FRAME 0 +#if ENABLE_SPLASH_FRAME + { + QMutexLocker viewLocker(&_renderArgsMutex); + + if (_appRenderArgs._isStereo) { + _gpuContext->enableStereo(true); + _gpuContext->setStereoProjections(_appRenderArgs._eyeProjections); + _gpuContext->setStereoViews(_appRenderArgs._eyeOffsets); + } + + // Frame resources + auto framebufferCache = DependencyManager::get(); + gpu::FramebufferPointer finalFramebuffer = framebufferCache->getFramebuffer(); + std::shared_ptr procedural = std::make_shared(); + procedural->parse(SPLASH_SKYBOX); + + _gpuContext->beginFrame(_appRenderArgs._view, _appRenderArgs._headPose); + gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) { + batch.resetStages(); + batch.enableStereo(false); + batch.setFramebuffer(finalFramebuffer); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, { 0, 0, 0, 1 }); + batch.enableSkybox(true); + batch.enableStereo(_appRenderArgs._isStereo); + batch.setViewportTransform({ 0, 0, finalFramebuffer->getSize() }); + procedural->render(batch, _appRenderArgs._renderArgs.getViewFrustum()); + }); + auto frame = _gpuContext->endFrame(); + frame->frameIndex = 0; + frame->framebuffer = finalFramebuffer; + frame->pose = _appRenderArgs._headPose; + frame->framebufferRecycler = [framebufferCache, procedural](const gpu::FramebufferPointer& framebuffer) { + framebufferCache->releaseFramebuffer(framebuffer); + }; + _displayPlugin->submitFrame(frame); + } +#endif } void Application::initializeRenderEngine() { @@ -2635,6 +2721,7 @@ void Application::initializeRenderEngine() { } extern void setupPreferences(); +static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, bool active); void Application::initializeUi() { // Build a shared canvas / context for the Chromium processes @@ -2776,10 +2863,25 @@ void Application::initializeUi() { offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1); offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); - // Now that the menu is instantiated, ensure the display plugin menu is properly updated - updateDisplayMode(); flushMenuUpdates(); + // Now that the menu is instantiated, ensure the display plugin menu is properly updated + { + auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); + // first sort the plugins into groupings: standard, advanced, developer + std::stable_sort(displayPlugins.begin(), displayPlugins.end(), + [](const DisplayPluginPointer& a, const DisplayPluginPointer& b)->bool { return a->getGrouping() < b->getGrouping(); }); + + // concatenate the groupings into a single list in the order: standard, advanced, developer + for(const auto& displayPlugin : displayPlugins) { + addDisplayPluginToMenu(displayPlugin, _displayPlugin == displayPlugin); + } + + // after all plugins have been added to the menu, add a separator to the menu + auto parent = getPrimaryMenu()->getMenu(MenuOption::OutputMenu); + parent->addSeparator(); + } + // The display plugins are created before the menu now, so we need to do this here to hide the menu bar // now that it exists if (_window && _window->isFullScreen()) { @@ -2930,7 +3032,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _thirdPersonHMDCameraBoomValid = false; _myCamera.setOrientation(myAvatar->getHead()->getOrientation()); - if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { + if (isOptionChecked(MenuOption::CenterPlayerInView)) { _myCamera.setPosition(myAvatar->getDefaultEyePosition() + _myCamera.getOrientation() * boomOffset); } @@ -5732,6 +5834,32 @@ void Application::update(float deltaTime) { } + updateRenderArgs(deltaTime); + + // HACK + // load the view frustum + // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering. + // Then we can move this logic into the Avatar::simulate call. + myAvatar->preDisplaySide(&_appRenderArgs._renderArgs); + + + { + PerformanceTimer perfTimer("limitless"); + AnimDebugDraw::getInstance().update(); + } + + { + PerformanceTimer perfTimer("limitless"); + DependencyManager::get()->update(); + } + + { // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over + PerformanceTimer perfTimer("enqueueFrame"); + getMain3DScene()->enqueueFrame(); + } +} + +void Application::updateRenderArgs(float deltaTime) { editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { PerformanceTimer perfTimer("editRenderArgs"); appRenderArgs._headPose = getHMDSensorPose(); @@ -5755,9 +5883,9 @@ void Application::update(float deltaTime) { QMutexLocker viewLocker(&_viewMutex); // adjust near clip plane to account for sensor scaling. auto adjustedProjection = glm::perspective(glm::radians(_fieldOfView.get()), - getActiveDisplayPlugin()->getRecommendedAspectRatio(), - DEFAULT_NEAR_CLIP * sensorToWorldScale, - DEFAULT_FAR_CLIP); + getActiveDisplayPlugin()->getRecommendedAspectRatio(), + DEFAULT_NEAR_CLIP * sensorToWorldScale, + DEFAULT_FAR_CLIP); _viewFrustum.setProjection(adjustedProjection); _viewFrustum.calculate(); } @@ -5773,8 +5901,14 @@ void Application::update(float deltaTime) { } { PROFILE_RANGE(render, "/resizeGL"); - PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + bool showWarnings = false; + bool suppressShortTimings = false; + auto menu = Menu::getInstance(); + if (menu) { + suppressShortTimings = menu->isOptionChecked(MenuOption::SuppressShortTimings); + showWarnings = menu->isOptionChecked(MenuOption::PipelineWarnings); + } + PerformanceWarning::setSuppressShortTimings(suppressShortTimings); PerformanceWarning warn(showWarnings, "Application::paintGL()"); resizeGL(); } @@ -5830,12 +5964,6 @@ void Application::update(float deltaTime) { } } - // HACK - // load the view frustum - // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering. - // Then we can move this logic into the Avatar::simulate call. - myAvatar->preDisplaySide(&appRenderArgs._renderArgs); - { QMutexLocker viewLocker(&_viewMutex); _myCamera.loadViewFrustum(_displayViewFrustum); @@ -5847,21 +5975,6 @@ void Application::update(float deltaTime) { appRenderArgs._renderArgs.setViewFrustum(_displayViewFrustum); } }); - - { - PerformanceTimer perfTimer("limitless"); - AnimDebugDraw::getInstance().update(); - } - - { - PerformanceTimer perfTimer("limitless"); - DependencyManager::get()->update(); - } - - { // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over - PerformanceTimer perfTimer("enqueueFrame"); - getMain3DScene()->enqueueFrame(); - } } void Application::queryAvatars() { @@ -7502,15 +7615,19 @@ void Application::shareSnapshot(const QString& path, const QUrl& href) { } float Application::getRenderResolutionScale() const { - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) { + auto menu = Menu::getInstance(); + if (!menu) { return 1.0f; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionTwoThird)) { + } + if (menu->isOptionChecked(MenuOption::RenderResolutionOne)) { + return 1.0f; + } else if (menu->isOptionChecked(MenuOption::RenderResolutionTwoThird)) { return 0.666f; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionHalf)) { + } else if (menu->isOptionChecked(MenuOption::RenderResolutionHalf)) { return 0.5f; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionThird)) { + } else if (menu->isOptionChecked(MenuOption::RenderResolutionThird)) { return 0.333f; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionQuarter)) { + } else if (menu->isOptionChecked(MenuOption::RenderResolutionQuarter)) { return 0.25f; } else { return 1.0f; @@ -7734,7 +7851,7 @@ DisplayPluginPointer Application::getActiveDisplayPlugin() const { static const char* EXCLUSION_GROUP_KEY = "exclusionGroup"; -static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) { +static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, bool active) { auto menu = Menu::getInstance(); QString name = displayPlugin->getName(); auto grouping = displayPlugin->getGrouping(); @@ -7779,65 +7896,12 @@ void Application::updateDisplayMode() { qFatal("Attempted to switch display plugins from a non-main thread"); } - auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); - - // Once time initialization code - static std::once_flag once; - std::call_once(once, [&] { - foreach(auto displayPlugin, displayPlugins) { - displayPlugin->setContext(_gpuContext); - QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, - [this](const QSize& size) { resizeGL(); }); - QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset); - } - }); - // Once time initialization code that depends on the UI being available - auto menu = Menu::getInstance(); - if (menu) { - static std::once_flag onceUi; - std::call_once(onceUi, [&] { - bool first = true; - - // first sort the plugins into groupings: standard, advanced, developer - DisplayPluginList standard; - DisplayPluginList advanced; - DisplayPluginList developer; - foreach(auto displayPlugin, displayPlugins) { - displayPlugin->setContext(_gpuContext); - auto grouping = displayPlugin->getGrouping(); - switch (grouping) { - case Plugin::ADVANCED: - advanced.push_back(displayPlugin); - break; - case Plugin::DEVELOPER: - developer.push_back(displayPlugin); - break; - default: - standard.push_back(displayPlugin); - break; - } - } - - // concatenate the groupings into a single list in the order: standard, advanced, developer - standard.insert(std::end(standard), std::begin(advanced), std::end(advanced)); - standard.insert(std::end(standard), std::begin(developer), std::end(developer)); - - foreach(auto displayPlugin, standard) { - addDisplayPluginToMenu(displayPlugin, first); - first = false; - } - - // after all plugins have been added to the menu, add a separator to the menu - auto parent = menu->getMenu(MenuOption::OutputMenu); - parent->addSeparator(); - }); - - } - + auto displayPlugins = getDisplayPlugins(); // Default to the first item on the list, in case none of the menu items match DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0); + auto menu = getPrimaryMenu(); if (menu) { foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { QString name = displayPlugin->getName(); @@ -7861,6 +7925,14 @@ void Application::updateDisplayMode() { } void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { + if (newDisplayPlugin == _displayPlugin) { + return; + } + + // FIXME don't have the application directly set the state of the UI, + // instead emit a signal that the display plugin is changing and let + // the desktop lock itself. Reduces coupling between the UI and display + // plugins auto offscreenUi = DependencyManager::get(); auto desktop = offscreenUi->getDesktop(); auto menu = Menu::getInstance(); @@ -7871,8 +7943,8 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { bool wasRepositionLocked = false; if (desktop) { // Tell the desktop to no reposition (which requires plugin info), until we have set the new plugin, below. - wasRepositionLocked = offscreenUi->getDesktop()->property("repositionLocked").toBool(); - offscreenUi->getDesktop()->setProperty("repositionLocked", true); + wasRepositionLocked = desktop->property("repositionLocked").toBool(); + desktop->setProperty("repositionLocked", true); } if (_displayPlugin) { diff --git a/interface/src/Application.h b/interface/src/Application.h index aa6469c592..4946dd7ad9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -148,6 +148,7 @@ public: Q_INVOKABLE QString getUserAgent(); void initializeGL(); + void initializeDisplayPlugins(); void initializeRenderEngine(); void initializeUi(); @@ -671,6 +672,7 @@ private: using RenderArgsEditor = std::function ; void editRenderArgs(RenderArgsEditor editor); + void updateRenderArgs(float deltaTime); Overlays _overlays; diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index e1015ca5d1..76babe3682 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -13,8 +13,9 @@ #include #include -#include "ui/Stats.h" +#include #include +#include "ui/Stats.h" #include "Util.h" @@ -233,3 +234,4 @@ void Application::runRenderFrame(RenderArgs* renderArgs) { _renderEngine->run(); } } + diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 15b220c63b..6c6f6d4d41 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2039,7 +2039,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { } } -void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { +void MyAvatar::preDisplaySide(const RenderArgs* renderArgs) { // toggle using the cauterizedBones depending on where the camera is and the rendering pass type. const bool shouldDrawHead = shouldRenderHead(renderArgs); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ac3d3cd2f4..154e2e4d09 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -272,7 +272,7 @@ public: void update(float deltaTime); virtual void postUpdate(float deltaTime, const render::ScenePointer& scene) override; - void preDisplaySide(RenderArgs* renderArgs); + void preDisplaySide(const RenderArgs* renderArgs); const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; } const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; } diff --git a/libraries/audio/src/AudioLogging.cpp b/libraries/audio/src/AudioLogging.cpp index 8a668a4bcb..73091885cb 100644 --- a/libraries/audio/src/AudioLogging.cpp +++ b/libraries/audio/src/AudioLogging.cpp @@ -12,5 +12,4 @@ #include "AudioLogging.h" Q_LOGGING_CATEGORY(audio, "hifi.audio") -Q_LOGGING_CATEGORY(audiostream, "hifi.audio-stream", QtWarningMsg) - +Q_LOGGING_CATEGORY(audiostream, "hifi.audio-stream") diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 4b33565bfd..3cfc0651d8 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -336,9 +336,8 @@ void OpenGLDisplayPlugin::deactivate() { _container->showDisplayPluginsTools(false); if (!_container->currentDisplayActions().isEmpty()) { - auto menu = _container->getPrimaryMenu(); foreach(auto itemInfo, _container->currentDisplayActions()) { - menu->removeMenuItem(itemInfo.first, itemInfo.second); + _container->removeMenuItem(itemInfo.first, itemInfo.second); } _container->currentDisplayActions().clear(); } diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index d89dd4f9d0..6c6c4b37d0 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -77,7 +77,6 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, _myAvatar->updateAvatarEntity(entityItemID, binaryProperties); entity->setLastBroadcast(usecTimestampNow()); - return; } @@ -85,10 +84,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityTreePointer entityTree, EntityItemID entityItemID, const EntityItemProperties& properties) { - if (!_shouldSend) { - return; // bail early - } - if (properties.getClientOnly() && properties.getOwningAvatarID() == _myAvatar->getID()) { // this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server queueEditAvatarEntityMessage(type, entityTree, entityItemID, properties); @@ -147,9 +142,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, } void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityItemID) { - if (!_shouldSend) { - return; // bail early - } // in case this was a clientOnly entity: if(_myAvatar) { diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 91f7954943..6598a26c99 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -103,12 +103,15 @@ void OffscreenGLCanvas::onMessageLogged(const QOpenGLDebugMessage& debugMessage) bool OffscreenGLCanvas::makeCurrent() { bool result = _context->makeCurrent(_offscreenSurface); - std::call_once(_reportOnce, []{ - qCDebug(glLogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); - qCDebug(glLogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); - qCDebug(glLogging) << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR)); - qCDebug(glLogging) << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); - }); + if (glGetString) { + std::call_once(_reportOnce, [] { + qCDebug(glLogging) << "GL Version: " << QString((const char*)glGetString(GL_VERSION)); + qCDebug(glLogging) << "GL Shader Language Version: " + << QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)); + qCDebug(glLogging) << "GL Vendor: " << QString((const char*)glGetString(GL_VENDOR)); + qCDebug(glLogging) << "GL Renderer: " << QString((const char*)glGetString(GL_RENDERER)); + }); + } return result; } diff --git a/libraries/networking/src/HMACAuth.cpp b/libraries/networking/src/HMACAuth.cpp new file mode 100644 index 0000000000..515af9a070 --- /dev/null +++ b/libraries/networking/src/HMACAuth.cpp @@ -0,0 +1,117 @@ +// +// HMACAuth.cpp +// libraries/networking/src +// +// Created by Simon Walton on 3/19/2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "HMACAuth.h" + +#include +#include + +#include +#include "NetworkLogging.h" +#include + +#if OPENSSL_VERSION_NUMBER >= 0x10100000 +HMACAuth::HMACAuth(AuthMethod authMethod) + : _hmacContext(HMAC_CTX_new()) + , _authMethod(authMethod) { } + +HMACAuth::~HMACAuth() +{ + HMAC_CTX_free(_hmacContext); +} + +#else + +HMACAuth::HMACAuth(AuthMethod authMethod) + : _hmacContext(new HMAC_CTX()) + , _authMethod(authMethod) { + HMAC_CTX_init(_hmacContext); +} + +HMACAuth::~HMACAuth() { + HMAC_CTX_cleanup(_hmacContext); + delete _hmacContext; +} +#endif + +bool HMACAuth::setKey(const char* keyValue, int keyLen) { + const EVP_MD* sslStruct = nullptr; + + switch (_authMethod) { + case MD5: + sslStruct = EVP_md5(); + break; + + case SHA1: + sslStruct = EVP_sha1(); + break; + + case SHA224: + sslStruct = EVP_sha224(); + break; + + case SHA256: + sslStruct = EVP_sha256(); + break; + + case RIPEMD160: + sslStruct = EVP_ripemd160(); + break; + + default: + return false; + } + + QMutexLocker lock(&_lock); + return (bool) HMAC_Init_ex(_hmacContext, keyValue, keyLen, sslStruct, nullptr); +} + +bool HMACAuth::setKey(const QUuid& uidKey) { + const QByteArray rfcBytes(uidKey.toRfc4122()); + return setKey(rfcBytes.constData(), rfcBytes.length()); +} + +bool HMACAuth::addData(const char* data, int dataLen) { + QMutexLocker lock(&_lock); + return (bool) HMAC_Update(_hmacContext, reinterpret_cast(data), dataLen); +} + +HMACAuth::HMACHash HMACAuth::result() { + HMACHash hashValue(EVP_MAX_MD_SIZE); + unsigned int hashLen; + QMutexLocker lock(&_lock); + + auto hmacResult = HMAC_Final(_hmacContext, &hashValue[0], &hashLen); + + if (hmacResult) { + hashValue.resize((size_t)hashLen); + } else { + // the HMAC_FINAL call failed - should not be possible to get into this state + qCWarning(networking) << "Error occured calling HMAC_Final"; + assert(hmacResult); + } + + // Clear state for possible reuse. + HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr); + return hashValue; +} + +bool HMACAuth::calculateHash(HMACHash& hashResult, const char* data, int dataLen) { + QMutexLocker lock(&_lock); + if (!addData(data, dataLen)) { + qCWarning(networking) << "Error occured calling HMACAuth::addData()"; + assert(false); + return false; + } + + hashResult = result(); + return true; +} diff --git a/libraries/networking/src/HMACAuth.h b/libraries/networking/src/HMACAuth.h new file mode 100644 index 0000000000..1346bdee17 --- /dev/null +++ b/libraries/networking/src/HMACAuth.h @@ -0,0 +1,47 @@ +// +// HMACAuth.h +// libraries/networking/src +// +// Created by Simon Walton on 3/19/2018. +// Copyright 2018 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 +// + +#ifndef hifi_HMACAuth_h +#define hifi_HMACAuth_h + +#include +#include +#include + +class QUuid; + +class HMACAuth { +public: + enum AuthMethod { MD5, SHA1, SHA224, SHA256, RIPEMD160 }; + using HMACHash = std::vector; + + explicit HMACAuth(AuthMethod authMethod = MD5); + ~HMACAuth(); + + bool setKey(const char* keyValue, int keyLen); + bool setKey(const QUuid& uidKey); + // Calculate complete hash in one. + bool calculateHash(HMACHash& hashResult, const char* data, int dataLen); + + // Append to data to be hashed. + bool addData(const char* data, int dataLen); + // Get the resulting hash from calls to addData(). + // Note that only one hash may be calculated at a time for each + // HMACAuth instance if this interface is used. + HMACHash result(); + +private: + QMutex _lock { QMutex::Recursive }; + struct hmac_ctx_st* _hmacContext; + AuthMethod _authMethod; +}; + +#endif // hifi_HMACAuth_h diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 0660f02fd6..012a891698 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -36,6 +36,7 @@ #include "HifiSockAddr.h" #include "NetworkLogging.h" #include "udt/Packet.h" +#include "HMACAuth.h" static Setting::Handle LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0); @@ -332,15 +333,20 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe if (verifiedPacket && !ignoreVerification) { QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet); - QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret()); + QByteArray expectedHash; + auto sourceNodeHMACAuth = sourceNode->getAuthenticateHash(); + if (sourceNode->getAuthenticateHash()) { + expectedHash = NLPacket::hashForPacketAndHMAC(packet, *sourceNodeHMACAuth); + } - // check if the md5 hash in the header matches the hash we would expect - if (packetHeaderHash != expectedHash) { + // check if the HMAC-md5 hash in the header matches the hash we would expect + if (!sourceNodeHMACAuth || packetHeaderHash != expectedHash) { static QMultiMap hashDebugSuppressMap; if (!hashDebugSuppressMap.contains(sourceID, headerType)) { - qCDebug(networking) << packetHeaderHash << expectedHash; qCDebug(networking) << "Packet hash mismatch on" << headerType << "- Sender" << sourceID; + qCDebug(networking) << "Packet len:" << packet.getDataSize() << "Expected hash:" << + expectedHash.toHex() << "Actual:" << packetHeaderHash.toHex(); hashDebugSuppressMap.insert(sourceID, headerType); } @@ -372,15 +378,15 @@ void LimitedNodeList::collectPacketStats(const NLPacket& packet) { _numCollectedBytes += packet.getDataSize(); } -void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret) { +void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth) { if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) { packet.writeSourceID(getSessionLocalID()); } - if (!connectionSecret.isNull() + if (hmacAuth && !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType()) && !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) { - packet.writeVerificationHashGivenSecret(connectionSecret); + packet.writeVerificationHash(*hmacAuth); } } @@ -396,17 +402,17 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node& emit dataSent(destinationNode.getType(), packet.getDataSize()); destinationNode.recordBytesSent(packet.getDataSize()); - return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret()); + return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getAuthenticateHash()); } qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr, - const QUuid& connectionSecret) { + HMACAuth* hmacAuth) { Q_ASSERT(!packet.isPartOfMessage()); Q_ASSERT_X(!packet.isReliable(), "LimitedNodeList::sendUnreliablePacket", "Trying to send a reliable packet unreliably."); collectPacketStats(packet); - fillPacketHeader(packet, connectionSecret); + fillPacketHeader(packet, hmacAuth); return _nodeSocket.writePacket(packet, sockAddr); } @@ -419,7 +425,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node& emit dataSent(destinationNode.getType(), packet->getDataSize()); destinationNode.recordBytesSent(packet->getDataSize()); - return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret()); + return sendPacket(std::move(packet), *activeSocket, destinationNode.getAuthenticateHash()); } else { qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending"; return ERROR_SENDING_PACKET_BYTES; @@ -427,18 +433,18 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node& } qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr, - const QUuid& connectionSecret) { + HMACAuth* hmacAuth) { Q_ASSERT(!packet->isPartOfMessage()); if (packet->isReliable()) { collectPacketStats(*packet); - fillPacketHeader(*packet, connectionSecret); + fillPacketHeader(*packet, hmacAuth); auto size = packet->getDataSize(); _nodeSocket.writePacket(std::move(packet), sockAddr); return size; } else { - return sendUnreliablePacket(*packet, sockAddr, connectionSecret); + return sendUnreliablePacket(*packet, sockAddr, hmacAuth); } } @@ -447,13 +453,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi if (activeSocket) { qint64 bytesSent = 0; - auto connectionSecret = destinationNode.getConnectionSecret(); + auto connectionHash = destinationNode.getAuthenticateHash(); // close the last packet in the list packetList.closeCurrentPacket(); while (!packetList._packets.empty()) { - bytesSent += sendPacket(packetList.takeFront(), *activeSocket, connectionSecret); + bytesSent += sendPacket(packetList.takeFront(), *activeSocket, + connectionHash); } emit dataSent(destinationNode.getType(), bytesSent); @@ -466,14 +473,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi } qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr, - const QUuid& connectionSecret) { + HMACAuth* hmacAuth) { qint64 bytesSent = 0; // close the last packet in the list packetList.closeCurrentPacket(); while (!packetList._packets.empty()) { - bytesSent += sendPacket(packetList.takeFront(), sockAddr, connectionSecret); + bytesSent += sendPacket(packetList.takeFront(), sockAddr, hmacAuth); } return bytesSent; @@ -501,7 +508,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, for (std::unique_ptr& packet : packetList->_packets) { NLPacket* nlPacket = static_cast(packet.get()); collectPacketStats(*nlPacket); - fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret()); + fillPacketHeader(*nlPacket, destinationNode.getAuthenticateHash()); } return _nodeSocket.writePacketList(std::move(packetList), *activeSocket); @@ -524,7 +531,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node& auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket() : overridenSockAddr; - return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret()); + return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getAuthenticateHash()); } int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer message, SharedNodePointer sendingNode) { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 3d6fd0cd91..05374bbfbb 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -138,19 +138,17 @@ public: // use sendUnreliablePacket to send an unreliable packet (that you do not need to move) // either to a node (via its active socket) or to a manual sockaddr qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode); - qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr, - const QUuid& connectionSecret = QUuid()); + qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr); // use sendPacket to send a moved unreliable or reliable NL packet to a node's active socket or manual sockaddr qint64 sendPacket(std::unique_ptr packet, const Node& destinationNode); - qint64 sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr, - const QUuid& connectionSecret = QUuid()); + qint64 sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr); // use sendUnreliableUnorderedPacketList to unreliably send separate packets from the packet list // either to a node's active socket or to a manual sockaddr qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const Node& destinationNode); qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr, - const QUuid& connectionSecret = QUuid()); + HMACAuth* hmacAuth = nullptr); // use sendPacketList to send reliable packet lists (ordered or unordered) to a node's active socket // or to a manual sock addr @@ -372,7 +370,7 @@ protected: qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr, const QUuid& connectionSecret = QUuid()); void collectPacketStats(const NLPacket& packet); - void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret = QUuid()); + void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr); void setLocalSocket(const HifiSockAddr& sockAddr); diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp index ac3fbc966b..f946e97bf4 100644 --- a/libraries/networking/src/NLPacket.cpp +++ b/libraries/networking/src/NLPacket.cpp @@ -11,6 +11,8 @@ #include "NLPacket.h" +#include "HMACAuth.h" + int NLPacket::localHeaderSize(PacketType type) { bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type); bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type); @@ -150,18 +152,16 @@ QByteArray NLPacket::verificationHashInHeader(const udt::Packet& packet) { return QByteArray(packet.getData() + offset, NUM_BYTES_MD5_HASH); } -QByteArray NLPacket::hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret) { - QCryptographicHash hash(QCryptographicHash::Md5); - +QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash) { int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH; // add the packet payload and the connection UUID - hash.addData(packet.getData() + offset, packet.getDataSize() - offset); - hash.addData(connectionSecret.toRfc4122()); - - // return the hash - return hash.result(); + HMACAuth::HMACHash hashResult; + if (!hash.calculateHash(hashResult, packet.getData() + offset, packet.getDataSize() - offset)) { + return QByteArray(); + } + return QByteArray((const char*) hashResult.data(), (int) hashResult.size()); } void NLPacket::writeTypeAndVersion() { @@ -214,14 +214,14 @@ void NLPacket::writeSourceID(LocalID sourceID) const { _sourceID = sourceID; } -void NLPacket::writeVerificationHashGivenSecret(const QUuid& connectionSecret) const { +void NLPacket::writeVerificationHash(HMACAuth& hmacAuth) const { Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) && !PacketTypeEnum::getNonVerifiedPackets().contains(_type)); auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_LOCALID; - QByteArray verificationHash = hashForPacketAndSecret(*this, connectionSecret); + QByteArray verificationHash = hashForPacketAndHMAC(*this, hmacAuth); memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size()); } diff --git a/libraries/networking/src/NLPacket.h b/libraries/networking/src/NLPacket.h index 9b07bc6581..4103f2068e 100644 --- a/libraries/networking/src/NLPacket.h +++ b/libraries/networking/src/NLPacket.h @@ -18,6 +18,8 @@ #include "udt/Packet.h" +class HMACAuth; + class NLPacket : public udt::Packet { Q_OBJECT public: @@ -69,7 +71,7 @@ public: static LocalID sourceIDInHeader(const udt::Packet& packet); static QByteArray verificationHashInHeader(const udt::Packet& packet); - static QByteArray hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret); + static QByteArray hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash); PacketType getType() const { return _type; } void setType(PacketType type); @@ -78,9 +80,9 @@ public: void setVersion(PacketVersion version); LocalID getSourceID() const { return _sourceID; } - + void writeSourceID(LocalID sourceID) const; - void writeVerificationHashGivenSecret(const QUuid& connectionSecret) const; + void writeVerificationHash(HMACAuth& hmacAuth) const; protected: diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 626503d8ae..5bbff103dd 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -86,7 +86,7 @@ NodeType_t NodeType::fromString(QString type) { Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, - const HifiSockAddr& localSocket, QObject* parent) : + const HifiSockAddr& localSocket, QObject* parent) : NetworkPeer(uuid, publicSocket, localSocket, parent), _type(type), _pingMs(-1), // "Uninitialized" @@ -108,6 +108,7 @@ void Node::setType(char type) { _symmetricSocket.setObjectName(typeString); } + void Node::updateClockSkewUsec(qint64 clockSkewSample) { _clockSkewMovingPercentile.updatePercentile(clockSkewSample); _clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile(); @@ -194,3 +195,16 @@ QDebug operator<<(QDebug debug, const Node& node) { debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket(); return debug.nospace(); } + +void Node::setConnectionSecret(const QUuid& connectionSecret) { + if (_connectionSecret == connectionSecret) { + return; + } + + if (!_authenticateHash) { + _authenticateHash.reset(new HMACAuth()); + } + + _connectionSecret = connectionSecret; + _authenticateHash->setKey(_connectionSecret); +} diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 93b6a649d4..bcfe525aa8 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -33,6 +33,7 @@ #include "SimpleMovingAverage.h" #include "MovingPercentile.h" #include "NodePermissions.h" +#include "HMACAuth.h" class Node : public NetworkPeer { Q_OBJECT @@ -55,7 +56,8 @@ public: void setIsUpstream(bool isUpstream) { _isUpstream = isUpstream; } const QUuid& getConnectionSecret() const { return _connectionSecret; } - void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; } + void setConnectionSecret(const QUuid& connectionSecret); + HMACAuth* getAuthenticateHash() const { return _authenticateHash.get(); } NodeData* getLinkedData() const { return _linkedData.get(); } void setLinkedData(std::unique_ptr linkedData) { _linkedData = std::move(linkedData); } @@ -97,6 +99,7 @@ private: NodeType_t _type; QUuid _connectionSecret; + std::unique_ptr _authenticateHash { nullptr }; std::unique_ptr _linkedData; bool _isReplicated { false }; int _pingMs; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 70880833bf..02853543ba 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -92,7 +92,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarQuery: return static_cast(AvatarQueryVersion::ConicalFrustums); default: - return 20; + return 21; } } diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 0156013821..d8d3041270 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -23,7 +23,6 @@ const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::D OctreeEditPacketSender::OctreeEditPacketSender() : - _shouldSend(true), _maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES), _releaseQueuedMessagesPending(false) { @@ -146,10 +145,6 @@ void OctreeEditPacketSender::queuePendingPacketToNodes(std::unique_ptr } void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr packet) { - if (!_shouldSend) { - return; // bail early - } - assert(serversExist()); // we must have servers to be here!! auto node = DependencyManager::get()->soloNodeOfType(getMyNodeType()); @@ -162,10 +157,6 @@ void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr packet // NOTE: editMessage - is JUST the octcode/color and does not contain the packet header void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, QByteArray& editMessage) { - if (!_shouldSend) { - return; // bail early - } - // If we don't have servers, then we will simply queue up all of these packets and wait till we have // servers for processing if (!serversExist()) { diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index 8a33eb8b73..1dad5b74c7 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -41,12 +41,10 @@ public: /// are we in sending mode. If we're not in sending mode then all packets and messages will be ignored and /// not queued and not sent - bool getShouldSend() const { return _shouldSend; } /// set sending mode. By default we are set to shouldSend=TRUE and packets will be sent. If shouldSend=FALSE, then we'll /// switch to not sending mode, and all packets and messages will be ignored, not queued, and not sent. This might be used /// in an application like interface when all octree features are disabled. - void setShouldSend(bool shouldSend) { _shouldSend = shouldSend; } /// if you're running in non-threaded mode, you must call this method regularly virtual bool process() override; @@ -76,7 +74,6 @@ public slots: protected: using EditMessagePair = std::pair; - bool _shouldSend; void queuePacketToNode(const QUuid& nodeID, std::unique_ptr packet); void queuePacketListToNode(const QUuid& nodeUUID, std::unique_ptr packetList); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index a801392b66..9f067e51c6 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -349,7 +349,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { if (_numInactiveUpdates > 0) { const uint8_t MAX_NUM_INACTIVE_UPDATES = 20; - if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) { + if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES || isServerlessMode()) { // clear local ownership (stop sending updates) and let the server clear itself _entity->clearSimulationOwnership(); return false; @@ -833,3 +833,9 @@ void EntityMotionState::clearObjectVelocities() const { } _entity->setAcceleration(glm::vec3(0.0f)); } + +bool EntityMotionState::isServerlessMode() { + EntityTreeElementPointer element = _entity->getElement(); + EntityTreePointer tree = element ? element->getTree() : nullptr; + return tree ? tree->isServerlessMode() : false; +} diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index a542c61d43..376cc5afe2 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -156,6 +156,8 @@ protected: uint8_t _numInactiveUpdates { 1 }; uint8_t _bidPriority { 0 }; bool _serverVariablesSet { false }; + + bool isServerlessMode(); }; #endif // hifi_EntityMotionState_h diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 3ea3616b15..e2970d6a03 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -328,10 +328,18 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta } void PhysicalEntitySimulation::addOwnershipBid(EntityMotionState* motionState) { - motionState->initForBid(); - motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps()); - _bids.push_back(motionState); - _nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry()); + if (getEntityTree()->isServerlessMode()) { + EntityItemPointer entity = motionState->getEntity(); + auto nodeList = DependencyManager::get(); + auto sessionID = nodeList->getSessionUUID(); + entity->setSimulationOwner(SimulationOwner(sessionID, SCRIPT_GRAB_SIMULATION_PRIORITY)); + _owned.push_back(motionState); + } else { + motionState->initForBid(); + motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps()); + _bids.push_back(motionState); + _nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry()); + } } void PhysicalEntitySimulation::addOwnership(EntityMotionState* motionState) { diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index c155d5bd7f..19c4a62443 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -19,7 +19,7 @@ #include #include #include - +#include #include "ProceduralCommon_frag.h" #include "Logging.h" @@ -178,6 +178,8 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) { return; } _shaderPath = shaderUrl.toLocalFile(); + } else if (shaderUrl.scheme() == URL_SCHEME_QRC) { + _shaderPath = ":" + shaderUrl.path(); } else { _networkShader = ShaderCache::instance().getShader(shaderUrl); } diff --git a/libraries/test-utils/CMakeLists.txt b/libraries/test-utils/CMakeLists.txt new file mode 100644 index 0000000000..2c23e96c1e --- /dev/null +++ b/libraries/test-utils/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME test-utils) +setup_hifi_library(Network Gui) + diff --git a/libraries/test-utils/src/test-utils/FileDownloader.cpp b/libraries/test-utils/src/test-utils/FileDownloader.cpp new file mode 100644 index 0000000000..09049e3e0c --- /dev/null +++ b/libraries/test-utils/src/test-utils/FileDownloader.cpp @@ -0,0 +1,21 @@ +#include "FileDownloader.h" + +#include +#include + +FileDownloader::FileDownloader(QUrl url, const Handler& handler, QObject* parent) : QObject(parent), _handler(handler) { + connect(&_accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*))); + _accessManager.get(QNetworkRequest(url)); +} + +void FileDownloader::waitForDownload() { + while (!_complete) { + QCoreApplication::processEvents(); + } +} + +void FileDownloader::fileDownloaded(QNetworkReply* pReply) { + _handler(pReply->readAll()); + pReply->deleteLater(); + _complete = true; +} diff --git a/libraries/test-utils/src/test-utils/FileDownloader.h b/libraries/test-utils/src/test-utils/FileDownloader.h new file mode 100644 index 0000000000..5a618fcf45 --- /dev/null +++ b/libraries/test-utils/src/test-utils/FileDownloader.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +class FileDownloader : public QObject { + Q_OBJECT + +public: + using Handler = std::function; + + FileDownloader(QUrl url, const Handler& handler, QObject* parent = 0); + + void waitForDownload(); + +private slots: + void fileDownloaded(QNetworkReply* pReply); + +private: + QNetworkAccessManager _accessManager; + Handler _handler; + bool _complete { false }; +}; diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h index da9ea46cf4..5df20ff30c 100644 --- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h +++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h @@ -54,6 +54,12 @@ public: void setFullscreen(const QScreen* targetScreen, bool hideMenu = false); void unsetFullscreen(const QScreen* avoidScreen = nullptr); + // FIXME remove access tot he menu from the plugin container + // Instead let display plugins expose a structure about the kinds + // of actions and menu items they want to have appear when they are + // active and allow the application to act on that when the display + // plugin becomes active (or when the UI is initialized, and a + // display plugin is already active) virtual ui::Menu* getPrimaryMenu() = 0; virtual void showDisplayPluginsTools(bool show = true) = 0; virtual void requestReset() = 0; diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index d8f2db75c9..7e337070d3 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -166,12 +166,11 @@ void OculusBaseDisplayPlugin::deactivateSession() { //_session = nullptr; } void OculusBaseDisplayPlugin::updatePresentPose() { - //mat4 sensorResetMat; - //_currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); - //_currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, _currentFrame->frameIndex); - //auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse); - //_currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose); - _currentPresentFrameInfo.presentPose = _currentPresentFrameInfo.renderPose; + _currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); + _currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, 0); + auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse); + _currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose); + _currentPresentFrameInfo.renderPose = _currentPresentFrameInfo.presentPose; } OculusBaseDisplayPlugin::~OculusBaseDisplayPlugin() { diff --git a/scripts/system/+android/radar.js b/scripts/system/+android/radar.js index 455299dd5f..5d93ed4db1 100644 --- a/scripts/system/+android/radar.js +++ b/scripts/system/+android/radar.js @@ -21,7 +21,8 @@ function printd(str) { } var radar = false; -var radarHeight = 10; // camera position meters above the avatar +var RADAR_HEIGHT_INIT_DELTA = 10; +var radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA; // camera position (absolute y) var tablet; var RADAR_CAMERA_OFFSET = -1; // 1 meter below the avatar @@ -45,12 +46,12 @@ var uniqueColor; function moveTo(position) { if (radar) { - MyAvatar.position = position; - Camera.position = Vec3.sum(MyAvatar.position, { - x : 0, + MyAvatar.goToLocation(position, false); + Camera.position = { + x : position.x, y : radarHeight, - z : 0 - }); + z : position.z + }; } } @@ -89,46 +90,6 @@ function keyPressEvent(event) { } } -function actionOnObjectFromEvent(event) { - var rayIntersection = findRayIntersection(Camera.computePickRay(event.x, - event.y)); - if (rayIntersection && rayIntersection.intersects - && rayIntersection.overlayID) { - printd("found overlayID touched " + rayIntersection.overlayID); - if (entitiesByOverlayID[rayIntersection.overlayID]) { - var entity = Entities.getEntityProperties( - entitiesByOverlayID[rayIntersection.overlayID], - [ "sourceUrl" ]); - App.openUrl(entity.sourceUrl); - return true; - } - } - if (rayIntersection && rayIntersection.intersects - && rayIntersection.entityID && rayIntersection.properties) { - printd("found " + rayIntersection.entityID + " of type " - + rayIntersection.properties.type); - if (rayIntersection.properties.type == "Web") { - printd("found web element to " - + rayIntersection.properties.sourceUrl); - App.openUrl(rayIntersection.properties.sourceUrl); - return true; - } - } - return false; -} - -function mousePress(event) { - mousePressOrTouchEnd(event); -} - -function mousePressOrTouchEnd(event) { - if (radar) { - if (actionOnObjectFromEvent(event)) { - return; - } - } -} - function toggleRadarMode() { if (radar) { endRadar(); @@ -229,9 +190,6 @@ function touchEnd(event) { if (analyzeDoubleTap(event)) return; // double tap detected, finish - if (radar) { - mousePressOrTouchEnd(event); - } } /** @@ -386,12 +344,13 @@ function pinchUpdate(event) { radarHeight -= pinchIncrement; } } - var deltaHeight = avatarY + radarHeight - Camera.position.y; - Camera.position = Vec3.sum(Camera.position, { - x : 0, - y : deltaHeight, - z : 0 - }); + + Camera.position = { + x : Camera.position.x, + y : radarHeight, + z : Camera.position.z + }; + if (!draggingCamera) { startedDraggingCamera = true; draggingCamera = true; @@ -401,7 +360,8 @@ function pinchUpdate(event) { } function isInsideSquare(coords0, coords1, halfside) { - return Math.abs(coords0.x - coords1.x) <= halfside + return coords0 != undefined && coords1 != undefined && + Math.abs(coords0.x - coords1.x) <= halfside && Math.abs(coords0.y - coords1.y) <= halfside; } @@ -412,7 +372,7 @@ function dragScrollUpdate(event) { // drag management var pickRay = Camera.computePickRay(event.x, event.y); var dragAt = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, - radarHeight)); + radarHeight - MyAvatar.position.y)); if (lastDragAt === undefined || lastDragAt === null) { lastDragAt = dragAt; @@ -654,6 +614,7 @@ function Teleporter() { return; } + Camera.position = Vec3.sum(Camera.position, { x : xDelta, y : 0, @@ -722,7 +683,7 @@ function Teleporter() { return { dragTeleportBegin : function(event) { printd("[newTeleport] TELEPORT began"); - var overlayDimensions = entityIconModelDimensions(); + var overlayDimensions = teleportIconModelDimensions(MyAvatar.position.y); // var destination = computeDestination(event, MyAvatar.position, // Camera.position, radarHeight); // Dimension teleport and cancel overlays (not show them yet) @@ -843,7 +804,7 @@ var avatarIconDimensionsVal = { }; function avatarIconPlaneDimensions() { // given the current height, give a size - var xy = -0.003531 * radarHeight + 0.1; + var xy = -0.003531 * (radarHeight - MyAvatar.position.y) + 0.1; avatarIconDimensionsVal.x = Math.abs(xy); avatarIconDimensionsVal.y = Math.abs(xy); // reuse object @@ -1121,172 +1082,20 @@ function renderAllOthersAvatarIcons() { } } -function entityAdded(entityID) { - printd("Entity added " + entityID); - var props = Entities.getEntityProperties(entityID, [ "type" ]); - printd("Entity added " + entityID + " PROPS " + JSON.stringify(props)); - if (props && props.type == "Web") { - printd("Entity Web added " + entityID); - saveEntityData(entityID, true); - } -} - -function entityRemoved(entityID) { - printd("Entity removed " + entityID); - var props = Entities.getEntityProperties(entityID, [ "type" ]); - if (props && props.type == "Web") { - print("Entity Web removed " + entityID); - removeEntityData(entityID); - } -} - -/******************************************************************************* - * Entities (to remark) cache structure for showing entities markers - ******************************************************************************/ - -var entitiesData = {}; // by entityID -var entitiesByOverlayID = {}; // by overlayID -var entitiesIcons = []; // a parallel list of icons (overlays) to easily run - // through - -var ICON_ENTITY_WEB_MODEL_URL = Script.resolvePath("../assets/images/web.svg"); -var ICON_ENTITY_IMG_MODEL_URL = Script - .resolvePath("../assets/models/teleport-cancel.fbx"); // FIXME - use - // correct - // model&texture var ICON_ENTITY_DEFAULT_DIMENSIONS = { x : 0.10, y : 0.00001, z : 0.10 }; -var entityIconModelDimensionsVal = { - x : 0, - y : 0.00001, - z : 0 -}; -function entityIconModelDimensions() { - // given the current height, give a size - var xz = -0.002831 * radarHeight + 0.1; - entityIconModelDimensionsVal.x = xz; - entityIconModelDimensionsVal.z = xz; + +function teleportIconModelDimensions(y) { + var teleportModelDimensions = ICON_ENTITY_DEFAULT_DIMENSIONS; + var xz = -0.002831 * (radarHeight - y) + 0.1; + teleportModelDimensions.x = xz; + teleportModelDimensions.z = xz; // reuse object - return entityIconModelDimensionsVal; -} -/* - * entityIconPlaneDimensions: similar to entityIconModelDimensions but using xy - * plane - */ -function entityIconPlaneDimensions() { - var dim = entityIconModelDimensions(); - var z = dim.z; - dim.z = dim.y; - dim.y = z; - return dim; -} - -function currentOverlayForEntity(QUuid) { - if (entitiesData[QUuid] != undefined) { - return entitiesData[QUuid].icon; - } else { - return null; - } -} - -function saveEntityData(QUuid, planar) { - if (QUuid == null) - return; - var entity = Entities.getEntityProperties(QUuid, [ "position" ]); - printd("entity added save entity " + QUuid); - if (entitiesData[QUuid] != undefined) { - entitiesData[QUuid].position = entity.position; - } else { - var entityIcon = Overlays.addOverlay("image3d", { - subImage : { - x : 0, - y : 0, - width : 150, - height : 150 - }, - url : ICON_ENTITY_WEB_MODEL_URL, - dimensions : ICON_ENTITY_DEFAULT_DIMENSIONS, - visible : false, - ignoreRayIntersection : false, - orientation : Quat.fromPitchYawRollDegrees(-90, 0, 0) - }); - - entitiesIcons.push(entityIcon); - entitiesData[QUuid] = { - position : entity.position, - icon : entityIcon - }; - entitiesByOverlayID[entityIcon] = QUuid; - } -} - -function removeEntityData(QUuid) { - if (QUuid == null) - return; - - var itsOverlay = currentOverlayForEntity(QUuid); - if (itsOverlay != null) { - Overlays.deleteOverlay(itsOverlay); - delete entitiesByOverlayID[itsOverlay]; - } - var idx = entitiesIcons.indexOf(itsOverlay); - entitiesIcons.splice(idx, 1); - - delete entitiesData[QUuid]; -} - -/******************************************************************************* - * Entities to remark Icon/Markers rendering - ******************************************************************************/ - -function hideAllEntitiesIcons() { - var len = entitiesIcons.length; - for (var i = 0; i < len; i++) { - Overlays.editOverlay(entitiesIcons[i], { - visible : false - }); - } -} - -function renderAllEntitiesIcons() { - var entityPos; - var entityProps; - var iconDimensions = entityIconModelDimensions(); - var planeDimensions = entityIconPlaneDimensions(); // plane overlays uses - // xy instead of xz - for ( var QUuid in entitiesData) { - if (entitiesData.hasOwnProperty(QUuid)) { - entityProps = Entities.getEntityProperties(QUuid, [ "position", - "visible" ]); - if (entityProps != null) { - entityPos = entityProps.position; - if (entitiesData[QUuid].icon != undefined && entityPos) { - var iconPos = findLineToHeightIntersectionCoords( - entityPos.x, - entityPos.y - + RADAR_ICONS_APPARENT_DISTANCE_TO_AVATAR_BASE, - entityPos.z, Camera.position.x, Camera.position.y, - Camera.position.z, Camera.position.y - - RADAR_CAMERA_DISTANCE_TO_ICONS); - if (!iconPos) { - printd("entity icon pos bad for " + QUuid); - continue; - } - var dimensions = entitiesData[QUuid].planar ? planeDimensions - : iconDimensions; - Overlays.editOverlay(entitiesData[QUuid].icon, { - visible : entityProps.visible, - dimensions : dimensions, - position : iconPos - }); - } - } - } - } + return teleportModelDimensions; } /******************************************************************************* @@ -1298,11 +1107,8 @@ function startRadar() { saveAllOthersAvatarsData(); Camera.mode = "independent"; - Camera.position = Vec3.sum(MyAvatar.position, { - x : 0, - y : radarHeight, - z : 0 - }); + initCameraOverMyAvatar(); + Camera.orientation = Quat.fromPitchYawRollDegrees(-90, 0, 0); radar = true; @@ -1319,7 +1125,6 @@ function endRadar() { Controller.setVPadEnabled(true); disconnectRadarModeEvents(); - hideAllEntitiesIcons(); hideAllAvatarIcons(); } @@ -1353,12 +1158,10 @@ function updateRadar() { // Update avatar icons if (startedDraggingCamera) { hideAllAvatarIcons(); - hideAllEntitiesIcons(); startedDraggingCamera = false; } else if (!draggingCamera) { renderMyAvatarIcon(); renderAllOthersAvatarIcons(); - renderAllEntitiesIcons(); } } @@ -1366,48 +1169,41 @@ function valueIfDefined(value) { return value !== undefined ? value : ""; } -function entitiesAnalysis() { - var ids = Entities.findEntitiesInFrustum(Camera.frustum); - var entities = []; - for (var i = 0; i < ids.length; i++) { - var id = ids[i]; - var properties = Entities.getEntityProperties(id); - entities.push({ - id : id, - name : properties.name, - type : properties.type, - url : properties.type == "Model" ? properties.modelURL : "", - sourceUrl : properties.sourceUrl, - locked : properties.locked, - visible : properties.visible, - drawCalls : valueIfDefined(properties.renderInfo.drawCalls), - hasScript : properties.script !== "" - }); - } -} - function connectRadarModeEvents() { Script.update.connect(updateRadar); // 60Hz loop Controller.keyPressEvent.connect(keyPressEvent); - Controller.mousePressEvent.connect(mousePress); // single click/touch Controller.touchUpdateEvent.connect(touchUpdate); + Window.domainChanged.connect(domainChanged); MyAvatar.positionGoneTo.connect(positionGoneTo); } -function positionGoneTo() { - Camera.position = Vec3.sum(MyAvatar.position, { - x : 0, +function initCameraOverMyAvatar() { + radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA; + Camera.position = { + x : MyAvatar.position.x, y : radarHeight, - z : 0 - }); + z : MyAvatar.position.z + }; +} + +function domainChanged() { + initCameraOverMyAvatar(); +} + +function positionGoneTo() { + Camera.position = { + x : MyAvatar.position.x, + y : radarHeight, + z : MyAvatar.position.z + }; } function disconnectRadarModeEvents() { Script.update.disconnect(updateRadar); Controller.keyPressEvent.disconnect(keyPressEvent); - Controller.mousePressEvent.disconnect(mousePress); Controller.touchUpdateEvent.disconnect(touchUpdate); MyAvatar.positionGoneTo.disconnect(positionGoneTo); + Window.domainChanged.disconnect(domainChanged); } function init() { @@ -1418,7 +1214,4 @@ function init() { AvatarList.avatarAddedEvent.connect(avatarAdded); AvatarList.avatarRemovedEvent.connect(avatarRemoved); - - Entities.addingEntity.connect(entityAdded); - Entities.deletingEntity.connect(entityRemoved); } diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index b9fd7f725c..2d4a2d3e97 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -18,7 +18,7 @@ var headset; // The preferred headset. Default to the first one found in the following list. var displayMenuName = "Display"; var desktopMenuItemName = "Desktop"; -['OpenVR (Vive)', 'Oculus Rift'].forEach(function (name) { +['HTC Vive', 'Oculus Rift', 'WindowMS'].forEach(function (name) { if (!headset && Menu.menuItemExists(displayMenuName, name)) { headset = name; } diff --git a/tests-manual/CMakeLists.txt b/tests-manual/CMakeLists.txt new file mode 100644 index 0000000000..bc64183298 --- /dev/null +++ b/tests-manual/CMakeLists.txt @@ -0,0 +1,8 @@ +# add the manual test directories +file(GLOB TEST_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*") +foreach(DIR ${TEST_SUBDIRS}) + if((IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}") AND (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}/CMakeLists.txt")) + set(TEST_PROJ_NAME ${DIR}) + add_subdirectory(${DIR}) + endif() +endforeach() diff --git a/tests/controllers/CMakeLists.txt b/tests-manual/controllers/CMakeLists.txt similarity index 100% rename from tests/controllers/CMakeLists.txt rename to tests-manual/controllers/CMakeLists.txt diff --git a/tests/controllers/qml/main.qml b/tests-manual/controllers/qml/main.qml similarity index 100% rename from tests/controllers/qml/main.qml rename to tests-manual/controllers/qml/main.qml diff --git a/tests/controllers/src/main.cpp b/tests-manual/controllers/src/main.cpp similarity index 100% rename from tests/controllers/src/main.cpp rename to tests-manual/controllers/src/main.cpp diff --git a/tests/entities/CMakeLists.txt b/tests-manual/entities/CMakeLists.txt similarity index 100% rename from tests/entities/CMakeLists.txt rename to tests-manual/entities/CMakeLists.txt diff --git a/tests/entities/packet.bin b/tests-manual/entities/packet.bin similarity index 100% rename from tests/entities/packet.bin rename to tests-manual/entities/packet.bin diff --git a/tests/entities/src/main.cpp b/tests-manual/entities/src/main.cpp similarity index 100% rename from tests/entities/src/main.cpp rename to tests-manual/entities/src/main.cpp diff --git a/tests/gl/CMakeLists.txt b/tests-manual/gl/CMakeLists.txt similarity index 100% rename from tests/gl/CMakeLists.txt rename to tests-manual/gl/CMakeLists.txt diff --git a/tests/gl/src/main.cpp b/tests-manual/gl/src/main.cpp similarity index 100% rename from tests/gl/src/main.cpp rename to tests-manual/gl/src/main.cpp diff --git a/tests-manual/gpu-textures/CMakeLists.txt b/tests-manual/gpu-textures/CMakeLists.txt new file mode 100644 index 0000000000..c10f2eda3f --- /dev/null +++ b/tests-manual/gpu-textures/CMakeLists.txt @@ -0,0 +1,16 @@ +set(TARGET_NAME gpu-textures-tests) +AUTOSCRIBE_SHADER_LIB(gpu graphics render-utils) +# This is not a testcase -- just set it up as a regular hifi project +setup_hifi_project(Quick Gui Script) +setup_memory_debugger() +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") +link_hifi_libraries( + shared task networking gl + ktx gpu octree + ${PLATFORM_GL_BACKEND} +) + +set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/qml\"") +package_libraries_for_deployment() + +target_nsight() diff --git a/tests-manual/gpu-textures/qml/textureStats.qml b/tests-manual/gpu-textures/qml/textureStats.qml new file mode 100644 index 0000000000..d3700c3eac --- /dev/null +++ b/tests-manual/gpu-textures/qml/textureStats.qml @@ -0,0 +1,52 @@ +import QtQuick 2.5 +import QtQuick.Controls 2.3 + +Item { + width: 400 + height: 600 + + Column { + spacing: 10 + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 10 + + Text { text: qsTr("Total") } + Text { text: Stats.total + " MB" } + Text { text: qsTr("Allocated") } + Text { text: Stats.allocated } + Text { text: qsTr("Populated") } + Text { text: Stats.populated } + Text { text: qsTr("Pending") } + Text { text: Stats.pending } + Text { text: qsTr("Current Index") } + Text { text: Stats.index } + Text { text: qsTr("Current Source") } + Text { text: Stats.source } + Text { text: qsTr("Current Rez") } + Text { text: Stats.rez.width + " x " + Stats.rez.height } + } + + Row { + id: row1 + spacing: 10 + anchors.bottom: row2.top + anchors.left: parent.left + anchors.margins: 10 + Button { text: "1024"; onClicked: Stats.maxTextureMemory(1024); } + Button { text: "256"; onClicked: Stats.maxTextureMemory(256); } + } + + Row { + id: row2 + spacing: 10 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.margins: 10 + Button { text: "Change Textures"; onClicked: Stats.changeTextures(); } + Button { text: "Next"; onClicked: Stats.nextTexture(); } + Button { text: "Previous"; onClicked: Stats.prevTexture(); } + } + +} + diff --git a/tests-manual/gpu-textures/src/TestHelpers.cpp b/tests-manual/gpu-textures/src/TestHelpers.cpp new file mode 100644 index 0000000000..f952a4385f --- /dev/null +++ b/tests-manual/gpu-textures/src/TestHelpers.cpp @@ -0,0 +1,26 @@ +// +// Created by Bradley Austin Davis on 2016/05/16 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "TestHelpers.h" +#include + +gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) { + auto vs = gpu::Shader::createVertex(vertexShaderSrc); + auto fs = gpu::Shader::createPixel(fragmentShaderSrc); + auto shader = gpu::Shader::createProgram(vs, fs); + if (!gpu::Shader::makeProgram(*shader, bindings)) { + printf("Could not compile shader\n"); + exit(-1); + } + return shader; +} + +QString projectRootDir() { + static QString projectRootPath = QFileInfo(QFileInfo(__FILE__).absolutePath() + "/..").absoluteFilePath(); + return projectRootPath; +} diff --git a/tests-manual/gpu-textures/src/TestHelpers.h b/tests-manual/gpu-textures/src/TestHelpers.h new file mode 100644 index 0000000000..17730c3642 --- /dev/null +++ b/tests-manual/gpu-textures/src/TestHelpers.h @@ -0,0 +1,40 @@ +// +// Created by Bradley Austin Davis on 2016/05/16 +// Copyright 2014 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 +// + +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include +#include + +struct RenderArgs { + gpu::ContextPointer _context; + ivec4 _viewport; + gpu::Batch* _batch; +}; + +class GpuTestBase : public QObject { +public: + virtual ~GpuTestBase() {} + virtual bool isReady() const { return true; } + virtual size_t getTestCount() const { return 1; } + virtual void renderTest(size_t test, const RenderArgs& args) = 0; + virtual QObject * statsObject() { return nullptr; } + virtual QUrl statUrl() { return QUrl(); } +}; + +uint32_t toCompactColor(const glm::vec4& color); +gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings); +QString projectRootDir(); diff --git a/tests-manual/gpu-textures/src/TestTextures.cpp b/tests-manual/gpu-textures/src/TestTextures.cpp new file mode 100644 index 0000000000..7aedb506da --- /dev/null +++ b/tests-manual/gpu-textures/src/TestTextures.cpp @@ -0,0 +1,166 @@ +// +// Created by Bradley Austin Davis on 2016/05/16 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "TestTextures.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "TestHelpers.h" + +std::string vertexShaderSource = R"SHADER( +#line 14 +layout(location = 0) out vec2 outTexCoord0; + +const vec4 VERTICES[] = vec4[]( + vec4(-1.0, -1.0, 0.0, 1.0), + vec4( 1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4( 1.0, 1.0, 0.0, 1.0) +); + +void main() { + outTexCoord0 = VERTICES[gl_VertexID].xy; + outTexCoord0 += 1.0; + outTexCoord0 /= 2.0; + gl_Position = VERTICES[gl_VertexID]; +} +)SHADER"; + +std::string fragmentShaderSource = R"SHADER( +#line 28 + +uniform sampler2D tex; + +layout(location = 0) in vec2 inTexCoord0; +layout(location = 0) out vec4 outFragColor; + +void main() { + outFragColor = texture(tex, inTexCoord0); + outFragColor.a = 1.0; + //outFragColor.rb = inTexCoord0; +} + +)SHADER"; + +#define STAT_UPDATE(name, src) \ + { \ + auto val = src; \ + if (_##name != val) { \ + _##name = val; \ + emit name##Changed(); \ + } \ + } + + +void TextureTestStats::update(int curIndex, const gpu::TexturePointer& texture) { + STAT_UPDATE(total, (int)BYTES_TO_MB(gpu::Context::getTextureGPUMemSize())); + STAT_UPDATE(allocated, (int)gpu::Context::getTextureResourceGPUMemSize()); + STAT_UPDATE(pending, (int)gpu::Context::getTexturePendingGPUTransferMemSize()); + STAT_UPDATE(populated, (int)gpu::Context::getTextureResourcePopulatedGPUMemSize()); + STAT_UPDATE(source, texture->source().c_str()); + STAT_UPDATE(index, curIndex); + auto dims = texture->getDimensions(); + STAT_UPDATE(rez, QSize(dims.x, dims.y)); +} + +TexturesTest::TexturesTest() { + connect(&stats, &TextureTestStats::changeTextures, this, &TexturesTest::onChangeTextures); + connect(&stats, &TextureTestStats::nextTexture, this, &TexturesTest::onNextTexture); + connect(&stats, &TextureTestStats::prevTexture, this, &TexturesTest::onPrevTexture); + connect(&stats, &TextureTestStats::maxTextureMemory, this, &TexturesTest::onMaxTextureMemory); + { + auto VS = gpu::Shader::createVertex(vertexShaderSource); + auto PS = gpu::Shader::createPixel(fragmentShaderSource); + auto program = gpu::Shader::createProgram(VS, PS); + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + // If the pipeline did not exist, make it + auto state = std::make_shared(); + state->setCullMode(gpu::State::CULL_NONE); + state->setDepthTest({}); + state->setBlendFunction({ false }); + pipeline = gpu::Pipeline::create(program, state); + } + + onChangeTextures(); +} + + +void TexturesTest::renderTest(size_t testId, const RenderArgs& args) { + stats.update((int)index, textures[index]); + gpu::Batch& batch = *(args._batch); + batch.setPipeline(pipeline); + batch.setInputFormat(vertexFormat); + for (const auto& texture : textures) { + batch.setResourceTexture(0, texture); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); + } + batch.setResourceTexture(0, textures[index]); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); +} + +#define LOAD_TEXTURE_COUNT 64 + +void TexturesTest::onChangeTextures() { + static const QDir TEST_DIR("D:/ktx_texture_test"); + static std::vector ALL_TEXTURE_FILES; + if (ALL_TEXTURE_FILES.empty()) { + auto entryList = TEST_DIR.entryList({ "*.ktx" }, QDir::Filter::Files); + ALL_TEXTURE_FILES.reserve(entryList.size()); + for (auto entry : entryList) { + auto textureFile = TEST_DIR.absoluteFilePath(entry).toStdString(); + ALL_TEXTURE_FILES.push_back(textureFile); + } + } + + oldTextures.clear(); + oldTextures.swap(textures); + +#if 0 + static const std::string bad = "D:/ktx_texture_test/b4beed38675dbc7a827ecd576399c1f4.ktx"; + auto texture = gpu::Texture::unserialize(bad); + auto texelFormat = texture->getTexelFormat(); + qDebug() << texture->getTexelFormat().getSemantic(); + qDebug() << texture->getTexelFormat().getScalarCount(); + textures.push_back(texture); +#else + std::shuffle(ALL_TEXTURE_FILES.begin(), ALL_TEXTURE_FILES.end(), std::default_random_engine()); + size_t newTextureCount = std::min(ALL_TEXTURE_FILES.size(), LOAD_TEXTURE_COUNT); + for (size_t i = 0; i < newTextureCount; ++i) { + const auto& textureFile = ALL_TEXTURE_FILES[i]; + auto texture = gpu::Texture::unserialize(textureFile); + qDebug() << textureFile.c_str(); + qDebug() << texture->getTexelFormat().getSemantic(); + qDebug() << texture->getTexelFormat().getScalarCount(); + textures.push_back(texture); + } +#endif + index = 0; + qDebug() << "Done"; +} + +void TexturesTest::onNextTexture() { + index += textures.size() + 1; + index %= textures.size(); +} + +void TexturesTest::onPrevTexture() { + index += textures.size() - 1; + index %= textures.size(); +} + +void TexturesTest::onMaxTextureMemory(int maxTextureMemory) { + gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(maxTextureMemory)); +} diff --git a/tests-manual/gpu-textures/src/TestTextures.h b/tests-manual/gpu-textures/src/TestTextures.h new file mode 100644 index 0000000000..e446ce6132 --- /dev/null +++ b/tests-manual/gpu-textures/src/TestTextures.h @@ -0,0 +1,74 @@ +// +// Created by Bradley Austin Davis on 2016/05/16 +// Copyright 2014 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 +// +#pragma once + +#include "TestHelpers.h" + +#define STATS_PROPERTY(type, name, initialValue) \ + Q_PROPERTY(type name READ name NOTIFY name##Changed) \ +public: \ + type name() { return _##name; }; \ +private: \ + type _##name{ initialValue }; + + +class TextureTestStats : public QObject { + Q_OBJECT; + STATS_PROPERTY(int, pending, 0) + STATS_PROPERTY(int, total, 0) + STATS_PROPERTY(int, populated, 0) + STATS_PROPERTY(int, allocated, 0) + STATS_PROPERTY(int, index, 0) + + STATS_PROPERTY(QString, source, QString()) + STATS_PROPERTY(QSize, rez, QSize(0, 0)) + +public: + void update(int index, const gpu::TexturePointer& texture); + +signals: + void pendingChanged(); + void totalChanged(); + void populatedChanged(); + void allocatedChanged(); + void changeTextures(); + void rezChanged(); + void indexChanged(); + void sourceChanged(); + void maxTextureMemory(int); + + void nextTexture(); + void prevTexture(); +}; + + +class TexturesTest : public GpuTestBase { + Q_OBJECT + + gpu::Stream::FormatPointer vertexFormat { std::make_shared() }; + std::vector textures; + std::vector oldTextures; + gpu::PipelinePointer pipeline; + TextureTestStats stats; + size_t index{ 0 }; + +public: + TexturesTest(); + QObject* statsObject() override { return &stats; } + QUrl statUrl() override { return QUrl::fromLocalFile(projectRootDir() + "/qml/textureStats.qml"); } + void renderTest(size_t testId, const RenderArgs& args) override; + +protected slots: + void onChangeTextures(); + void onMaxTextureMemory(int newValue); + void onNextTexture(); + void onPrevTexture(); + +}; + + diff --git a/tests-manual/gpu-textures/src/TestWindow.cpp b/tests-manual/gpu-textures/src/TestWindow.cpp new file mode 100644 index 0000000000..038b0b9b80 --- /dev/null +++ b/tests-manual/gpu-textures/src/TestWindow.cpp @@ -0,0 +1,117 @@ +// +// Created by Bradley Austin Davis on 2016/05/16 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "TestWindow.h" + +#include +#include + +#include +#include + +#include + +#include + +TestWindow::TestWindow() { + + auto timer = new QTimer(this); + timer->setTimerType(Qt::PreciseTimer); + timer->setInterval(5); + connect(timer, &QTimer::timeout, [&] { draw(); }); + timer->start(); + + connect(qApp, &QCoreApplication::aboutToQuit, [this, timer] { + timer->stop(); + _aboutToQuit = true; + }); + + setSurfaceType(QSurface::OpenGLSurface); + + QSurfaceFormat format = getDefaultOpenGLSurfaceFormat(); + format.setOption(QSurfaceFormat::DebugContext); + setFormat(format); + _glContext.setFormat(format); + _glContext.create(); + _glContext.makeCurrent(this); + + show(); +} + +void TestWindow::initGl() { + _glContext.makeCurrent(this); + gl::initModuleGl(); + gpu::Context::init(); + _renderArgs->_context = std::make_shared(); + _glContext.makeCurrent(this); + resize(QSize(800, 600)); +} + +void TestWindow::resizeWindow(const QSize& size) { + _size = size; + _renderArgs->_viewport = ivec4(0, 0, _size.width(), _size.height()); +} + +void TestWindow::beginFrame() { + _renderArgs->_context->recycle(); + _renderArgs->_context->beginFrame(); + gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) { + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f }); + batch.clearDepthFramebuffer(1e4); + batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() }); + }); + + gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) { + batch.setViewportTransform(_renderArgs->_viewport); + batch.setStateScissorRect(_renderArgs->_viewport); + batch.setProjectionTransform(_projectionMatrix); + }); +} + +void TestWindow::endFrame() { + gpu::doInBatch("TestWindow::endFrame::finish", _renderArgs->_context, [&](gpu::Batch& batch) { + batch.resetStages(); + }); + auto framePointer = _renderArgs->_context->endFrame(); + _renderArgs->_context->consumeFrameUpdates(framePointer); + _renderArgs->_context->executeFrame(framePointer); + _glContext.swapBuffers(this); +} + +void TestWindow::draw() { + if (_aboutToQuit) { + return; + } + + // Attempting to draw before we're visible and have a valid size will + // produce GL errors. + if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) { + return; + } + + if (!_glContext.makeCurrent(this)) { + return; + } + + static std::once_flag once; + std::call_once(once, [&] { initGl(); }); + beginFrame(); + + renderFrame(); + + endFrame(); +} + +void TestWindow::resizeEvent(QResizeEvent* ev) { + resizeWindow(ev->size()); + float fov_degrees = 60.0f; + float aspect_ratio = (float)_size.width() / _size.height(); + float near_clip = 0.1f; + float far_clip = 1000.0f; + _projectionMatrix = glm::perspective(glm::radians(fov_degrees), aspect_ratio, near_clip, far_clip); +} diff --git a/tests-manual/gpu-textures/src/TestWindow.h b/tests-manual/gpu-textures/src/TestWindow.h new file mode 100644 index 0000000000..00bbf03836 --- /dev/null +++ b/tests-manual/gpu-textures/src/TestWindow.h @@ -0,0 +1,41 @@ +// +// Created by Bradley Austin Davis on 2016/05/16 +// Copyright 2014 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 +// + +#pragma once + +#include +#include + +#include +#include +#include +#include "TestHelpers.h" + +#define DEFERRED_LIGHTING + +class TestWindow : public QWindow { +protected: + QOpenGLContextWrapper _glContext; + QSize _size; + glm::mat4 _projectionMatrix; + bool _aboutToQuit { false }; + std::shared_ptr _renderArgs{ std::make_shared() }; + + TestWindow(); + virtual void initGl(); + virtual void renderFrame() = 0; + +private: + void resizeWindow(const QSize& size); + + void beginFrame(); + void endFrame(); + void draw(); + void resizeEvent(QResizeEvent* ev) override; +}; + diff --git a/tests-manual/gpu-textures/src/main.cpp b/tests-manual/gpu-textures/src/main.cpp new file mode 100644 index 0000000000..3d0051dc1d --- /dev/null +++ b/tests-manual/gpu-textures/src/main.cpp @@ -0,0 +1,170 @@ +// +// main.cpp +// tests/gpu-test/src +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "TestWindow.h" +#include "TestTextures.h" + +using TestBuilder = std::function; +using TestBuilders = std::list; + +#define INTERACTIVE + +class MyTestWindow : public TestWindow { + using Parent = TestWindow; + TestBuilders _testBuilders; + GpuTestBase* _currentTest{ nullptr }; + size_t _currentTestId{ 0 }; + size_t _currentMaxTests{ 0 }; + glm::mat4 _camera; + QTime _time; + + void initGl() override { + Parent::initGl(); + _time.start(); + updateCamera(); + _testBuilders = TestBuilders({ + [] { return new TexturesTest(); }, + }); + } + + void updateCamera() { + float t = _time.elapsed() * 1e-3f; + glm::vec3 unitscale{ 1.0f }; + glm::vec3 up{ 0.0f, 1.0f, 0.0f }; + + float distance = 3.0f; + glm::vec3 camera_position{ distance * sinf(t), 0.5f, distance * cosf(t) }; + + static const vec3 camera_focus(0); + static const vec3 camera_up(0, 1, 0); + _camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up)); + + ViewFrustum frustum; + frustum.setPosition(camera_position); + frustum.setOrientation(glm::quat_cast(_camera)); + frustum.setProjection(_projectionMatrix); + } + + void renderFrame() override { + updateCamera(); + + while ((!_currentTest || (_currentTestId >= _currentMaxTests)) && !_testBuilders.empty()) { + if (_currentTest) { + delete _currentTest; + _currentTest = nullptr; + } + + _currentTest = _testBuilders.front()(); + _testBuilders.pop_front(); + + if (_currentTest) { + auto statsObject = _currentTest->statsObject(); + QUrl url = _currentTest->statUrl(); + if (statsObject) { + auto screens = qApp->screens(); + auto primaryScreen = qApp->primaryScreen(); + auto targetScreen = primaryScreen; + for (const auto& screen : screens) { + if (screen == primaryScreen) { + continue; + } + targetScreen = screen; + break; + } + + auto destPoint = targetScreen->availableGeometry().topLeft(); + QQuickView* view = new QQuickView(); + view->rootContext()->setContextProperty("Stats", statsObject); + view->setSource(url); + view->show(); + view->setPosition({ destPoint.x() + 100, destPoint.y() + 100 }); + } + _currentMaxTests = _currentTest->getTestCount(); + _currentTestId = 0; + } + } + + if (!_currentTest && _testBuilders.empty()) { + qApp->quit(); + return; + } + + // Tests might need to wait for resources to download + if (!_currentTest->isReady()) { + return; + } + + gpu::doInBatch("main::renderFrame", _renderArgs->_context, [&](gpu::Batch& batch) { + batch.setViewTransform(_camera); + _renderArgs->_batch = &batch; + _currentTest->renderTest(_currentTestId, *_renderArgs); + _renderArgs->_batch = nullptr; + }); + } +}; + +int main(int argc, char** argv) { + setupHifiApplication("GPU Test"); + qputenv("HIFI_DEBUG_OPENGL", QByteArray("1")); + QApplication app(argc, argv); + MyTestWindow window; + app.exec(); + + return 0; +} diff --git a/tests/gpu-test/CMakeLists.txt b/tests-manual/gpu/CMakeLists.txt similarity index 100% rename from tests/gpu-test/CMakeLists.txt rename to tests-manual/gpu/CMakeLists.txt diff --git a/tests/gpu-test/src/TestFbx.cpp b/tests-manual/gpu/src/TestFbx.cpp similarity index 100% rename from tests/gpu-test/src/TestFbx.cpp rename to tests-manual/gpu/src/TestFbx.cpp diff --git a/tests/gpu-test/src/TestFbx.h b/tests-manual/gpu/src/TestFbx.h similarity index 100% rename from tests/gpu-test/src/TestFbx.h rename to tests-manual/gpu/src/TestFbx.h diff --git a/tests/gpu-test/src/TestFloorGrid.cpp b/tests-manual/gpu/src/TestFloorGrid.cpp similarity index 100% rename from tests/gpu-test/src/TestFloorGrid.cpp rename to tests-manual/gpu/src/TestFloorGrid.cpp diff --git a/tests/gpu-test/src/TestFloorGrid.h b/tests-manual/gpu/src/TestFloorGrid.h similarity index 100% rename from tests/gpu-test/src/TestFloorGrid.h rename to tests-manual/gpu/src/TestFloorGrid.h diff --git a/tests/gpu-test/src/TestFloorTexture.cpp b/tests-manual/gpu/src/TestFloorTexture.cpp similarity index 100% rename from tests/gpu-test/src/TestFloorTexture.cpp rename to tests-manual/gpu/src/TestFloorTexture.cpp diff --git a/tests/gpu-test/src/TestFloorTexture.h b/tests-manual/gpu/src/TestFloorTexture.h similarity index 100% rename from tests/gpu-test/src/TestFloorTexture.h rename to tests-manual/gpu/src/TestFloorTexture.h diff --git a/tests/gpu-test/src/TestHelpers.cpp b/tests-manual/gpu/src/TestHelpers.cpp similarity index 100% rename from tests/gpu-test/src/TestHelpers.cpp rename to tests-manual/gpu/src/TestHelpers.cpp diff --git a/tests/gpu-test/src/TestHelpers.h b/tests-manual/gpu/src/TestHelpers.h similarity index 100% rename from tests/gpu-test/src/TestHelpers.h rename to tests-manual/gpu/src/TestHelpers.h diff --git a/tests/gpu-test/src/TestInstancedShapes.cpp b/tests-manual/gpu/src/TestInstancedShapes.cpp similarity index 100% rename from tests/gpu-test/src/TestInstancedShapes.cpp rename to tests-manual/gpu/src/TestInstancedShapes.cpp diff --git a/tests/gpu-test/src/TestInstancedShapes.h b/tests-manual/gpu/src/TestInstancedShapes.h similarity index 100% rename from tests/gpu-test/src/TestInstancedShapes.h rename to tests-manual/gpu/src/TestInstancedShapes.h diff --git a/tests/gpu-test/src/TestShapes.cpp b/tests-manual/gpu/src/TestShapes.cpp similarity index 100% rename from tests/gpu-test/src/TestShapes.cpp rename to tests-manual/gpu/src/TestShapes.cpp diff --git a/tests/gpu-test/src/TestShapes.h b/tests-manual/gpu/src/TestShapes.h similarity index 100% rename from tests/gpu-test/src/TestShapes.h rename to tests-manual/gpu/src/TestShapes.h diff --git a/tests/gpu-test/src/TestWindow.cpp b/tests-manual/gpu/src/TestWindow.cpp similarity index 100% rename from tests/gpu-test/src/TestWindow.cpp rename to tests-manual/gpu/src/TestWindow.cpp diff --git a/tests/gpu-test/src/TestWindow.h b/tests-manual/gpu/src/TestWindow.h similarity index 100% rename from tests/gpu-test/src/TestWindow.h rename to tests-manual/gpu/src/TestWindow.h diff --git a/tests/gpu-test/src/main.cpp b/tests-manual/gpu/src/main.cpp similarity index 100% rename from tests/gpu-test/src/main.cpp rename to tests-manual/gpu/src/main.cpp diff --git a/tests/qml/CMakeLists.txt b/tests-manual/qml/CMakeLists.txt similarity index 100% rename from tests/qml/CMakeLists.txt rename to tests-manual/qml/CMakeLists.txt diff --git a/tests/qml/qml/controls/WebEntityView.qml b/tests-manual/qml/qml/controls/WebEntityView.qml similarity index 100% rename from tests/qml/qml/controls/WebEntityView.qml rename to tests-manual/qml/qml/controls/WebEntityView.qml diff --git a/tests/qml/qml/main.qml b/tests-manual/qml/qml/main.qml similarity index 100% rename from tests/qml/qml/main.qml rename to tests-manual/qml/qml/main.qml diff --git a/tests/qml/qml/qml/+android/UI.js b/tests-manual/qml/qml/qml/+android/UI.js similarity index 100% rename from tests/qml/qml/qml/+android/UI.js rename to tests-manual/qml/qml/qml/+android/UI.js diff --git a/tests/qml/qml/qml/+ios/UI.js b/tests-manual/qml/qml/qml/+ios/UI.js similarity index 100% rename from tests/qml/qml/qml/+ios/UI.js rename to tests-manual/qml/qml/qml/+ios/UI.js diff --git a/tests/qml/qml/qml/+osx/UI.js b/tests-manual/qml/qml/qml/+osx/UI.js similarity index 100% rename from tests/qml/qml/qml/+osx/UI.js rename to tests-manual/qml/qml/qml/+osx/UI.js diff --git a/tests/qml/qml/qml/ButtonPage.qml b/tests-manual/qml/qml/qml/ButtonPage.qml similarity index 100% rename from tests/qml/qml/qml/ButtonPage.qml rename to tests-manual/qml/qml/qml/ButtonPage.qml diff --git a/tests/qml/qml/qml/InputPage.qml b/tests-manual/qml/qml/qml/InputPage.qml similarity index 100% rename from tests/qml/qml/qml/InputPage.qml rename to tests-manual/qml/qml/qml/InputPage.qml diff --git a/tests/qml/qml/qml/ProgressPage.qml b/tests-manual/qml/qml/qml/ProgressPage.qml similarity index 100% rename from tests/qml/qml/qml/ProgressPage.qml rename to tests-manual/qml/qml/qml/ProgressPage.qml diff --git a/tests/qml/qml/qml/UI.js b/tests-manual/qml/qml/qml/UI.js similarity index 100% rename from tests/qml/qml/qml/UI.js rename to tests-manual/qml/qml/qml/UI.js diff --git a/tests/qml/src/main.cpp b/tests-manual/qml/src/main.cpp similarity index 100% rename from tests/qml/src/main.cpp rename to tests-manual/qml/src/main.cpp diff --git a/tests/render-perf/CMakeLists.txt b/tests-manual/render-perf/CMakeLists.txt similarity index 100% rename from tests/render-perf/CMakeLists.txt rename to tests-manual/render-perf/CMakeLists.txt diff --git a/tests/render-perf/src/Camera.hpp b/tests-manual/render-perf/src/Camera.hpp similarity index 100% rename from tests/render-perf/src/Camera.hpp rename to tests-manual/render-perf/src/Camera.hpp diff --git a/tests/render-perf/src/main.cpp b/tests-manual/render-perf/src/main.cpp similarity index 100% rename from tests/render-perf/src/main.cpp rename to tests-manual/render-perf/src/main.cpp diff --git a/tests/render-texture-load/CMakeLists.txt b/tests-manual/render-texture-load/CMakeLists.txt similarity index 100% rename from tests/render-texture-load/CMakeLists.txt rename to tests-manual/render-texture-load/CMakeLists.txt diff --git a/tests/render-texture-load/src/GLIHelpers.cpp b/tests-manual/render-texture-load/src/GLIHelpers.cpp similarity index 100% rename from tests/render-texture-load/src/GLIHelpers.cpp rename to tests-manual/render-texture-load/src/GLIHelpers.cpp diff --git a/tests/render-texture-load/src/GLIHelpers.h b/tests-manual/render-texture-load/src/GLIHelpers.h similarity index 100% rename from tests/render-texture-load/src/GLIHelpers.h rename to tests-manual/render-texture-load/src/GLIHelpers.h diff --git a/tests/render-texture-load/src/main.cpp b/tests-manual/render-texture-load/src/main.cpp similarity index 100% rename from tests/render-texture-load/src/main.cpp rename to tests-manual/render-texture-load/src/main.cpp diff --git a/tests/render-utils/CMakeLists.txt b/tests-manual/render-utils/CMakeLists.txt similarity index 100% rename from tests/render-utils/CMakeLists.txt rename to tests-manual/render-utils/CMakeLists.txt diff --git a/tests/render-utils/src/main.cpp b/tests-manual/render-utils/src/main.cpp similarity index 100% rename from tests/render-utils/src/main.cpp rename to tests-manual/render-utils/src/main.cpp diff --git a/tests/shaders/CMakeLists.txt b/tests-manual/shaders/CMakeLists.txt similarity index 100% rename from tests/shaders/CMakeLists.txt rename to tests-manual/shaders/CMakeLists.txt diff --git a/tests/shaders/src/main.cpp b/tests-manual/shaders/src/main.cpp similarity index 100% rename from tests/shaders/src/main.cpp rename to tests-manual/shaders/src/main.cpp diff --git a/tests/ui/qml/Palettes.qml b/tests-manual/ui/qml/Palettes.qml similarity index 100% rename from tests/ui/qml/Palettes.qml rename to tests-manual/ui/qml/Palettes.qml diff --git a/tests/ui/qml/ScrollingGraph.qml b/tests-manual/ui/qml/ScrollingGraph.qml similarity index 100% rename from tests/ui/qml/ScrollingGraph.qml rename to tests-manual/ui/qml/ScrollingGraph.qml diff --git a/tests/ui/qml/StubMenu.qml b/tests-manual/ui/qml/StubMenu.qml similarity index 100% rename from tests/ui/qml/StubMenu.qml rename to tests-manual/ui/qml/StubMenu.qml diff --git a/tests/ui/qml/Stubs.qml b/tests-manual/ui/qml/Stubs.qml similarity index 100% rename from tests/ui/qml/Stubs.qml rename to tests-manual/ui/qml/Stubs.qml diff --git a/tests/ui/qml/TestControllers.qml b/tests-manual/ui/qml/TestControllers.qml similarity index 100% rename from tests/ui/qml/TestControllers.qml rename to tests-manual/ui/qml/TestControllers.qml diff --git a/tests/ui/qml/TestDialog.qml b/tests-manual/ui/qml/TestDialog.qml similarity index 100% rename from tests/ui/qml/TestDialog.qml rename to tests-manual/ui/qml/TestDialog.qml diff --git a/tests/ui/qml/TestMenu.qml b/tests-manual/ui/qml/TestMenu.qml similarity index 100% rename from tests/ui/qml/TestMenu.qml rename to tests-manual/ui/qml/TestMenu.qml diff --git a/tests/ui/qml/TestRoot.qml b/tests-manual/ui/qml/TestRoot.qml similarity index 100% rename from tests/ui/qml/TestRoot.qml rename to tests-manual/ui/qml/TestRoot.qml diff --git a/tests/ui/qml/controlDemo/ButtonPage.qml b/tests-manual/ui/qml/controlDemo/ButtonPage.qml similarity index 100% rename from tests/ui/qml/controlDemo/ButtonPage.qml rename to tests-manual/ui/qml/controlDemo/ButtonPage.qml diff --git a/tests/ui/qml/controlDemo/InputPage.qml b/tests-manual/ui/qml/controlDemo/InputPage.qml similarity index 100% rename from tests/ui/qml/controlDemo/InputPage.qml rename to tests-manual/ui/qml/controlDemo/InputPage.qml diff --git a/tests/ui/qml/controlDemo/ProgressPage.qml b/tests-manual/ui/qml/controlDemo/ProgressPage.qml similarity index 100% rename from tests/ui/qml/controlDemo/ProgressPage.qml rename to tests-manual/ui/qml/controlDemo/ProgressPage.qml diff --git a/tests/ui/qml/controlDemo/main.qml b/tests-manual/ui/qml/controlDemo/main.qml similarity index 100% rename from tests/ui/qml/controlDemo/main.qml rename to tests-manual/ui/qml/controlDemo/main.qml diff --git a/tests/ui/qml/main.qml b/tests-manual/ui/qml/main.qml similarity index 100% rename from tests/ui/qml/main.qml rename to tests-manual/ui/qml/main.qml diff --git a/tests/ui/qmlscratch.pro b/tests-manual/ui/qmlscratch.pro similarity index 100% rename from tests/ui/qmlscratch.pro rename to tests-manual/ui/qmlscratch.pro diff --git a/tests/ui/src/main.cpp b/tests-manual/ui/src/main.cpp similarity index 100% rename from tests/ui/src/main.cpp rename to tests-manual/ui/src/main.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a8b0727e3d..bc11580979 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,9 +27,9 @@ set_target_properties("test-extensions" PROPERTIES FOLDER "Tests") # set (ALL_TEST_TARGETS "${ALL_TEST_TARGETS}" PARENT_SCOPE) # copies this back to parent scope # add_custom_target("all-tests" - COMMAND ctest . DEPENDS "${ALL_TEST_TARGETS}") -set_target_properties("all-tests" PROPERTIES FOLDER "hidden/test-targets") + set_target_properties("all-tests" PROPERTIES + FOLDER "hidden/test-targets" EXCLUDE_FROM_DEFAULT_BUILD TRUE EXCLUDE_FROM_ALL TRUE) diff --git a/tests/QTestExtensions.h b/tests/QTestExtensions.h index b7b9795a9a..ac037d831b 100644 --- a/tests/QTestExtensions.h +++ b/tests/QTestExtensions.h @@ -13,8 +13,9 @@ #define hifi_QTestExtensions_hpp #include +#include #include - +#include #include "GLMTestUtils.h" // Implements several extensions to QtTest. @@ -302,3 +303,43 @@ inline auto errorTest (float actual, float expected, float acceptableRelativeErr QCOMPARE_WITH_LAMBDA(actual, expected, errorTest(actual, expected, relativeError)) + +inline QString getTestResource(const QString& relativePath) { + static QDir dir; + static std::once_flag once; + std::call_once(once, []{ + QFileInfo fileInfo(__FILE__); + auto parentDir = fileInfo.absoluteDir(); + auto rootDir = parentDir.absoluteFilePath(".."); + dir = QDir::cleanPath(rootDir); + }); + + return QDir::cleanPath(dir.absoluteFilePath(relativePath)); +} + +inline bool afterUsecs(quint64& startUsecs, quint64 maxIntervalUecs) { + auto now = usecTimestampNow(); + auto interval = now - startUsecs; + if (interval > maxIntervalUecs) { + startUsecs = now; + return true; + } + return false; +} + +inline bool afterSecs(quint64& startUsecs, quint64 maxIntervalSecs) { + return afterUsecs(startUsecs, maxIntervalSecs * USECS_PER_SECOND); +} + +template +void doEvery(quint64& lastReportUsecs, quint64 secs, F lamdba) { + if (afterSecs(lastReportUsecs, secs)) { + lamdba(); + } +} + +inline void failAfter(quint64 startUsecs, quint64 secs, const char* message) { + if (afterSecs(startUsecs, secs)) { + QFAIL(message); + } +} diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index fe54ac3781..432129594a 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -39,6 +39,7 @@ void AnimTests::initTestCase() { void AnimTests::cleanupTestCase() { //DependencyManager::destroy(); + DependencyManager::get()->cleanup(); } void AnimTests::testClipInternalState() { diff --git a/tests/gpu/CMakeLists.txt b/tests/gpu/CMakeLists.txt new file mode 100644 index 0000000000..ad0eac5822 --- /dev/null +++ b/tests/gpu/CMakeLists.txt @@ -0,0 +1,17 @@ +# Declare dependencies +macro (setup_testcase_dependencies) + # link in the shared libraries + link_hifi_libraries(shared test-utils ktx gpu gl ${PLATFORM_GL_BACKEND}) + package_libraries_for_deployment() + target_opengl() + target_zlib() + find_package(QuaZip REQUIRED) + target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES}) + if (WIN32) + add_paths_to_fixup_libs(${QUAZIP_DLL_PATH}) + add_dependency_external_projects(wasapi) + endif () +endmacro () + +setup_hifi_testcase() diff --git a/tests/gpu/src/TextureTest.cpp b/tests/gpu/src/TextureTest.cpp new file mode 100644 index 0000000000..18361af791 --- /dev/null +++ b/tests/gpu/src/TextureTest.cpp @@ -0,0 +1,284 @@ +// +// Created by Bradley Austin Davis on 2018/01/11 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "TextureTest.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../../QTestExtensions.h" + +QTEST_MAIN(TextureTest) + +#define LOAD_TEXTURE_COUNT 40 + +static const QString TEST_DATA("https://hifi-public.s3.amazonaws.com/austin/test_data/test_ktx.zip"); +static const QString TEST_DIR_NAME("{630b8f02-52af-4cdf-a896-24e472b94b28}"); + +std::string vertexShaderSource = R"SHADER( +#line 14 +layout(location = 0) out vec2 outTexCoord0; + +const vec4 VERTICES[] = vec4[]( + vec4(-1.0, -1.0, 0.0, 1.0), + vec4( 1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4( 1.0, 1.0, 0.0, 1.0) +); + +void main() { + outTexCoord0 = VERTICES[gl_VertexID].xy; + outTexCoord0 += 1.0; + outTexCoord0 /= 2.0; + gl_Position = VERTICES[gl_VertexID]; +} +)SHADER"; + +std::string fragmentShaderSource = R"SHADER( +#line 28 + +uniform sampler2D tex; + +layout(location = 0) in vec2 inTexCoord0; +layout(location = 0) out vec4 outFragColor; + +void main() { + outFragColor = texture(tex, inTexCoord0); + outFragColor.a = 1.0; + //outFragColor.rb = inTexCoord0; +} + +)SHADER"; + +QtMessageHandler originalHandler; + +void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { +#if defined(Q_OS_WIN) + OutputDebugStringA(message.toStdString().c_str()); + OutputDebugStringA("\n"); +#endif + originalHandler(type, context, message); +} + +void TextureTest::initTestCase() { + originalHandler = qInstallMessageHandler(messageHandler); + _resourcesPath = getTestResource("interface/resources"); + getDefaultOpenGLSurfaceFormat(); + _canvas.create(); + if (!_canvas.makeCurrent()) { + qFatal("Unable to make test GL context current"); + } + gl::initModuleGl(); + gpu::Context::init(); + _gpuContext = std::make_shared(); + + _resourcesPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + TEST_DIR_NAME; + if (!QFileInfo(_resourcesPath).exists()) { + QDir(_resourcesPath).mkpath("."); + FileDownloader(TEST_DATA, + [&](const QByteArray& data) { + QTemporaryFile zipFile; + if (zipFile.open()) { + zipFile.write(data); + zipFile.close(); + } + JlCompress::extractDir(zipFile.fileName(), _resourcesPath); + }) + .waitForDownload(); + } + + _canvas.makeCurrent(); + { + auto VS = gpu::Shader::createVertex(vertexShaderSource); + auto PS = gpu::Shader::createPixel(fragmentShaderSource); + auto program = gpu::Shader::createProgram(VS, PS); + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + // If the pipeline did not exist, make it + auto state = std::make_shared(); + state->setCullMode(gpu::State::CULL_NONE); + state->setDepthTest({}); + state->setBlendFunction({ false }); + _pipeline = gpu::Pipeline::create(program, state); + } + + _framebuffer.reset(gpu::Framebuffer::create("cached", gpu::Element::COLOR_SRGBA_32, _size.x, _size.y)); + + // Find the test textures + { + QDir resourcesDir(_resourcesPath); + auto entryList = resourcesDir.entryList({ "*.ktx" }, QDir::Filter::Files); + _textureFiles.reserve(entryList.size()); + for (auto entry : entryList) { + auto textureFile = resourcesDir.absoluteFilePath(entry).toStdString(); + _textureFiles.push_back(textureFile); + } + } + + // Load the test textures + { + size_t newTextureCount = std::min(_textureFiles.size(), LOAD_TEXTURE_COUNT); + for (size_t i = 0; i < newTextureCount; ++i) { + const auto& textureFile = _textureFiles[i]; + auto texture = gpu::Texture::unserialize(textureFile); + _textures.push_back(texture); + } + } +} + +void TextureTest::cleanupTestCase() { + _framebuffer.reset(); + _pipeline.reset(); + _gpuContext->recycle(); + _gpuContext.reset(); +} + +void TextureTest::beginFrame() { + _gpuContext->recycle(); + _gpuContext->beginFrame(); + gpu::doInBatch("TestWindow::beginFrame", _gpuContext, [&](gpu::Batch& batch) { + batch.setFramebuffer(_framebuffer); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f }); + batch.clearDepthFramebuffer(1e4); + batch.setViewportTransform({ 0, 0, _size.x, _size.y }); + }); +} + +void TextureTest::endFrame() { + gpu::doInBatch("TestWindow::endFrame::finish", _gpuContext, [&](gpu::Batch& batch) { batch.resetStages(); }); + auto framePointer = _gpuContext->endFrame(); + _gpuContext->consumeFrameUpdates(framePointer); + _gpuContext->executeFrame(framePointer); + // Simulate swapbuffers with a finish + glFinish(); + QThread::msleep(10); +} + +void TextureTest::renderFrame(const std::function& renderLambda) { + beginFrame(); + gpu::doInBatch("Test::body", _gpuContext, renderLambda); + endFrame(); +} + +void TextureTest::testTextureLoading() { + QVERIFY(_textures.size() > 0); + auto renderTexturesLamdba = [this](gpu::Batch& batch) { + batch.setPipeline(_pipeline); + for (const auto& texture : _textures) { + batch.setResourceTexture(0, texture); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); + } + }; + + size_t expectedAllocation = 0; + for (const auto& texture : _textures) { + expectedAllocation += texture->evalTotalSize(); + } + QVERIFY(_textures.size() > 0); + + auto reportLambda = [=] { + qDebug() << "Allowed " << gpu::Texture::getAllowedGPUMemoryUsage(); + qDebug() << "Allocated " << gpu::Context::getTextureResourceGPUMemSize(); + qDebug() << "Populated " << gpu::Context::getTextureResourcePopulatedGPUMemSize(); + qDebug() << "Pending " << gpu::Context::getTexturePendingGPUTransferMemSize(); + }; + + auto allocatedMemory = gpu::Context::getTextureResourceGPUMemSize(); + auto populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize(); + + // Cycle frames we're fully allocated + // We need to use the texture rendering lambda + auto lastReport = usecTimestampNow(); + auto start = usecTimestampNow(); + while (expectedAllocation != allocatedMemory) { + doEvery(lastReport, 4, reportLambda); + failAfter(start, 10, "Failed to allocate texture memory after 10 seconds"); + renderFrame(renderTexturesLamdba); + allocatedMemory = gpu::Context::getTextureResourceGPUMemSize(); + populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize(); + } + QCOMPARE(allocatedMemory, expectedAllocation); + + // Restart the timer + start = usecTimestampNow(); + // Cycle frames we're fully populated + while (allocatedMemory != populatedMemory || 0 != gpu::Context::getTexturePendingGPUTransferMemSize()) { + doEvery(lastReport, 4, reportLambda); + failAfter(start, 10, "Failed to populate texture memory after 10 seconds"); + renderFrame(); + allocatedMemory = gpu::Context::getTextureResourceGPUMemSize(); + populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize(); + } + reportLambda(); + QCOMPARE(populatedMemory, allocatedMemory); + + // FIXME workaround a race condition in the difference between populated size and the actual _populatedMip value in the texture + for (size_t i = 0; i < _textures.size(); ++i) { + renderFrame(); + } + + // Test on-demand deallocation of memory + auto maxMemory = allocatedMemory / 2; + gpu::Texture::setAllowedGPUMemoryUsage(maxMemory); + + // Restart the timer + start = usecTimestampNow(); + // Cycle frames until the allocated memory is below the max memory + while (allocatedMemory > maxMemory || allocatedMemory != populatedMemory) { + doEvery(lastReport, 4, reportLambda); + failAfter(start, 10, "Failed to deallocate texture memory after 10 seconds"); + renderFrame(renderTexturesLamdba); + allocatedMemory = gpu::Context::getTextureResourceGPUMemSize(); + populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize(); + } + reportLambda(); + + // Verify that the allocation is now below the target + QVERIFY(allocatedMemory <= maxMemory); + // Verify that populated memory is the same as allocated memory + QCOMPARE(populatedMemory, allocatedMemory); + + // Restart the timer + start = usecTimestampNow(); + // Reset the max memory to automatic + gpu::Texture::setAllowedGPUMemoryUsage(0); + // Cycle frames we're fully populated + while (allocatedMemory != expectedAllocation || allocatedMemory != populatedMemory) { + doEvery(lastReport, 4, reportLambda); + failAfter(start, 10, "Failed to populate texture memory after 10 seconds"); + renderFrame(); + allocatedMemory = gpu::Context::getTextureResourceGPUMemSize(); + populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize(); + } + reportLambda(); + QCOMPARE(allocatedMemory, expectedAllocation); + QCOMPARE(populatedMemory, allocatedMemory); + + _textures.clear(); + // Cycle frames we're fully populated + while (allocatedMemory != 0) { + failAfter(start, 10, "Failed to clear texture memory after 10 seconds"); + renderFrame(); + allocatedMemory = gpu::Context::getTextureResourceGPUMemSize(); + populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize(); + } + QCOMPARE(allocatedMemory, 0); + QCOMPARE(populatedMemory, 0); + qDebug() << "Done"; + +} diff --git a/tests/gpu/src/TextureTest.h b/tests/gpu/src/TextureTest.h new file mode 100644 index 0000000000..91f8a358ea --- /dev/null +++ b/tests/gpu/src/TextureTest.h @@ -0,0 +1,40 @@ +// +// Created by Bradley Austin Davis on 2018/05/08 +// Copyright 2013-2018 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 +// + +#pragma once + +#include +#include + +#include +#include + +class TextureTest : public QObject { + Q_OBJECT + +private: + void beginFrame(); + void endFrame(); + void renderFrame(const std::function& = [](gpu::Batch&) {}); + +private slots: + void initTestCase(); + void cleanupTestCase(); + void testTextureLoading(); + +private: + QString _resourcesPath; + OffscreenGLCanvas _canvas; + gpu::ContextPointer _gpuContext; + gpu::PipelinePointer _pipeline; + gpu::FramebufferPointer _framebuffer; + gpu::TexturePointer _colorBuffer, _depthBuffer; + const glm::uvec2 _size{ 640, 480 }; + std::vector _textureFiles; + std::vector _textures; +}; diff --git a/tests/ktx/src/KtxTests.cpp b/tests/ktx/src/KtxTests.cpp index 65d9cbec3d..5158ad2fd4 100644 --- a/tests/ktx/src/KtxTests.cpp +++ b/tests/ktx/src/KtxTests.cpp @@ -76,7 +76,9 @@ void KtxTests::testKtxEvalFunctions() { void KtxTests::testKtxSerialization() { const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png"; QImage image(TEST_IMAGE); - gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true); + std::atomic abortSignal; + gpu::TexturePointer testTexture = + image::TextureUsage::process2DTextureColorFromImage(std::move(image), TEST_IMAGE.toStdString(), true, abortSignal); auto ktxMemory = gpu::Texture::serialize(*testTexture); QVERIFY(ktxMemory.get()); diff --git a/tests/networking/src/ResourceTests.cpp b/tests/networking/src/ResourceTests.cpp index 864d7c9939..6af3629bc7 100644 --- a/tests/networking/src/ResourceTests.cpp +++ b/tests/networking/src/ResourceTests.cpp @@ -11,16 +11,23 @@ #include -#include "ResourceCache.h" -#include "NetworkAccessManager.h" -#include "DependencyManager.h" +#include +#include +#include +#include +#include +#include QTEST_MAIN(ResourceTests) void ResourceTests::initTestCase() { - auto resourceCacheSharedItems = DependencyManager::set(); - + //DependencyManager::set(); + DependencyManager::set(); + DependencyManager::registerInheritance(); + DependencyManager::set(NodeType::Agent, INVALID_PORT); + DependencyManager::set(); + DependencyManager::set(); const qint64 MAXIMUM_CACHE_SIZE = 1024 * 1024 * 1024; // 1GB // set up the file cache @@ -34,6 +41,10 @@ void ResourceTests::initTestCase() { networkAccessManager.setCache(cache); } +void ResourceTests::cleanupTestCase() { + DependencyManager::get()->cleanup(); +} + static QSharedPointer resource; diff --git a/tests/networking/src/ResourceTests.h b/tests/networking/src/ResourceTests.h index 32fc151982..59675816ba 100644 --- a/tests/networking/src/ResourceTests.h +++ b/tests/networking/src/ResourceTests.h @@ -18,6 +18,7 @@ private slots: void initTestCase(); void downloadFirst(); void downloadAgain(); + void cleanupTestCase(); }; #endif // hifi_ResourceTests_h diff --git a/tests/qt59/CMakeLists.txt b/tests/qt59/CMakeLists.txt deleted file mode 100644 index e3450ae069..0000000000 --- a/tests/qt59/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ - -set(TARGET_NAME qt59) - -if (WIN32) - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217") -endif() - -setup_memory_debugger() - -# This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Gui) -set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") - - -# link in the shared libraries -link_hifi_libraries(shared networking) - -if (WIN32) - add_dependency_external_projects(wasapi) -endif() - -package_libraries_for_deployment() diff --git a/tests/qt59/src/main.cpp b/tests/qt59/src/main.cpp deleted file mode 100644 index 19b922de9f..0000000000 --- a/tests/qt59/src/main.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// -// Created by Bradley Austin Davis on 2017/06/06 -// Copyright 2013-2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include - -#include -#include -#include -#include - -#include - - -class Qt59TestApp : public QCoreApplication { - Q_OBJECT -public: - Qt59TestApp(int argc, char* argv[]); - ~Qt59TestApp(); - -private: - void finish(int exitCode); -}; - - - -Qt59TestApp::Qt59TestApp(int argc, char* argv[]) : - QCoreApplication(argc, argv) -{ - DependencyManager::registerInheritance(); - DependencyManager::set([&] { return QString("Mozilla/5.0 (HighFidelityACClient)"); }); - DependencyManager::set(); - DependencyManager::set(NodeType::Agent, 0); - auto nodeList = DependencyManager::get(); - nodeList->startThread(); - auto messagesClient = DependencyManager::set(); - messagesClient->startThread(); - QTimer::singleShot(1000, [this] { finish(0); }); -} - -Qt59TestApp::~Qt59TestApp() { -} - - -void Qt59TestApp::finish(int exitCode) { - auto nodeList = DependencyManager::get(); - - // send the domain a disconnect packet, force stoppage of domain-server check-ins - nodeList->getDomainHandler().disconnect(); - nodeList->setIsShuttingDown(true); - nodeList->getPacketReceiver().setShouldDropPackets(true); - - // remove the NodeList from the DependencyManager - DependencyManager::destroy(); - QCoreApplication::exit(exitCode); -} - - -int main(int argc, char * argv[]) { - setupHifiApplication("Qt59Test"); - - Qt59TestApp app(argc, argv); - - return app.exec(); -} - -#include "main.moc" diff --git a/tests/ui/CMakeLists.txt b/tests/ui/CMakeLists.txt deleted file mode 100644 index 0a5d6796e0..0000000000 --- a/tests/ui/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -# This folder contains code for testing the QML UI using Qt Creator. It is not intended to be included in the CMake project