diff --git a/BUILD.md b/BUILD.md index c868a8e9d9..c51e40cb58 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,7 +1,7 @@ ###Dependencies * [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 3.3.2 -* [Qt](http://www.qt.io/download-open-source) ~> 5.5.1 +* [Qt](http://www.qt.io/download-open-source) ~> 5.6.1 * [OpenSSL](https://www.openssl.org/community/binaries.html) ~> 1.0.1m * IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities. * [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) @@ -41,14 +41,14 @@ If you would like to use a specific install of a dependency instead of the versi Hifi uses CMake to generate build files and project files for your platform. ####Qt -In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your Qt installation. +In order for CMake to find the Qt5 find modules, you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt installation. -For example, a Qt5 5.5.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). +This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). The path it needs to be set to will depend on where and how Qt5 was installed. e.g. - export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.5.1/clang_64/lib/cmake/ - export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake + export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.1/clang_64/lib/cmake/ + export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.1-1/lib/cmake export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake ####Generating build files @@ -65,7 +65,7 @@ Any variables that need to be set for CMake to find dependencies can be set as E For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation: - cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.5.1/lib/cmake + cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.1/lib/cmake ####Finding Dependencies diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index 88294f3040..bb8de0cc9a 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -4,13 +4,11 @@ Please read the [general build guide](BUILD.md) for information on dependencies You will need the following tools to build our Android targets. -* [cmake](http://www.cmake.org/download/) ~> 3.1.0 - * Note that this is a newer version required than the minimum for hifi desktop targets. -* [Qt](http://www.qt.io/download-open-source/#) ~> 5.4.0 - * Note that this is a newer version required than the minimum for hifi desktop targets. +* [cmake](http://www.cmake.org/download/) ~> 3.5.1 +* [Qt](http://www.qt.io/download-open-source/#) ~> 5.5.1 * [ant](http://ant.apache.org/bindownload.cgi) ~> 1.9.4 -* [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) = r10c -* [Android SDK](http://developer.android.com/sdk/installing/index.html) ~> 24.0.2 +* [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) ~> r10d +* [Android SDK](http://developer.android.com/sdk/installing/index.html) ~> 24.4.1.1 * Install the latest Platform-tools * Install the latest Build-tools * Install the SDK Platform for API Level 19 @@ -19,6 +17,12 @@ You will need the following tools to build our Android targets. You will also need to cross-compile the dependencies required for all platforms for Android, and help CMake find these compiled libraries on your machine. +####Scribe + +High Fidelity has a shader pre-processing tool called `scribe` that various libraries will call on during the build process. You must compile scribe using your native toolchain (following the build instructions for your platform) and then pass a CMake variable or set an ENV variable `SCRIBE_PATH` that is a path to the scribe executable. + +CMake will fatally error if it does not find the scribe executable while using the android toolchain. + ####Optional Components * [Oculus Mobile SDK](https://developer.oculus.com/downloads/#sdk=mobile) ~> 0.4.2 @@ -31,11 +35,11 @@ This is most easily accomplished by installing all Android dependencies in the s ####Qt -Install Qt 5.4 for Android for your host environment from the [Qt downloads page](http://www.qt.io/download/). Install Qt to ``$ANDROID_LIB_DIR/Qt``. This is required so that our root CMakeLists file can help CMake find your Android Qt installation. +Install Qt 5.5.1 for Android for your host environment from the [Qt downloads page](http://www.qt.io/download/). Install Qt to ``$ANDROID_LIB_DIR/Qt``. This is required so that our root CMakeLists file can help CMake find your Android Qt installation. The component required for the Android build is the `Android armv7` component. -If you would like to install Qt to a different location, or attempt to build with a different Qt version, you can pass `ANDROID_QT_CMAKE_PREFIX_PATH` to CMake. Point to the `cmake` folder inside `$VERSION_NUMBER/android_armv7/lib`. Otherwise, our root CMakeLists will set it to `$ANDROID_LIB_DIR/Qt/5.3/android_armv7/lib/cmake`. +If you would like to install Qt to a different location, or attempt to build with a different Qt version, you can pass `ANDROID_QT_CMAKE_PREFIX_PATH` to CMake. Point to the `cmake` folder inside `$VERSION_NUMBER/android_armv7/lib`. Otherwise, our root CMakeLists will set it to `$ANDROID_LIB_DIR/Qt/5.5/android_armv7/lib/cmake`. ####OpenSSL diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 1c9c5a9796..55d4276aa0 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -1,25 +1,31 @@ Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file. ###Homebrew -[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple. +[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of some High Fidelity dependencies very simple. brew tap homebrew/versions - brew install cmake openssl qt55 + brew install cmake openssl -We no longer require install of qt5 via our [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas). Versions of Qt that are 5.5.x provide a mechanism to disable the wireless scanning we previously had a custom patch for. +###OpenSSL -###OpenSSL and Qt - -Assuming you've installed OpenSSL or Qt 5 using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR and QT_CMAKE_PREFIX_PATH so CMake can find your installations. +Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations. For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR: export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2h_1/ - -For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows. - export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt55/5.5.1/lib/cmake +Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change. -Note that these use the versions from homebrew formulae at the time of this writing, and the version in the path will likely change. +###Qt +You can use the online installer or the offline installer. + +* [Download the online installer](http://www.qt.io/download-open-source/#section-2) + * When it asks you to select components, select the following: + * Qt > Qt 5.6 + +* [Download the offline installer](http://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-mac-x64-clang-5.6.1-1.dmg) + +Once Qt is installed, you need to manually configure the following: +* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.1/5.6/clang_64/lib/cmake/` directory. ###Xcode If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 25e20952ca..b8adaad8d1 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -27,17 +27,17 @@ We expect nmake.exe to be located at the following path. ###Qt You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version. -* [Download the online installer](http://qt-project.org/downloads) +* [Download the online installer](http://www.qt.io/download-open-source/#section-2) * When it asks you to select components, ONLY select one of the following, 32- or 64-bit to match your build preference: - * Qt > Qt 5.5.1 > **msvc2013 32-bit** - * Qt > Qt 5.5.1 > **msvc2013 64-bit** + * Qt > Qt 5.6.1 > **msvc2013 32-bit** + * Qt > Qt 5.6.1 > **msvc2013 64-bit** * Download the offline installer, 32- or 64-bit to match your build preference: - * [32-bit](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013-5.5.1.exe) - * [64-bit](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013_64-5.5.1.exe) + * [32-bit](http://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013-5.6.1-1.exe) + * [64-bit](http://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe) Once Qt is installed, you need to manually configure the following: -* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.5.1\msvc2013\lib\cmake` or `Qt\5.5.1\msvc2013_64\lib\cmake` directory. +* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.6.1\msvc2013\lib\cmake` or `Qt\5.6.1\msvc2013_64\lib\cmake` directory. * You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New ###External Libraries diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bcd2352de..1ab7e55343 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.2) if (USE_ANDROID_TOOLCHAIN) set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/android/android.toolchain.cmake") set(ANDROID_NATIVE_API_LEVEL 19) + set(ANDROID_TOOLCHAIN_NAME arm-linux-androideabi-clang3.5) + set(ANDROID_STL c++_shared) endif () if (WIN32) @@ -64,7 +66,7 @@ if (WIN32) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG /OPT:REF /OPT:ICF") else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter") - if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Woverloaded-virtual -Wdouble-promotion") if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "5.1") # gcc 5.1 and on have suggest-override set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override") @@ -72,18 +74,30 @@ else () endif () endif(WIN32) -if ((NOT MSVC12) AND (NOT MSVC14)) - include(CheckCXXCompilerFlag) - CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) - CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) - - if (COMPILER_SUPPORTS_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - elseif(COMPILER_SUPPORTS_CXX0X) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") - else() - message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.3") + # GLM 0.9.8 on Ubuntu 14 (gcc 4.4) has issues with the simd declarations + add_definitions(-DGLM_FORCE_PURE) endif() +endif() + +if (NOT ANDROID) + if ((NOT MSVC12) AND (NOT MSVC14)) + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + + if (COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + elseif(COMPILER_SUPPORTS_CXX0X) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + else() + message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") + endif() + endif () +else () + # assume that the toolchain selected for android has C++11 support + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif () if (APPLE) @@ -98,7 +112,7 @@ endif () if (ANDROID) if (NOT ANDROID_QT_CMAKE_PREFIX_PATH) - set(QT_CMAKE_PREFIX_PATH ${ANDROID_LIB_DIR}/Qt/5.4/android_armv7/lib/cmake) + set(QT_CMAKE_PREFIX_PATH ${ANDROID_LIB_DIR}/Qt/5.5/android_armv7/lib/cmake) else () set(QT_CMAKE_PREFIX_PATH ${ANDROID_QT_CMAKE_PREFIX_PATH}) endif () @@ -236,7 +250,9 @@ if (NOT ANDROID) endif() if (ANDROID OR DESKTOP_GVR) + add_subdirectory(interface) add_subdirectory(gvr-interface) + add_subdirectory(plugins) endif () if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index d0fd2c1176..54afabfd21 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -2,6 +2,11 @@ set(TARGET_NAME assignment-client) setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets) +# Fix up the rpath so macdeployqt works +if (APPLE) + set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") +endif () + # link in the shared libraries link_hifi_libraries( audio avatars octree gpu model fbx entities diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 350d359412..c6a2a3d5e8 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -48,11 +48,8 @@ static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10; Agent::Agent(ReceivedMessage& message) : ThreadedAssignment(message), _entityEditSender(), - _receivedAudioStream(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, - InboundAudioStream::Settings(0, false, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, false, - DEFAULT_WINDOW_STARVE_THRESHOLD, DEFAULT_WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES, - DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION, false)) -{ + _receivedAudioStream(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO, + RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES) { DependencyManager::get()->setPacketSender(&_entityEditSender); ResourceManager::init(); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index eabb4955d9..ccd80a4a11 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -61,15 +61,14 @@ #include "AudioMixer.h" -const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f; -const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.5f; // attenuation = -6dB * log2(distance) -const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.003f; -const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; -const QString AUDIO_ENV_GROUP_KEY = "audio_env"; -const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer"; - -InboundAudioStream::Settings AudioMixer::_streamSettings; +static const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f; +static const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.5f; // attenuation = -6dB * log2(distance) +static const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.003f; +static const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; +static const QString AUDIO_ENV_GROUP_KEY = "audio_env"; +static const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer"; +int AudioMixer::_numStaticJitterFrames{ -1 }; bool AudioMixer::_enableFilter = true; bool AudioMixer::shouldMute(float quietestFrame) { @@ -269,20 +268,18 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& if (!streamToAdd.lastPopSucceeded()) { bool forceSilentBlock = true; - if (_streamSettings._repetitionWithFade && !streamToAdd.getLastPopOutput().isNull()) { + if (!streamToAdd.getLastPopOutput().isNull()) { + bool isInjector = dynamic_cast(&streamToAdd); - // reptition with fade is enabled, and we do have a valid previous frame to repeat - // so we mix the previously-mixed block - - // this is preferable to not mixing it at all to avoid the harsh jump to silence + // in an injector, just go silent - the injector has likely ended + // in other inputs (microphone, &c.), repeat with fade to avoid the harsh jump to silence // we'll repeat the last block until it has a block to mix // and we'll gradually fade that repeated block into silence. // calculate its fade factor, which depends on how many times it's already been repeated. - repeatedFrameFadeFactor = calculateRepeatedFrameFadeFactor(streamToAdd.getConsecutiveNotMixedCount() - 1); - if (repeatedFrameFadeFactor > 0.0f) { + if (!isInjector && repeatedFrameFadeFactor > 0.0f) { // apply the repeatedFrameFadeFactor to the gain gain *= repeatedFrameFadeFactor; @@ -641,7 +638,7 @@ QString AudioMixer::percentageForMixStats(int counter) { void AudioMixer::sendStatsPacket() { static QJsonObject statsObject; - statsObject["useDynamicJitterBuffers"] = _streamSettings._dynamicJitterBuffers; + statsObject["useDynamicJitterBuffers"] = _numStaticJitterFrames == -1; statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100.0f; statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio; @@ -902,63 +899,62 @@ void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) { // check the payload to see if we have asked for dynamicJitterBuffer support const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic_jitter_buffer"; - _streamSettings._dynamicJitterBuffers = audioBufferGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool(); - if (_streamSettings._dynamicJitterBuffers) { - qDebug() << "Enable dynamic jitter buffers."; + bool enableDynamicJitterBuffer = audioBufferGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool(); + if (enableDynamicJitterBuffer) { + qDebug() << "Enabling dynamic jitter buffers."; + + bool ok; + const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames"; + _numStaticJitterFrames = audioBufferGroupObject[DESIRED_JITTER_BUFFER_FRAMES_KEY].toString().toInt(&ok); + if (!ok) { + _numStaticJitterFrames = InboundAudioStream::DEFAULT_STATIC_JITTER_FRAMES; + } + qDebug() << "Static desired jitter buffer frames:" << _numStaticJitterFrames; } else { - qDebug() << "Dynamic jitter buffers disabled."; + qDebug() << "Disabling dynamic jitter buffers."; + _numStaticJitterFrames = -1; } + // check for deprecated audio settings + auto deprecationNotice = [](const QString& setting, const QString& value) { + qInfo().nospace() << "[DEPRECATION NOTICE] " << setting << "(" << value << ") has been deprecated, and has no effect"; + }; bool ok; - const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames"; - _streamSettings._staticDesiredJitterBufferFrames = audioBufferGroupObject[DESIRED_JITTER_BUFFER_FRAMES_KEY].toString().toInt(&ok); - if (!ok) { - _streamSettings._staticDesiredJitterBufferFrames = DEFAULT_STATIC_DESIRED_JITTER_BUFFER_FRAMES; - } - qDebug() << "Static desired jitter buffer frames:" << _streamSettings._staticDesiredJitterBufferFrames; const QString MAX_FRAMES_OVER_DESIRED_JSON_KEY = "max_frames_over_desired"; - _streamSettings._maxFramesOverDesired = audioBufferGroupObject[MAX_FRAMES_OVER_DESIRED_JSON_KEY].toString().toInt(&ok); - if (!ok) { - _streamSettings._maxFramesOverDesired = DEFAULT_MAX_FRAMES_OVER_DESIRED; - } - qDebug() << "Max frames over desired:" << _streamSettings._maxFramesOverDesired; - - const QString USE_STDEV_FOR_DESIRED_CALC_JSON_KEY = "use_stdev_for_desired_calc"; - _streamSettings._useStDevForJitterCalc = audioBufferGroupObject[USE_STDEV_FOR_DESIRED_CALC_JSON_KEY].toBool(); - if (_streamSettings._useStDevForJitterCalc) { - qDebug() << "Using stdev method for jitter calc if dynamic jitter buffers enabled"; - } else { - qDebug() << "Using max-gap method for jitter calc if dynamic jitter buffers enabled"; + int maxFramesOverDesired = audioBufferGroupObject[MAX_FRAMES_OVER_DESIRED_JSON_KEY].toString().toInt(&ok); + if (ok && maxFramesOverDesired != InboundAudioStream::MAX_FRAMES_OVER_DESIRED) { + deprecationNotice(MAX_FRAMES_OVER_DESIRED_JSON_KEY, QString::number(maxFramesOverDesired)); } const QString WINDOW_STARVE_THRESHOLD_JSON_KEY = "window_starve_threshold"; - _streamSettings._windowStarveThreshold = audioBufferGroupObject[WINDOW_STARVE_THRESHOLD_JSON_KEY].toString().toInt(&ok); - if (!ok) { - _streamSettings._windowStarveThreshold = DEFAULT_WINDOW_STARVE_THRESHOLD; + int windowStarveThreshold = audioBufferGroupObject[WINDOW_STARVE_THRESHOLD_JSON_KEY].toString().toInt(&ok); + if (ok && windowStarveThreshold != InboundAudioStream::WINDOW_STARVE_THRESHOLD) { + deprecationNotice(WINDOW_STARVE_THRESHOLD_JSON_KEY, QString::number(windowStarveThreshold)); } - qDebug() << "Window A starve threshold:" << _streamSettings._windowStarveThreshold; const QString WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY = "window_seconds_for_desired_calc_on_too_many_starves"; - _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = audioBufferGroupObject[WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY].toString().toInt(&ok); - if (!ok) { - _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES; + int windowSecondsForDesiredCalcOnTooManyStarves = audioBufferGroupObject[WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY].toString().toInt(&ok); + if (ok && windowSecondsForDesiredCalcOnTooManyStarves != InboundAudioStream::WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES) { + deprecationNotice(WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES_JSON_KEY, QString::number(windowSecondsForDesiredCalcOnTooManyStarves)); } - qDebug() << "Window A length:" << _streamSettings._windowSecondsForDesiredCalcOnTooManyStarves << "seconds"; const QString WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY = "window_seconds_for_desired_reduction"; - _streamSettings._windowSecondsForDesiredReduction = audioBufferGroupObject[WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY].toString().toInt(&ok); - if (!ok) { - _streamSettings._windowSecondsForDesiredReduction = DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION; + int windowSecondsForDesiredReduction = audioBufferGroupObject[WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY].toString().toInt(&ok); + if (ok && windowSecondsForDesiredReduction != InboundAudioStream::WINDOW_SECONDS_FOR_DESIRED_REDUCTION) { + deprecationNotice(WINDOW_SECONDS_FOR_DESIRED_REDUCTION_JSON_KEY, QString::number(windowSecondsForDesiredReduction)); + } + + const QString USE_STDEV_FOR_JITTER_JSON_KEY = "use_stdev_for_desired_calc"; + bool useStDevForJitterCalc = audioBufferGroupObject[USE_STDEV_FOR_JITTER_JSON_KEY].toBool(); + if (useStDevForJitterCalc != InboundAudioStream::USE_STDEV_FOR_JITTER) { + deprecationNotice(USE_STDEV_FOR_JITTER_JSON_KEY, useStDevForJitterCalc ? "true" : "false"); } - qDebug() << "Window B length:" << _streamSettings._windowSecondsForDesiredReduction << "seconds"; const QString REPETITION_WITH_FADE_JSON_KEY = "repetition_with_fade"; - _streamSettings._repetitionWithFade = audioBufferGroupObject[REPETITION_WITH_FADE_JSON_KEY].toBool(); - if (_streamSettings._repetitionWithFade) { - qDebug() << "Repetition with fade enabled"; - } else { - qDebug() << "Repetition with fade disabled"; + bool repetitionWithFade = audioBufferGroupObject[REPETITION_WITH_FADE_JSON_KEY].toBool(); + if (repetitionWithFade != InboundAudioStream::REPETITION_WITH_FADE) { + deprecationNotice(REPETITION_WITH_FADE_JSON_KEY, repetitionWithFade ? "true" : "false"); } } diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 7e3ef8a69b..bccac529c1 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -39,7 +39,7 @@ public slots: void sendStatsPacket() override; - static const InboundAudioStream::Settings& getStreamSettings() { return _streamSettings; } + static int getStaticJitterFrames() { return _numStaticJitterFrames; } private slots: void broadcastMixes(); @@ -112,7 +112,7 @@ private: }; QVector _zoneReverbSettings; - static InboundAudioStream::Settings _streamSettings; + static int _numStaticJitterFrames; // -1 denotes dynamic jitter buffering static bool _enableFilter; }; diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 0a45137da6..290a361118 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -109,7 +109,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { bool isStereo = channelFlag == 1; - auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStreamSettings()); + auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames()); avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO); qDebug() << "creating new AvatarAudioStream... codec:" << _selectedCodecName; @@ -143,7 +143,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { if (streamIt == _audioStreams.end()) { // we don't have this injected stream yet, so add it - auto injectorStream = new InjectedAudioStream(streamIdentifier, isStereo, AudioMixer::getStreamSettings()); + auto injectorStream = new InjectedAudioStream(streamIdentifier, isStereo, AudioMixer::getStaticJitterFrames()); #if INJECTORS_SUPPORT_CODECS injectorStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO); @@ -270,6 +270,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() { downstreamStats["desired"] = streamStats._desiredJitterBufferFrames; downstreamStats["available_avg_10s"] = streamStats._framesAvailableAverage; downstreamStats["available"] = (double) streamStats._framesAvailable; + downstreamStats["unplayed"] = (double) streamStats._unplayedMs; downstreamStats["starves"] = (double) streamStats._starveCount; downstreamStats["not_mixed"] = (double) streamStats._consecutiveNotMixedCount; downstreamStats["overflows"] = (double) streamStats._overflowCount; @@ -294,6 +295,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() { upstreamStats["desired_calc"] = avatarAudioStream->getCalculatedJitterBufferFrames(); upstreamStats["available_avg_10s"] = streamStats._framesAvailableAverage; upstreamStats["available"] = (double) streamStats._framesAvailable; + upstreamStats["unplayed"] = (double) streamStats._unplayedMs; upstreamStats["starves"] = (double) streamStats._starveCount; upstreamStats["not_mixed"] = (double) streamStats._consecutiveNotMixedCount; upstreamStats["overflows"] = (double) streamStats._overflowCount; @@ -323,6 +325,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() { upstreamStats["desired_calc"] = injectorPair.second->getCalculatedJitterBufferFrames(); upstreamStats["available_avg_10s"] = streamStats._framesAvailableAverage; upstreamStats["available"] = (double) streamStats._framesAvailable; + upstreamStats["unplayed"] = (double) streamStats._unplayedMs; upstreamStats["starves"] = (double) streamStats._starveCount; upstreamStats["not_mixed"] = (double) streamStats._consecutiveNotMixedCount; upstreamStats["overflows"] = (double) streamStats._overflowCount; diff --git a/assignment-client/src/audio/AvatarAudioStream.cpp b/assignment-client/src/audio/AvatarAudioStream.cpp index da995c999d..1e0c3ed9e6 100644 --- a/assignment-client/src/audio/AvatarAudioStream.cpp +++ b/assignment-client/src/audio/AvatarAudioStream.cpp @@ -13,10 +13,8 @@ #include "AvatarAudioStream.h" -AvatarAudioStream::AvatarAudioStream(bool isStereo, const InboundAudioStream::Settings& settings) : - PositionalAudioStream(PositionalAudioStream::Microphone, isStereo, settings) -{ -} +AvatarAudioStream::AvatarAudioStream(bool isStereo, int numStaticJitterFrames) : + PositionalAudioStream(PositionalAudioStream::Microphone, isStereo, numStaticJitterFrames) {} int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) { int readBytes = 0; diff --git a/assignment-client/src/audio/AvatarAudioStream.h b/assignment-client/src/audio/AvatarAudioStream.h index d2e1137ae6..497e522922 100644 --- a/assignment-client/src/audio/AvatarAudioStream.h +++ b/assignment-client/src/audio/AvatarAudioStream.h @@ -18,7 +18,7 @@ class AvatarAudioStream : public PositionalAudioStream { public: - AvatarAudioStream(bool isStereo, const InboundAudioStream::Settings& settings); + AvatarAudioStream(bool isStereo, int numStaticJitterFrames = -1); private: // disallow copying of AvatarAudioStream objects diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 65989b389e..d87a5f1cc9 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -413,6 +413,9 @@ void AvatarMixer::handleAvatarDataPacket(QSharedPointer message } void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode) { + auto nodeList = DependencyManager::get(); + nodeList->getOrCreateLinkedData(senderNode); + if (senderNode->getLinkedData()) { AvatarMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData()); if (nodeData != nullptr) { diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 48ffc2fdbc..3e36250a82 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/cmake/android/android.toolchain.cmake b/cmake/android/android.toolchain.cmake index e1f50abfa6..806cef6b18 100755 --- a/cmake/android/android.toolchain.cmake +++ b/cmake/android/android.toolchain.cmake @@ -29,8 +29,8 @@ # POSSIBILITY OF SUCH DAMAGE. # ------------------------------------------------------------------------------ -# Android CMake toolchain file, for use with the Android NDK r5-r10c -# Requires cmake 2.6.3 or newer (2.8.5 or newer is recommended). +# Android CMake toolchain file, for use with the Android NDK r5-r10d +# Requires cmake 2.6.3 or newer (2.8.9 or newer is recommended). # See home page: https://github.com/taka-no-me/android-cmake # # Usage Linux: @@ -39,12 +39,6 @@ # $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .. # $ make -j8 # -# Usage Linux (using standalone toolchain): -# $ export ANDROID_STANDALONE_TOOLCHAIN=/absolute/path/to/android-toolchain -# $ mkdir build && cd build -# $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .. -# $ make -j8 -# # Usage Windows: # You need native port of make to build your project. # Android NDK r7 (and newer) already has make.exe on board. @@ -63,11 +57,6 @@ # ANDROID_NDK=/opt/android-ndk - path to the NDK root. # Can be set as environment variable. Can be set only at first cmake run. # -# ANDROID_STANDALONE_TOOLCHAIN=/opt/android-toolchain - path to the -# standalone toolchain. This option is not used if full NDK is found -# (ignored if ANDROID_NDK is set). -# Can be set as environment variable. Can be set only at first cmake run. -# # ANDROID_ABI=armeabi-v7a - specifies the target Application Binary # Interface (ABI). This option nearly matches to the APP_ABI variable # used by ndk-build tool from Android NDK. @@ -123,8 +112,8 @@ # * x86_64-clang3.5 # # ANDROID_FORCE_ARM_BUILD=OFF - set ON to generate 32-bit ARM instructions -# instead of Thumb. Is not available for "x86" (inapplicable) and -# "armeabi-v6 with VFP" (is forced to be ON) ABIs. +# instead of Thumb. Is not available for "armeabi-v6 with VFP" +# (is forced to be ON) ABI. # # ANDROID_NO_UNDEFINED=ON - set ON to show all undefined symbols as linker # errors even if they are not used. @@ -133,13 +122,6 @@ # libraries. Automatically turned for NDK r5x and r6x due to GLESv2 # problems. # -# LIBRARY_OUTPUT_PATH_ROOT=${CMAKE_SOURCE_DIR} - where to output binary -# files. See additional details below. -# -# ANDROID_SET_OBSOLETE_VARIABLES=ON - if set, then toolchain defines some -# obsolete variables which were used by previous versions of this file for -# backward compatibility. -# # ANDROID_STL=gnustl_static - specify the runtime to use. # # Possible values are: @@ -172,6 +154,8 @@ # Implies -frtti -fno-exceptions. # Available for NDK r7b and newer. # Silently degrades to gnustl_static if not available. +# c++_static -> Use the LLVM libc++ runtime as a static library. +# c++_shared -> Use the LLVM libc++ runtime as a shared library. # # ANDROID_STL_FORCE_FEATURES=ON - turn rtti and exceptions support based on # chosen runtime. If disabled, then the user is responsible for settings @@ -200,12 +184,6 @@ # will be set true, mutually exclusive. NEON option will be set true # if VFP is set to NEON. # -# LIBRARY_OUTPUT_PATH_ROOT should be set in cache to determine where Android -# libraries will be installed. -# Default is ${CMAKE_SOURCE_DIR}, and the android libs will always be -# under the ${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME} -# (depending on the target ABI). This is convenient for Android packaging. -# # ------------------------------------------------------------------------------ cmake_minimum_required( VERSION 2.6.3 ) @@ -235,22 +213,22 @@ endif() # this one not so much set( CMAKE_SYSTEM_VERSION 1 ) -# rpath makes low sence for Android +# rpath makes low sense for Android set( CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "" ) set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." ) # NDK search paths -set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r10c -r10b -r10 -r9d -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) -if(NOT DEFINED ANDROID_NDK_SEARCH_PATHS) +set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r10d -r10c -r10b -r10 -r9d -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) +if( NOT DEFINED ANDROID_NDK_SEARCH_PATHS ) if( CMAKE_HOST_WIN32 ) file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS ) - set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}/android-ndk" "$ENV{SystemDrive}/NVPACK/android-ndk" ) + set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}" "$ENV{SystemDrive}/NVPACK" ) else() file( TO_CMAKE_PATH "$ENV{HOME}" ANDROID_NDK_SEARCH_PATHS ) - set( ANDROID_NDK_SEARCH_PATHS /opt/android-ndk "${ANDROID_NDK_SEARCH_PATHS}/NVPACK/android-ndk" ) + set( ANDROID_NDK_SEARCH_PATHS /opt "${ANDROID_NDK_SEARCH_PATHS}/NVPACK" ) endif() endif() -if(NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH) +if( NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) set( ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH /opt/android-toolchain ) endif() @@ -272,106 +250,90 @@ set( ANDROID_DEFAULT_NDK_API_LEVEL_mips64 21 ) macro( __LIST_FILTER listvar regex ) - if( ${listvar} ) - foreach( __val ${${listvar}} ) - if( __val MATCHES "${regex}" ) - list( REMOVE_ITEM ${listvar} "${__val}" ) - endif() - endforeach() - endif() + if( ${listvar} ) + foreach( __val ${${listvar}} ) + if( __val MATCHES "${regex}" ) + list( REMOVE_ITEM ${listvar} "${__val}" ) + endif() + endforeach() + endif() endmacro() macro( __INIT_VARIABLE var_name ) - set( __test_path 0 ) - foreach( __var ${ARGN} ) - if( __var STREQUAL "PATH" ) - set( __test_path 1 ) - break() - endif() - endforeach() - if( __test_path AND NOT EXISTS "${${var_name}}" ) - unset( ${var_name} CACHE ) - endif() - if( "${${var_name}}" STREQUAL "" ) - set( __values 0 ) + set( __test_path 0 ) foreach( __var ${ARGN} ) - if( __var STREQUAL "VALUES" ) - set( __values 1 ) - elseif( NOT __var STREQUAL "PATH" ) - set( __obsolete 0 ) - if( __var MATCHES "^OBSOLETE_.*$" ) - string( REPLACE "OBSOLETE_" "" __var "${__var}" ) - set( __obsolete 1 ) - endif() - if( __var MATCHES "^ENV_.*$" ) - string( REPLACE "ENV_" "" __var "${__var}" ) - set( __value "$ENV{${__var}}" ) - elseif( DEFINED ${__var} ) - set( __value "${${__var}}" ) - else() - if( __values ) - set( __value "${__var}" ) - else() - set( __value "" ) - endif() - endif() - if( NOT "${__value}" STREQUAL "" ) - if( __test_path ) - if( EXISTS "${__value}" ) - file( TO_CMAKE_PATH "${__value}" ${var_name} ) - if( __obsolete AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "Using value of obsolete variable ${__var} as initial value for ${var_name}. Please note, that ${__var} can be completely removed in future versions of the toolchain." ) - endif() - break() - endif() - else() - set( ${var_name} "${__value}" ) - if( __obsolete AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "Using value of obsolete variable ${__var} as initial value for ${var_name}. Please note, that ${__var} can be completely removed in future versions of the toolchain." ) - endif() + if( __var STREQUAL "PATH" ) + set( __test_path 1 ) break() - endif() endif() - endif() endforeach() - unset( __value ) - unset( __values ) - unset( __obsolete ) - elseif( __test_path ) - file( TO_CMAKE_PATH "${${var_name}}" ${var_name} ) - endif() - unset( __test_path ) + + if( __test_path AND NOT EXISTS "${${var_name}}" ) + unset( ${var_name} CACHE ) + endif() + + if( " ${${var_name}}" STREQUAL " " ) + set( __values 0 ) + foreach( __var ${ARGN} ) + if( __var STREQUAL "VALUES" ) + set( __values 1 ) + elseif( NOT __var STREQUAL "PATH" ) + if( __var MATCHES "^ENV_.*$" ) + string( REPLACE "ENV_" "" __var "${__var}" ) + set( __value "$ENV{${__var}}" ) + elseif( DEFINED ${__var} ) + set( __value "${${__var}}" ) + elseif( __values ) + set( __value "${__var}" ) + else() + set( __value "" ) + endif() + + if( NOT " ${__value}" STREQUAL " " AND (NOT __test_path OR EXISTS "${__value}") ) + set( ${var_name} "${__value}" ) + break() + endif() + endif() + endforeach() + unset( __value ) + unset( __values ) + endif() + + if( __test_path ) + file( TO_CMAKE_PATH "${${var_name}}" ${var_name} ) + endif() + unset( __test_path ) endmacro() macro( __DETECT_NATIVE_API_LEVEL _var _path ) - SET( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*.*$" ) - FILE( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" ) - if( NOT __apiFileContent ) - message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." ) - endif() - string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" ) - unset( __apiFileContent ) - unset( __ndkApiLevelRegex ) + set( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*.*$" ) + file( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" ) + if( NOT __apiFileContent ) + message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." ) + endif() + string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" ) + unset( __apiFileContent ) + unset( __ndkApiLevelRegex ) endmacro() macro( __DETECT_TOOLCHAIN_MACHINE_NAME _var _root ) if( EXISTS "${_root}" ) - file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) - __LIST_FILTER( __gccExePath "^[.].*" ) - list( LENGTH __gccExePath __gccExePathsCount ) - if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "Could not determine machine name for compiler from ${_root}" ) - set( ${_var} "" ) + file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) + __LIST_FILTER( __gccExePath "^[.].*" ) + list( LENGTH __gccExePath __gccExePathsCount ) + if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "Could not determine machine name for compiler from ${_root}" ) + set( ${_var} "" ) + else() + get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) + string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) + endif() + unset( __gccExePath ) + unset( __gccExePathsCount ) + unset( __gccExeName ) else() - get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) - string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) + set( ${_var} "" ) endif() - unset( __gccExePath ) - unset( __gccExePathsCount ) - unset( __gccExeName ) - else() - set( ${_var} "" ) - endif() endmacro() @@ -419,17 +381,19 @@ if( NOT ANDROID_NDK_HOST_X64 ) endif() # see if we have path to Android NDK -__INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) +if( NOT ANDROID_NDK AND NOT ANDROID_STANDALONE_TOOLCHAIN ) + __INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) +endif() if( NOT ANDROID_NDK ) # see if we have path to Android standalone toolchain - __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN OBSOLETE_ANDROID_NDK_TOOLCHAIN_ROOT OBSOLETE_ENV_ANDROID_NDK_TOOLCHAIN_ROOT ) + __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN ) if( NOT ANDROID_STANDALONE_TOOLCHAIN ) #try to find Android NDK in one of the the default locations set( __ndkSearchPaths ) foreach( __ndkSearchPath ${ANDROID_NDK_SEARCH_PATHS} ) foreach( suffix ${ANDROID_SUPPORTED_NDK_VERSIONS} ) - list( APPEND __ndkSearchPaths "${__ndkSearchPath}${suffix}" ) + list( APPEND __ndkSearchPaths "${__ndkSearchPath}/android-ndk${suffix}" ) endforeach() endforeach() __INIT_VARIABLE( ANDROID_NDK PATH VALUES ${__ndkSearchPaths} ) @@ -487,7 +451,7 @@ else() or export ANDROID_STANDALONE_TOOLCHAIN=~/my-android-toolchain or put the toolchain or NDK in the default path: - sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH} + sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH}/android-ndk sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" ) endif() @@ -636,7 +600,7 @@ if( BUILD_WITH_ANDROID_NDK ) endif() if( NOT __availableToolchains ) file( GLOB __availableToolchainsLst RELATIVE "${ANDROID_NDK_TOOLCHAINS_PATH}" "${ANDROID_NDK_TOOLCHAINS_PATH}/*" ) - if( __availableToolchains ) + if( __availableToolchainsLst ) list(SORT __availableToolchainsLst) # we need clang to go after gcc endif() __LIST_FILTER( __availableToolchainsLst "^[.]" ) @@ -669,7 +633,7 @@ if( NOT ANDROID_SUPPORTED_ABIS ) endif() # choose target ABI -__INIT_VARIABLE( ANDROID_ABI OBSOLETE_ARM_TARGET OBSOLETE_ARM_TARGETS VALUES ${ANDROID_SUPPORTED_ABIS} ) +__INIT_VARIABLE( ANDROID_ABI VALUES ${ANDROID_SUPPORTED_ABIS} ) # verify that target ABI is supported list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx ) if( __androidAbiIdx EQUAL -1 ) @@ -760,7 +724,7 @@ if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMa endif() if( ANDROID_ARCH_NAME STREQUAL "arm" AND NOT ARMEABI_V6 ) - __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD OBSOLETE_FORCE_ARM VALUES OFF ) + __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD VALUES OFF ) set( ANDROID_FORCE_ARM_BUILD ${ANDROID_FORCE_ARM_BUILD} CACHE BOOL "Use 32-bit ARM instructions instead of Thumb-1" FORCE ) mark_as_advanced( ANDROID_FORCE_ARM_BUILD ) else() @@ -845,6 +809,7 @@ else() unset( __realApiLevel ) endif() set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE ) + set( CMAKE_ANDROID_API ${ANDROID_NATIVE_API_LEVEL} ) if( CMAKE_VERSION VERSION_GREATER "2.8" ) list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS ) set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) @@ -863,23 +828,14 @@ endif() # runtime choice (STL, rtti, exceptions) if( NOT ANDROID_STL ) - # honor legacy ANDROID_USE_STLPORT - if( DEFINED ANDROID_USE_STLPORT ) - if( ANDROID_USE_STLPORT ) - set( ANDROID_STL stlport_static ) - endif() - message( WARNING "You are using an obsolete variable ANDROID_USE_STLPORT to select the STL variant. Use -DANDROID_STL=stlport_static instead." ) - endif() - if( NOT ANDROID_STL ) set( ANDROID_STL gnustl_static ) - endif() endif() set( ANDROID_STL "${ANDROID_STL}" CACHE STRING "C++ runtime" ) set( ANDROID_STL_FORCE_FEATURES ON CACHE BOOL "automatically configure rtti and exceptions support based on C++ runtime" ) mark_as_advanced( ANDROID_STL ANDROID_STL_FORCE_FEATURES ) if( BUILD_WITH_ANDROID_NDK ) - if( NOT "${ANDROID_STL}" MATCHES "^(none|system|system_re|gabi\\+\\+_static|gabi\\+\\+_shared|stlport_static|stlport_shared|gnustl_static|gnustl_shared)$") + if( NOT "${ANDROID_STL}" MATCHES "^(none|system|system_re|gabi\\+\\+_static|gabi\\+\\+_shared|stlport_static|stlport_shared|gnustl_static|gnustl_shared|c\\+\\+_static|c\\+\\+_shared)$") message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". The possible values are: none -> Do not configure the runtime. @@ -891,6 +847,8 @@ The possible values are: stlport_shared -> Use the STLport runtime as a shared library. gnustl_static -> (default) Use the GNU STL as a static library. gnustl_shared -> Use the GNU STL as a shared library. + c++_static -> Use the LLVM libc++ runtime as a static library. + c++_shared -> Use the LLVM libc++ runtime as a shared library. " ) endif() elseif( BUILD_WITH_STANDALONE_TOOLCHAIN ) @@ -1033,7 +991,7 @@ if( BUILD_WITH_ANDROID_NDK ) set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) elseif( ANDROID_STL MATCHES "gabi" ) if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - message( FATAL_ERROR "gabi++ is not awailable in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.") + message( FATAL_ERROR "gabi++ is not available in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.") endif() set( ANDROID_RTTI ON ) set( ANDROID_EXCEPTIONS OFF ) @@ -1066,12 +1024,40 @@ if( BUILD_WITH_ANDROID_NDK ) else() set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++" ) endif() - set( ANDROID_STL_INCLUDE_DIRS "${__libstl}/include" "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/include" ) + set( ANDROID_STL_INCLUDE_DIRS "${__libstl}/include" "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/include" "${__libstl}/include/backward" ) if( EXISTS "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) else() set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libstdc++.a" ) endif() + elseif( ANDROID_STL MATCHES "c\\+\\+_shared" OR ANDROID_STL MATCHES "c\\+\\+_static" ) + set( ANDROID_EXCEPTIONS ON ) + set( ANDROID_RTTI ON ) + set( ANDROID_CXX_ROOT "${ANDROID_NDK}/sources/cxx-stl/" ) + set( ANDROID_LLVM_ROOT "${ANDROID_CXX_ROOT}/llvm-libc++" ) + + if( X86 ) + set( ANDROID_ABI_INCLUDE_DIRS "${ANDROID_CXX_ROOT}/gabi++/include" ) + else() + set( ANDROID_ABI_INCLUDE_DIRS "${ANDROID_CXX_ROOT}/llvm-libc++abi/include" ) + endif() + + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_LLVM_ROOT}/libcxx/include" "${ANDROID_ABI_INCLUDE_DIRS}" ) + + # android support sfiles + include_directories ( SYSTEM ${ANDROID_NDK}/sources/android/support/include ) + + if(ANDROID_STL MATCHES "c\\+\\+_shared") + set ( LLVM_LIBRARY_NAME "libc++_shared.so") + else() + set ( LLVM_LIBRARY_NAME "libc++_static.a" ) + endif () + + if( EXISTS "${ANDROID_LLVM_ROOT}/libs/${ANDROID_NDK_ABI_NAME}/${LLVM_LIBRARY_NAME}" ) + set( __libstl "${ANDROID_LLVM_ROOT}/libs/${ANDROID_NDK_ABI_NAME}/${LLVM_LIBRARY_NAME}" ) + else() + message( FATAL_ERROR "Could not find libc++ library" ) + endif() else() message( FATAL_ERROR "Unknown runtime: ${ANDROID_STL}" ) endif() @@ -1144,7 +1130,12 @@ if( NOT CMAKE_C_COMPILER ) endif() set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "assembler" ) set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" ) - set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) + if( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" ) + # Use gcc-ar if we have it for better LTO support. + set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) + else() + set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) + endif() set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" ) set( CMAKE_NM "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm" ) set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" ) @@ -1168,7 +1159,7 @@ endif() include( CMakeForceCompiler ) CMAKE_FORCE_C_COMPILER( "${CMAKE_C_COMPILER}" GNU ) if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_C_COMPILER_ID Clang) + set( CMAKE_C_COMPILER_ID Clang ) endif() set( CMAKE_C_PLATFORM_ID Linux ) if( X86_64 OR MIPS64 OR ARM64_V8A ) @@ -1195,6 +1186,14 @@ set( CMAKE_ASM_COMPILER_FORCED TRUE ) set( CMAKE_COMPILER_IS_GNUASM 1) set( CMAKE_ASM_SOURCE_FILE_EXTENSIONS s S asm ) +foreach( lang C CXX ASM ) + if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_CLANG_VERSION} ) + else() + set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_COMPILER_VERSION} ) + endif() +endforeach() + # flags and definitions remove_definitions( -DANDROID ) add_definitions( -DANDROID ) @@ -1225,14 +1224,14 @@ endif() # NDK flags if (ARM64_V8A ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fpic -ffunction-sections -funwind-tables" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) if( NOT ANDROID_COMPILER_IS_CLANG ) set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) endif() elseif( ARMEABI OR ARMEABI_V7A) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fpic -funwind-tables" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 ) set( ANDROID_CXX_FLAGS_RELEASE "-mthumb -fomit-frame-pointer -fno-strict-aliasing" ) set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) @@ -1251,13 +1250,11 @@ elseif( X86 OR X86_64 ) set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) if( NOT ANDROID_COMPILER_IS_CLANG ) set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) - else() - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fPIC" ) endif() set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) elseif( MIPS OR MIPS64 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fpic -fno-strict-aliasing -finline-functions -ffunction-sections -funwind-tables -fmessage-length=0" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-strict-aliasing -finline-functions -funwind-tables -fmessage-length=0" ) set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer" ) set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer" ) if( NOT ANDROID_COMPILER_IS_CLANG ) @@ -1342,7 +1339,7 @@ if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 else() __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) endif() -__INIT_VARIABLE( ANDROID_NO_UNDEFINED OBSOLETE_NO_UNDEFINED VALUES ON ) +__INIT_VARIABLE( ANDROID_NO_UNDEFINED VALUES ON ) __INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) __INIT_VARIABLE( ANDROID_GOLD_LINKER VALUES ON ) __INIT_VARIABLE( ANDROID_NOEXECSTACK VALUES ON ) @@ -1350,7 +1347,7 @@ __INIT_VARIABLE( ANDROID_RELRO VALUES ON ) set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" ) set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) -set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Put each function in separate section and enable garbage collection of unused input sections at link time" ) set( ANDROID_GOLD_LINKER ${ANDROID_GOLD_LINKER} CACHE BOOL "Enables gold linker" ) set( ANDROID_NOEXECSTACK ${ANDROID_NOEXECSTACK} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) set( ANDROID_RELRO ${ANDROID_RELRO} CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" ) @@ -1452,6 +1449,16 @@ if( MIPS AND BUILD_WITH_ANDROID_NDK AND ANDROID_NDK_RELEASE STREQUAL "r8" ) set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.x ${CMAKE_EXE_LINKER_FLAGS}" ) endif() +# pie/pic +if( NOT (ANDROID_NATIVE_API_LEVEL LESS 16) AND (NOT DEFINED ANDROID_APP_PIE OR ANDROID_APP_PIE) AND (CMAKE_VERSION VERSION_GREATER 2.8.8) ) + set( CMAKE_POSITION_INDEPENDENT_CODE TRUE ) + set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie") +else() + set( CMAKE_POSITION_INDEPENDENT_CODE FALSE ) + set( CMAKE_CXX_FLAGS "-fpic ${CMAKE_CXX_FLAGS}" ) + set( CMAKE_C_FLAGS "-fpic ${CMAKE_C_FLAGS}" ) +endif() + # configure rtti if( DEFINED ANDROID_RTTI AND ANDROID_STL_FORCE_FEATURES ) if( ANDROID_RTTI ) @@ -1515,27 +1522,31 @@ if( ANDROID_EXPLICIT_CRT_LINK ) endif() # setup output directories -set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "root for library output, set this to change where android libs are installed to" ) set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" ) -if(NOT _CMAKE_IN_TRY_COMPILE) - if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) - else() - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) - endif() - set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "path for android libs" ) +if( DEFINED LIBRARY_OUTPUT_PATH_ROOT + OR EXISTS "${CMAKE_SOURCE_DIR}/AndroidManifest.xml" + OR (EXISTS "${CMAKE_SOURCE_DIR}/../AndroidManifest.xml" AND EXISTS "${CMAKE_SOURCE_DIR}/../jni/") ) + set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "Root for binaries output, set this to change where Android libs are installed to" ) + if( NOT _CMAKE_IN_TRY_COMPILE ) + if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) + set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) + else() + set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) + endif() + set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for Android libs" ) + endif() endif() # copy shaed stl library to build directory -if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" ) - get_filename_component( __libstlname "${__libstl}" NAME ) - execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) - if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") - message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) - endif() - unset( __fileCopyProcess ) - unset( __libstlname ) +if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" AND DEFINED LIBRARY_OUTPUT_PATH ) + get_filename_component( __libstlname "${__libstl}" NAME ) + execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) + if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") + message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) + endif() + unset( __fileCopyProcess ) + unset( __libstlname ) endif() @@ -1596,28 +1607,10 @@ macro( find_host_program ) endmacro() -macro( ANDROID_GET_ABI_RAWNAME TOOLCHAIN_FLAG VAR ) - if( "${TOOLCHAIN_FLAG}" STREQUAL "ARMEABI" ) - set( ${VAR} "armeabi" ) - elseif( "${TOOLCHAIN_FLAG}" STREQUAL "ARMEABI_V7A" ) - set( ${VAR} "armeabi-v7a" ) - elseif( "${TOOLCHAIN_FLAG}" STREQUAL "X86" ) - set( ${VAR} "x86" ) - elseif( "${TOOLCHAIN_FLAG}" STREQUAL "MIPS" ) - set( ${VAR} "mips" ) - else() - set( ${VAR} "unknown" ) - endif() -endmacro() - -if (POLICY CMP0054) - cmake_policy(SET CMP0054 NEW) -endif () - # export toolchain settings for the try_compile() command -if( NOT PROJECT_NAME STREQUAL "CMAKE_TRY_COMPILE" ) +if( NOT _CMAKE_IN_TRY_COMPILE ) set( __toolchain_config "") - foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN ANDROID_SET_OBSOLETE_VARIABLES + foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN ANDROID_NDK_HOST_X64 ANDROID_NDK ANDROID_NDK_LAYOUT @@ -1636,9 +1629,10 @@ if( NOT PROJECT_NAME STREQUAL "CMAKE_TRY_COMPILE" ) ANDROID_RELRO ANDROID_LIBM_PATH ANDROID_EXPLICIT_CRT_LINK + ANDROID_APP_PIE ) if( DEFINED ${__var} ) - if( "${__var}" MATCHES " ") + if( ${__var} MATCHES " ") set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" ) else() set( __toolchain_config "${__toolchain_config}set( ${__var} ${${__var}} CACHE INTERNAL \"\" )\n" ) @@ -1663,16 +1657,6 @@ if( CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_HOST_WIN32 ) endif() -# set some obsolete variables for backward compatibility -set( ANDROID_SET_OBSOLETE_VARIABLES ON CACHE BOOL "Define obsolete Andrid-specific cmake variables" ) -mark_as_advanced( ANDROID_SET_OBSOLETE_VARIABLES ) -if( ANDROID_SET_OBSOLETE_VARIABLES ) - set( ANDROID_API_LEVEL ${ANDROID_NATIVE_API_LEVEL} ) - set( ARM_TARGET "${ANDROID_ABI}" ) - set( ARMEABI_NDK_NAME "${ANDROID_NDK_ABI_NAME}" ) -endif() - - # Variables controlling behavior or set by cmake toolchain: # ANDROID_ABI : "armeabi-v7a" (default), "armeabi", "armeabi-v7a with NEON", "armeabi-v7a with VFPV3", "armeabi-v6 with VFP", "x86", "mips", "arm64-v8a", "x86_64", "mips64" # ANDROID_NATIVE_API_LEVEL : 3,4,5,8,9,14,15,16,17,18,19,21 (depends on NDK version) @@ -1686,22 +1670,15 @@ endif() # ANDROID_RELRO : ON/OFF # ANDROID_FORCE_ARM_BUILD : ON/OFF # ANDROID_STL_FORCE_FEATURES : ON/OFF -# ANDROID_SET_OBSOLETE_VARIABLES : ON/OFF +# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product//obj/lib/libm.so) to workaround unresolved `sincos` # Can be set only at the first run: -# ANDROID_NDK -# ANDROID_STANDALONE_TOOLCHAIN +# ANDROID_NDK : path to your NDK install +# NDK_CCACHE : path to your ccache executable # ANDROID_TOOLCHAIN_NAME : the NDK name of compiler toolchain # ANDROID_NDK_HOST_X64 : try to use x86_64 toolchain (default for x64 host systems) # ANDROID_NDK_LAYOUT : the inner NDK structure (RELEASE, LINARO, ANDROID) # LIBRARY_OUTPUT_PATH_ROOT : -# NDK_CCACHE : -# Obsolete: -# ANDROID_API_LEVEL : superseded by ANDROID_NATIVE_API_LEVEL -# ARM_TARGET : superseded by ANDROID_ABI -# ARM_TARGETS : superseded by ANDROID_ABI (can be set only) -# ANDROID_NDK_TOOLCHAIN_ROOT : superseded by ANDROID_STANDALONE_TOOLCHAIN (can be set only) -# ANDROID_USE_STLPORT : superseded by ANDROID_STL=stlport_static -# ANDROID_LEVEL : superseded by ANDROID_NATIVE_API_LEVEL (completely removed) +# ANDROID_STANDALONE_TOOLCHAIN # # Primary read-only variables: # ANDROID : always TRUE @@ -1715,19 +1692,16 @@ endif() # X86_64 : TRUE if configured for x86_64 # MIPS : TRUE if configured for mips # MIPS64 : TRUE if configured for mips64 -# BUILD_ANDROID : always TRUE # BUILD_WITH_ANDROID_NDK : TRUE if NDK is used # BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used # ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform # ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a", "x86", "mips", "arm64-v8a", "x86_64", "mips64" depending on ANDROID_ABI -# ANDROID_NDK_RELEASE : from r5 to r10c; set only for NDK +# ANDROID_NDK_RELEASE : from r5 to r10d; set only for NDK # ANDROID_NDK_RELEASE_NUM : numeric ANDROID_NDK_RELEASE version (1000*major+minor) # ANDROID_ARCH_NAME : "arm", "x86", "mips", "arm64", "x86_64", "mips64" depending on ANDROID_ABI # ANDROID_SYSROOT : path to the compiler sysroot # TOOL_OS_SUFFIX : "" or ".exe" depending on host platform # ANDROID_COMPILER_IS_CLANG : TRUE if clang compiler is used -# Obsolete: -# ARMEABI_NDK_NAME : superseded by ANDROID_NDK_ABI_NAME # # Secondary (less stable) read-only variables: # ANDROID_COMPILER_VERSION : GCC version used (not Clang version) @@ -1742,12 +1716,10 @@ endif() # ANDROID_RTTI : if rtti is enabled by the runtime # ANDROID_EXCEPTIONS : if exceptions are enabled by the runtime # ANDROID_GCC_TOOLCHAIN_NAME : read-only, differs from ANDROID_TOOLCHAIN_NAME only if clang is used -# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product//obj/lib/libm.so) to workaround unresolved `sincos` # # Defaults: # ANDROID_DEFAULT_NDK_API_LEVEL # ANDROID_DEFAULT_NDK_API_LEVEL_${ARCH} # ANDROID_NDK_SEARCH_PATHS -# ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH # ANDROID_SUPPORTED_ABIS_${ARCH} -# ANDROID_SUPPORTED_NDK_VERSIONS \ No newline at end of file +# ANDROID_SUPPORTED_NDK_VERSIONS diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index 5b8a689a9a..54a4a47929 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -20,8 +20,8 @@ if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.3.0_public.zip - URL_MD5 a2dcf695e0f03a70fdd1ed7480585e82 + URL https://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.8.0_public.zip + URL_MD5 bea17e04acc1dd8cf7cabefa1b28cc3c CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" @@ -29,8 +29,8 @@ if (WIN32) ) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - - set(LIBOVR_DIR ${SOURCE_DIR}/OculusSDK/LibOVR) + message("LIBOVR dir ${SOURCE_DIR}") + set(LIBOVR_DIR ${SOURCE_DIR}/LibOVR) if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") set(LIBOVR_LIB_DIR ${LIBOVR_DIR}/Lib/Windows/x64/Release/VS2013 CACHE TYPE INTERNAL) else() @@ -38,6 +38,7 @@ if (WIN32) endif() set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${LIBOVR_DIR}/Include CACHE TYPE INTERNAL) + message("LIBOVR include dir ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS}") set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${LIBOVR_LIB_DIR}/LibOVR.lib CACHE TYPE INTERNAL) elseif(APPLE) diff --git a/cmake/externals/glew/CMakeLists.txt b/cmake/externals/glew/CMakeLists.txt index c9ad5f837a..28a599bfa6 100644 --- a/cmake/externals/glew/CMakeLists.txt +++ b/cmake/externals/glew/CMakeLists.txt @@ -15,7 +15,6 @@ ExternalProject_Add( LOG_BUILD 1 ) - # Hide this external target (for ide users) set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") @@ -32,4 +31,4 @@ elseif (WIN32) endif () set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glew_d.${LIB_EXT} CACHE FILEPATH "Path to glew debug library") -set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glew.${LIB_EXT} CACHE FILEPATH "Path to glew release library") \ No newline at end of file +set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glew.${LIB_EXT} CACHE FILEPATH "Path to glew release library") diff --git a/cmake/externals/gli/CMakeLists.txt b/cmake/externals/gli/CMakeLists.txt new file mode 100644 index 0000000000..2ef4d2b3af --- /dev/null +++ b/cmake/externals/gli/CMakeLists.txt @@ -0,0 +1,21 @@ +set(EXTERNAL_NAME gli) + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://hifi-public.s3.amazonaws.com/dependencies/gli-0.8.1.0.zip + URL_MD5 00c990f59c12bbf367956ef399d6f798 + BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 +) + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR} CACHE PATH "List of gli include directories") \ No newline at end of file diff --git a/cmake/externals/glm/CMakeLists.txt b/cmake/externals/glm/CMakeLists.txt index 74e98ad19e..79a44fa48e 100644 --- a/cmake/externals/glm/CMakeLists.txt +++ b/cmake/externals/glm/CMakeLists.txt @@ -3,8 +3,8 @@ set(EXTERNAL_NAME glm) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.5.4.zip - URL_MD5 fab76fc982b256b46208e5c750ed456a + URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.zip + URL_MD5 579ac77a3110befa3244d68c0ceb7281 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= LOG_DOWNLOAD 1 diff --git a/cmake/externals/hifiAudioCodec/CMakeLists.txt b/cmake/externals/hifiAudioCodec/CMakeLists.txt index 87b5044b42..a30396c6fd 100644 --- a/cmake/externals/hifiAudioCodec/CMakeLists.txt +++ b/cmake/externals/hifiAudioCodec/CMakeLists.txt @@ -5,39 +5,43 @@ set(EXTERNAL_NAME hifiAudioCodec) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -if (WIN32 OR APPLE) - ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-1.zip - URL_MD5 23ec3fe51eaa155ea159a4971856fc13 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - ) -elseif(NOT ANDROID) - ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux.zip - URL_MD5 7d37914a18aa4de971d2f45dd3043bde - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - ) -endif() - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL) - -if (WIN32) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL) -elseif(APPLE) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL) -elseif(NOT ANDROID) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL) +if (NOT ANDROID) + + if (WIN32 OR APPLE) + ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-1.zip + URL_MD5 23ec3fe51eaa155ea159a4971856fc13 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + ) + else () + ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux.zip + URL_MD5 7d37914a18aa4de971d2f45dd3043bde + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + ) + endif() + + # Hide this external target (for ide users) + set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + + ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL) + + if (WIN32) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL) + elseif(APPLE) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL) + elseif(NOT ANDROID) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL) + endif() + endif() diff --git a/cmake/externals/sdl2/CMakeLists.txt b/cmake/externals/sdl2/CMakeLists.txt index 4222cd41f0..cb61516b9a 100644 --- a/cmake/externals/sdl2/CMakeLists.txt +++ b/cmake/externals/sdl2/CMakeLists.txt @@ -46,7 +46,7 @@ else () if (ANDROID) set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") endif () - + ExternalProject_Add( ${EXTERNAL_NAME} URL http://www.libsdl.org/release/SDL2-2.0.3.tar.gz @@ -61,7 +61,6 @@ endif () # Hide this external target (for ide users) set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - if (APPLE) # NOOP @@ -78,9 +77,9 @@ elseif (WIN32) set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library") set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL") endif() - + add_paths_to_fixup_libs(${${EXTERNAL_NAME_UPPER}_DLL_PATH}) - + else () ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) diff --git a/cmake/externals/tbb/CMakeLists.txt b/cmake/externals/tbb/CMakeLists.txt index 6ea03fd7a4..74f7e10859 100644 --- a/cmake/externals/tbb/CMakeLists.txt +++ b/cmake/externals/tbb/CMakeLists.txt @@ -3,9 +3,9 @@ set(EXTERNAL_NAME tbb) include(ExternalProject) if (ANDROID) - + find_program(NDK_BUILD_COMMAND NAMES ndk-build DOC "Path to the ndk-build command") - + ExternalProject_Add( ${EXTERNAL_NAME} URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150316oss_src.tgz @@ -20,7 +20,7 @@ if (ANDROID) ) elseif (APPLE) find_program(MAKE_COMMAND NAMES make DOC "Path to the make command") - + ExternalProject_Add( ${EXTERNAL_NAME} URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150316oss_src.tgz @@ -37,11 +37,11 @@ else () if (WIN32) set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150316oss_win.zip) set(DOWNLOAD_MD5 d250d40bb93b255f75bcbb19e976a440) - else () + else () set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150316oss_lin.tgz) set(DOWNLOAD_MD5 7830ba2bc62438325fba2ec0c95367a5) endif () - + ExternalProject_Add( ${EXTERNAL_NAME} URL ${DOWNLOAD_URL} @@ -60,11 +60,11 @@ ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -if (APPLE) +if (APPLE) set(_TBB_LIB_DIR "${SOURCE_DIR}/lib") set(_LIB_PREFIX "lib") set(_LIB_EXT "dylib") - + ExternalProject_Add_Step( ${EXTERNAL_NAME} change-install-name @@ -74,7 +74,7 @@ if (APPLE) WORKING_DIRECTORY LOG 1 ) - + elseif (WIN32) if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/intel64/vc12") @@ -91,18 +91,18 @@ elseif (ANDROID) elseif (UNIX) set(_LIB_PREFIX "lib") set(_LIB_EXT "so") - + if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_TBB_ARCH_DIR "intel64") else() set(_TBB_ARCH_DIR "ia32") endif() - + execute_process( COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION ) - + if (GCC_VERSION VERSION_GREATER 4.4 OR GCC_VERSION VERSION_EQUAL 4.4) set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/${_TBB_ARCH_DIR}/gcc4.4") elseif (GCC_VERSION VERSION_GREATER 4.1 OR GCC_VERSION VERSION_EQUAL 4.1) @@ -110,9 +110,9 @@ elseif (UNIX) else () message(STATUS "Could not find a compatible version of Threading Building Blocks library for your compiler.") endif () - - -endif () + + +endif () if (DEFINED _TBB_LIB_DIR) set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${_TBB_LIB_DIR}/${_LIB_PREFIX}tbb_debug.${_LIB_EXT} CACHE FILEPATH "TBB debug library location") diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt new file mode 100644 index 0000000000..019920df77 --- /dev/null +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -0,0 +1,24 @@ +if (WIN32) + + set(EXTERNAL_NAME wasapi) + string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + + include(ExternalProject) + ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi.zip + URL_MD5 11c8a7728d6eda7223df800e10b70723 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + ) + + # Hide this external target (for ide users) + set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + + ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + + set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR} CACHE FILEPATH "Location of wasapi DLL") + +endif() diff --git a/cmake/macros/AutoMTC.cmake b/cmake/macros/AutoMTC.cmake deleted file mode 100644 index de60d5c21f..0000000000 --- a/cmake/macros/AutoMTC.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# -# AutoMTC.cmake -# -# Created by Andrzej Kapolka on 12/31/13. -# Copyright 2013 High Fidelity, Inc. -# -# Distributed under the Apache License, Version 2.0. -# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# - -macro(AUTO_MTC) - set(AUTOMTC_SRC ${TARGET_NAME}_automtc.cpp) - - file(GLOB INCLUDE_FILES src/*.h) - - if (NOT ANDROID) - set(MTC_EXECUTABLE mtc) - else () - set(MTC_EXECUTABLE $ENV{MTC_PATH}/mtc) - endif () - - add_custom_command(OUTPUT ${AUTOMTC_SRC} COMMAND ${MTC_EXECUTABLE} -o ${AUTOMTC_SRC} ${INCLUDE_FILES} DEPENDS ${MTC_EXECUTABLE} ${INCLUDE_FILES}) -endmacro() diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index e586304503..c43ade45d2 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -1,77 +1,92 @@ -# +# # AutoScribeShader.cmake -# +# # Created by Sam Gateau on 12/17/14. # 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 -# +# function(AUTOSCRIBE_SHADER SHADER_FILE) - - # Grab include files - foreach(includeFile ${ARGN}) - list(APPEND SHADER_INCLUDE_FILES ${includeFile}) - endforeach() + # Grab include files + foreach(includeFile ${ARGN}) + list(APPEND SHADER_INCLUDE_FILES ${includeFile}) + endforeach() - foreach(SHADER_INCLUDE ${SHADER_INCLUDE_FILES}) - get_filename_component(INCLUDE_DIR ${SHADER_INCLUDE} PATH) - list(APPEND SHADER_INCLUDES_PATHS ${INCLUDE_DIR}) - endforeach() + foreach(SHADER_INCLUDE ${SHADER_INCLUDE_FILES}) + get_filename_component(INCLUDE_DIR ${SHADER_INCLUDE} PATH) + list(APPEND SHADER_INCLUDES_PATHS ${INCLUDE_DIR}) + endforeach() + #Extract the unique include shader paths + set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}) + #message(${TARGET_NAME} Hifi for includes ${INCLUDES}) + foreach(EXTRA_SHADER_INCLUDE ${INCLUDES}) + list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE}) + endforeach() - #Extract the unique include shader paths - set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}) - #message(${TARGET_NAME} Hifi for includes ${INCLUDES}) - foreach(EXTRA_SHADER_INCLUDE ${INCLUDES}) - list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE}) - endforeach() + list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS) + #message(ready for includes ${SHADER_INCLUDES_PATHS}) - list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS) - #message(ready for includes ${SHADER_INCLUDES_PATHS}) + # make the scribe include arguments + set(SCRIBE_INCLUDES) + foreach(INCLUDE_PATH ${SHADER_INCLUDES_PATHS}) + set(SCRIBE_INCLUDES ${SCRIBE_INCLUDES} -I ${INCLUDE_PATH}/) + endforeach() - # make the scribe include arguments - set(SCRIBE_INCLUDES) - foreach(INCLUDE_PATH ${SHADER_INCLUDES_PATHS}) - set(SCRIBE_INCLUDES ${SCRIBE_INCLUDES} -I ${INCLUDE_PATH}/) - endforeach() + # Define the final name of the generated shader file + get_filename_component(SHADER_TARGET ${SHADER_FILE} NAME_WE) + get_filename_component(SHADER_EXT ${SHADER_FILE} EXT) + if(SHADER_EXT STREQUAL .slv) + set(SHADER_TARGET ${SHADER_TARGET}_vert.h) + elseif(${SHADER_EXT} STREQUAL .slf) + set(SHADER_TARGET ${SHADER_TARGET}_frag.h) + elseif(${SHADER_EXT} STREQUAL .slg) + set(SHADER_TARGET ${SHADER_TARGET}_geom.h) + endif() - # Define the final name of the generated shader file - get_filename_component(SHADER_TARGET ${SHADER_FILE} NAME_WE) - get_filename_component(SHADER_EXT ${SHADER_FILE} EXT) - if(SHADER_EXT STREQUAL .slv) - set(SHADER_TARGET ${SHADER_TARGET}_vert.h) - elseif(${SHADER_EXT} STREQUAL .slf) - set(SHADER_TARGET ${SHADER_TARGET}_frag.h) - elseif(${SHADER_EXT} STREQUAL .slg) - set(SHADER_TARGET ${SHADER_TARGET}_geom.h) - endif() + set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}") - set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}") + # Target dependant Custom rule on the SHADER_FILE + if (APPLE) + set(GLPROFILE MAC_GL) + set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - # Target dependant Custom rule on the SHADER_FILE - if (APPLE) - set(GLPROFILE MAC_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + elseif (ANDROID) + set(GLPROFILE LINUX_GL) + set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) - elseif (UNIX) - set(GLPROFILE LINUX_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + # for an android build, we can't use the scribe that cmake would normally produce as a target, + # since it's unrunnable by the cross-compiling build machine - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) - else () - set(GLPROFILE PC_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + # so, we require the compiling user to point us at a compiled executable version for their native toolchain + find_program(NATIVE_SCRIBE scribe PATHS ${SCRIBE_PATH} ENV SCRIBE_PATH) - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) - endif() + if (NOT NATIVE_SCRIBE) + message(FATAL_ERROR "The High Fidelity scribe tool is required for shader pre-processing. \ + Please compile scribe using your native toolchain and set SCRIBE_PATH to the path containing the scribe executable in your ENV.\ + ") + endif () - #output the generated file name - set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET} PARENT_SCOPE) + add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND ${NATIVE_SCRIBE} ${SCRIBE_ARGS} DEPENDS ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + elseif (UNIX) + set(GLPROFILE LINUX_GL) + set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - file(GLOB INCLUDE_FILES ${SHADER_TARGET}) + add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + else () + set(GLPROFILE PC_GL) + set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + + add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + endif() + + #output the generated file name + set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET} PARENT_SCOPE) + + file(GLOB INCLUDE_FILES ${SHADER_TARGET}) endfunction() @@ -79,11 +94,11 @@ endfunction() macro(AUTOSCRIBE_SHADER_LIB) set(HIFI_LIBRARIES_SHADER_INCLUDE_FILES "") file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}") - foreach(HIFI_LIBRARY ${ARGN}) + foreach(HIFI_LIBRARY ${ARGN}) #if (NOT TARGET ${HIFI_LIBRARY}) # file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}/src/) #endif () - + #file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src/*.slh) list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src) endforeach() @@ -99,9 +114,9 @@ macro(AUTOSCRIBE_SHADER_LIB) #message("${TARGET_NAME} ${SHADER_INCLUDE_FILES}") set(AUTOSCRIBE_SHADER_SRC "") foreach(SHADER_FILE ${SHADER_SOURCE_FILES}) - AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES}) - file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE) - list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE}) + AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES}) + file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE) + list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE}) endforeach() #message(${TARGET_NAME} ${AUTOSCRIBE_SHADER_SRC}) @@ -118,4 +133,4 @@ macro(AUTOSCRIBE_SHADER_LIB) # Link library shaders, if they exist include_directories("${SHADERS_DIR}") -endmacro() \ No newline at end of file +endmacro() diff --git a/cmake/macros/FixupInterface.cmake b/cmake/macros/FixupInterface.cmake index 3e5ea7a3e2..93b5cc25fb 100644 --- a/cmake/macros/FixupInterface.cmake +++ b/cmake/macros/FixupInterface.cmake @@ -16,26 +16,6 @@ macro(fixup_interface) string(REPLACE " " "\\ " ESCAPED_INSTALL_PATH ${INTERFACE_INSTALL_DIR}) set(_INTERFACE_INSTALL_PATH "${ESCAPED_INSTALL_PATH}/${ESCAPED_BUNDLE_NAME}.app") - # install QtWebProcess from Qt to the application bundle - # since it is missed by macdeployqt - # https://bugreports.qt.io/browse/QTBUG-35211 - set(LIBEXEC_PATH "${_INTERFACE_INSTALL_PATH}/Contents/libexec") - install( - PROGRAMS "${QT_DIR}/libexec/QtWebProcess" - DESTINATION ${LIBEXEC_PATH} - COMPONENT ${CLIENT_COMPONENT} - ) - - set(QTWEBPROCESS_PATH "\${CMAKE_INSTALL_PREFIX}/${LIBEXEC_PATH}") - - # we also need a qt.conf in the directory of QtWebProcess - install(CODE " - file(WRITE ${QTWEBPROCESS_PATH}/qt.conf - \"[Paths]\nPlugins = ../PlugIns\nImports = ../Resources/qml\nQml2Imports = ../Resources/qml\" - )" - COMPONENT ${CLIENT_COMPONENT} - ) - find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH) if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD)) @@ -49,7 +29,6 @@ macro(fixup_interface) execute_process(COMMAND ${MACDEPLOYQT_COMMAND}\ \${CMAKE_INSTALL_PREFIX}/${_INTERFACE_INSTALL_PATH}/\ -verbose=2 -qmldir=${CMAKE_SOURCE_DIR}/interface/resources/qml/\ - -executable=\${CMAKE_INSTALL_PREFIX}/${_INTERFACE_INSTALL_PATH}/Contents/libexec/QtWebProcess\ )" COMPONENT ${CLIENT_COMPONENT} ) diff --git a/cmake/macros/InstallBesideConsole.cmake b/cmake/macros/InstallBesideConsole.cmake index 0eb6025f38..d5777fff12 100644 --- a/cmake/macros/InstallBesideConsole.cmake +++ b/cmake/macros/InstallBesideConsole.cmake @@ -59,7 +59,12 @@ macro(install_beside_console) set(EXECUTABLE_NEEDING_FIXUP "\${CMAKE_INSTALL_PREFIX}/${COMPONENT_INSTALL_DIR}/${TARGET_NAME}") string(REPLACE " " "\\ " ESCAPED_EXECUTABLE_NAME ${EXECUTABLE_NEEDING_FIXUP}) + # configure Info.plist for COMPONENT_APP install(CODE " + set(MACOSX_BUNDLE_EXECUTABLE_NAME domain-server) + set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.server-components) + set(MACOSX_BUNDLE_BUNDLE_NAME Sandbox\\ Components) + configure_file(${HF_CMAKE_DIR}/templates/MacOSXBundleSandboxComponentsInfo.plist.in ${ESCAPED_BUNDLE_NAME}/Contents/Info.plist) execute_process(COMMAND ${MACDEPLOYQT_COMMAND} ${ESCAPED_BUNDLE_NAME} -verbose=2 -executable=${ESCAPED_EXECUTABLE_NAME})" COMPONENT ${SERVER_COMPONENT} ) diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index 050cea9fe1..7f826c1f8c 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -41,5 +41,15 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) POST_BUILD COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> $" ) + + set(QTAUDIO_PATH $/audio) + + # if present, replace qtaudio_windows.dll with qtaudio_wasapi.dll + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E remove ${QTAUDIO_PATH}/qtaudio_windows.dll && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.dll ${QTAUDIO_PATH} && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.pdb ${QTAUDIO_PATH} ) + ) + endif () endmacro() diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 76f6812921..531f9a1da2 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -18,6 +18,13 @@ macro(SET_PACKAGING_PARAMETERS) set(RELEASE_TYPE $ENV{RELEASE_TYPE}) set(RELEASE_NUMBER $ENV{RELEASE_NUMBER}) + string(TOLOWER "$ENV{BRANCH}" BUILD_BRANCH) + set(BUILD_GLOBAL_SERVICES "DEVELOPMENT") + set(USE_STABLE_GLOBAL_SERVICES FALSE) + + message(STATUS "The BUILD_BRANCH variable is: ${BUILD_BRANCH}") + message(STATUS "The BRANCH environment variable is: $ENV{BRANCH}") + message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}") if (RELEASE_TYPE STREQUAL "PRODUCTION") set(DEPLOY_PACKAGE TRUE) @@ -31,6 +38,14 @@ macro(SET_PACKAGING_PARAMETERS) # add definition for this release type add_definitions(-DPRODUCTION_BUILD) + # if the build is a PRODUCTION_BUILD from the "stable" branch + # then use the STABLE gobal services + if (BUILD_BRANCH STREQUAL "stable") + message(STATUS "The RELEASE_TYPE is PRODUCTION and the BUILD_BRANCH is stable...") + set(BUILD_GLOBAL_SERVICES "STABLE") + set(USE_STABLE_GLOBAL_SERVICES TRUE) + endif() + elseif (RELEASE_TYPE STREQUAL "PR") set(DEPLOY_PACKAGE TRUE) set(PR_BUILD 1) @@ -132,6 +147,10 @@ macro(SET_PACKAGING_PARAMETERS) set(CLIENT_COMPONENT client) set(SERVER_COMPONENT server) + # print out some results for testing this new build feature + message(STATUS "The BUILD_GLOBAL_SERVICES variable is: ${BUILD_GLOBAL_SERVICES}") + message(STATUS "The USE_STABLE_GLOBAL_SERVICES variable is: ${USE_STABLE_GLOBAL_SERVICES}") + # create a header file our targets can use to find out the application version file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/includes") configure_file("${HF_CMAKE_DIR}/templates/BuildInfo.h.in" "${CMAKE_BINARY_DIR}/includes/BuildInfo.h") diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index a10c7c11e6..e4a286cf3f 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -1,16 +1,16 @@ -# +# # SetupHifiLibrary.cmake -# +# # Copyright 2013 High Fidelity, Inc. # # Distributed under the Apache License, Version 2.0. # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# +# macro(SETUP_HIFI_LIBRARY) - + project(${TARGET_NAME}) - + # grab the implementation and header files file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c") list(APPEND ${TARGET_NAME}_SRCS ${LIB_SRCS}) @@ -34,19 +34,19 @@ macro(SETUP_HIFI_LIBRARY) set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS "-mavx2 -mfma") endif() endforeach() - + setup_memory_debugger() # create a library and set the property so it can be referenced later if (${${TARGET_NAME}_SHARED}) - add_library(${TARGET_NAME} SHARED ${LIB_SRCS} ${AUTOMTC_SRC} ${AUTOSCRIBE_SHADER_LIB_SRC} ${QT_RESOURCES_FILE}) + add_library(${TARGET_NAME} SHARED ${LIB_SRCS} ${AUTOSCRIBE_SHADER_LIB_SRC} ${QT_RESOURCES_FILE}) else () - add_library(${TARGET_NAME} ${LIB_SRCS} ${AUTOMTC_SRC} ${AUTOSCRIBE_SHADER_LIB_SRC} ${QT_RESOURCES_FILE}) + add_library(${TARGET_NAME} ${LIB_SRCS} ${AUTOSCRIBE_SHADER_LIB_SRC} ${QT_RESOURCES_FILE}) endif () - + set(${TARGET_NAME}_DEPENDENCY_QT_MODULES ${ARGN}) list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core) - + # find these Qt modules and link them to our own target find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED) @@ -59,7 +59,7 @@ macro(SETUP_HIFI_LIBRARY) set(QT_RESOURCES_FILE "") target_glm() - + set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Libraries") - -endmacro(SETUP_HIFI_LIBRARY) \ No newline at end of file + +endmacro(SETUP_HIFI_LIBRARY) diff --git a/cmake/macros/SetupHifiProject.cmake b/cmake/macros/SetupHifiProject.cmake index 6c150b6c8d..8695063556 100644 --- a/cmake/macros/SetupHifiProject.cmake +++ b/cmake/macros/SetupHifiProject.cmake @@ -22,7 +22,7 @@ macro(SETUP_HIFI_PROJECT) endif () endforeach() - add_executable(${TARGET_NAME} ${TARGET_SRCS} ${AUTOMTC_SRC} ${AUTOSCRIBE_SHADER_LIB_SRC}) + add_executable(${TARGET_NAME} ${TARGET_SRCS} ${AUTOSCRIBE_SHADER_LIB_SRC}) # include the generated application version header target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes") diff --git a/cmake/macros/TargetGli.cmake b/cmake/macros/TargetGli.cmake new file mode 100644 index 0000000000..664fcc00c5 --- /dev/null +++ b/cmake/macros/TargetGli.cmake @@ -0,0 +1,12 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Bradley Austin Davis on 2015/10/10 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_GLI) + add_dependency_external_projects(gli) + find_package(GLI REQUIRED) + target_include_directories(${TARGET_NAME} PUBLIC ${GLI_INCLUDE_DIRS}) +endmacro() \ No newline at end of file diff --git a/cmake/modules/FindGLI.cmake b/cmake/modules/FindGLI.cmake new file mode 100644 index 0000000000..0f42a9b052 --- /dev/null +++ b/cmake/modules/FindGLI.cmake @@ -0,0 +1,26 @@ +# +# FindGLI.cmake +# +# Try to find GLI include path. +# Once done this will define +# +# GLI_INCLUDE_DIRS +# +# Created on 2016/09/03 by Bradley Austin Davis +# Copyright 2013-2016 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# + +# setup hints for GLI search +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("gli") + +# locate header +find_path(GLI_INCLUDE_DIRS "gli/gli.hpp" HINTS ${GLI_SEARCH_DIRS}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GLI DEFAULT_MSG GLI_INCLUDE_DIRS) + +mark_as_advanced(GLI_INCLUDE_DIRS GLI_SEARCH_DIRS) \ No newline at end of file diff --git a/cmake/templates/BuildInfo.h.in b/cmake/templates/BuildInfo.h.in index 3a462feb1a..3d388a907f 100644 --- a/cmake/templates/BuildInfo.h.in +++ b/cmake/templates/BuildInfo.h.in @@ -9,7 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#define USE_STABLE_GLOBAL_SERVICES @USE_STABLE_GLOBAL_SERVICES@ + namespace BuildInfo { const QString MODIFIED_ORGANIZATION = "@BUILD_ORGANIZATION@"; const QString VERSION = "@BUILD_VERSION@"; + const QString BUILD_BRANCH = "@BUILD_BRANCH@"; + const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@"; } + diff --git a/cmake/templates/MacOSXBundleSandboxComponentsInfo.plist.in b/cmake/templates/MacOSXBundleSandboxComponentsInfo.plist.in new file mode 100644 index 0000000000..a466dc7c4e --- /dev/null +++ b/cmake/templates/MacOSXBundleSandboxComponentsInfo.plist.in @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + + diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 4786b12743..a80367cee1 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -130,6 +130,11 @@ Var AR_RegFlags SectionSetFlags ${${SecName}} $AR_SecFlags "default_${SecName}:" + ; The client is always selected by default + ${If} ${SecName} == @CLIENT_COMPONENT_NAME@ + SectionSetFlags ${${SecName}} 17 + ${EndIf} + !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected !macroend @@ -243,6 +248,12 @@ FunctionEnd ;-------------------------------- ; Installation types + +Section "-Previous Install Cleanup" + ; Remove the resources folder so we don't end up including removed QML files + RMDir /r "$INSTDIR\resources" +SectionEnd + @CPACK_NSIS_INSTALLATION_TYPES@ ;-------------------------------- @@ -589,6 +600,9 @@ Section "-Core installation" Delete "$INSTDIR\version" Delete "$INSTDIR\xinput1_3.dll" + ;Delete old Qt files + Delete "$INSTDIR\audio\qtaudio_windows.dll" + ; Delete old desktop shortcuts before they were renamed during Sandbox rename Delete "$DESKTOP\@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@.lnk" Delete "$DESKTOP\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk" diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index b7eb8c0138..746e599d4e 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -9,6 +9,11 @@ endif () # setup the project and link required Qt modules setup_hifi_project(Network) +# Fix up the rpath so macdeployqt works +if (APPLE) + set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") +endif () + # TODO: find a solution that will handle web file changes in resources on windows without a re-build. # Currently the resources are only copied on post-build. If one is changed but the domain-server is not, they will # not be re-copied. This is worked-around on OS X/UNIX by using a symlink. diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index c9d7ea77d5..8a7d03ba75 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -380,6 +380,14 @@ "default": "0", "advanced": false }, + { + "name": "maximum_user_capacity_redirect_location", + "label": "Redirect to Location on Maximum Capacity", + "help": "Is there another domain, you'd like to redirect clients to when the maximum number of avatars are connected.", + "placeholder": "", + "default": "", + "advanced": false + }, { "name": "standard_permissions", "type": "table", @@ -1025,65 +1033,41 @@ "name": "dynamic_jitter_buffer", "type": "checkbox", "label": "Dynamic Jitter Buffers", - "help": "Dynamically buffer client audio based on perceived jitter in packet receipt timing", + "help": "Dynamically buffer inbound audio streams based on perceived jitter in packet receipt timing.", "default": true, "advanced": true }, { "name": "static_desired_jitter_buffer_frames", "label": "Static Desired Jitter Buffer Frames", - "help": "If dynamic jitter buffers is disabled, this determines the target number of frames maintained by the AudioMixer's jitter buffers", + "help": "If dynamic jitter buffers is disabled, this determines the size of the jitter buffers of inbound audio streams in the mixer. Higher numbers introduce more latency.", "placeholder": "1", "default": "1", "advanced": true }, { - "name": "max_frames_over_desired", - "label": "Max Frames Over Desired", - "help": "The highest number of frames an AudioMixer's ringbuffer can exceed the desired jitter buffer frames by", - "placeholder": "10", - "default": "10", - "advanced": true + "name": "max_frames_over_desired", + "deprecated": true }, { - "name": "use_stdev_for_desired_calc", - "type": "checkbox", - "label": "Stdev for Desired Jitter Frames Calc", - "help": "Use Philip's method (stdev of timegaps) to calculate desired jitter frames (otherwise Fred's max timegap method is used)", - "default": false, - "advanced": true + "name": "window_starve_threshold", + "deprecated": true }, { - "name": "window_starve_threshold", - "label": "Window Starve Threshold", - "help": "If this many starves occur in an N-second window (N is the number in the next field), then the desired jitter frames will be re-evaluated using Window A.", - "placeholder": "3", - "default": "3", - "advanced": true + "name": "window_seconds_for_desired_calc_on_too_many_starves", + "deprecated": true }, { - "name": "window_seconds_for_desired_calc_on_too_many_starves", - "label": "Timegaps Window (A) Seconds", - "help": "Window A contains a history of timegaps. Its max timegap is used to re-evaluate the desired jitter frames when too many starves occur within it.", - "placeholder": "50", - "default": "50", - "advanced": true + "name": "window_seconds_for_desired_reduction", + "deprecated": true }, { - "name": "window_seconds_for_desired_reduction", - "label": "Timegaps Window (B) Seconds", - "help": "Window B contains a history of timegaps. Its max timegap is used as a ceiling for the desired jitter frames value.", - "placeholder": "10", - "default": "10", - "advanced": true + "name": "use_stdev_for_desired_calc", + "deprecated": true }, { - "name": "repetition_with_fade", - "type": "checkbox", - "label": "Repetition with Fade", - "help": "Dropped frames and mixing during starves repeat the last frame, eventually fading to silence", - "default": false, - "advanced": true + "name": "repetition_with_fade", + "deprecated": true } ] }, diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index ef967a47bf..ad426671a4 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -75,7 +75,7 @@ span.port { color: #666666; } -.advanced-setting { +.advanced-setting, .deprecated-setting { display: none; } diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 802038d806..7af21fe84b 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -40,7 +40,8 @@ + + + + + +
+

+ Marketplaces +

+
+

+ You can bring content into High Fidelity forom anywhere you want. Here are a few places that support direct import of content right now. If you'd like to suggest a Market to include here, let us know. +

+
+
+
+ +
+
+

This is the default High Fidelity marketplace. Viewing and downloading content from here is fully supported in Interface.

+
+
+
+
+
+
+
+ +
+
+

Clara.io has thousands of models available for importing into High Fidelity. Follow these steps for the best experience:

+
    +
  1. Create an account here or log in as an existing user.
  2. +
  3. Choose a model from the list and click Download -> Autodesk FBX.
  4. +
  5. After the file processes, click Download.
  6. +
  7. Add the model to your asset server, then find it from the list and choose Add To World.
  8. +
+
+ +
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 34368e475b..a255b7a494 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -11,6 +11,8 @@ var RAD_TO_DEG = 180 / Math.PI; var X_AXIS = {x: 1, y: 0, z: 0}; var Y_AXIS = {x: 0, y: 1, z: 0}; +var DEFAULT_DPI = 32; +var DEFAULT_WIDTH = 0.5; var TABLET_URL = "https://s3.amazonaws.com/hifi-public/tony/tablet.fbx"; @@ -37,12 +39,13 @@ function calcSpawnInfo() { } // ctor -WebTablet = function (url) { +WebTablet = function (url, width, dpi) { var ASPECT = 4.0 / 3.0; - var WIDTH = 0.4; + var WIDTH = width || DEFAULT_WIDTH; var HEIGHT = WIDTH * ASPECT; var DEPTH = 0.025; + var DPI = dpi || DEFAULT_DPI; var spawnInfo = calcSpawnInfo(); @@ -78,7 +81,7 @@ WebTablet = function (url) { position: webEntityPosition, rotation: webEntityRotation, shapeType: "box", - dpi: 45, + dpi: DPI, parentID: this.tabletEntityID, parentJointIndex: -1 }); diff --git a/scripts/system/libraries/controllers.js b/scripts/system/libraries/controllers.js new file mode 100644 index 0000000000..9bae46380e --- /dev/null +++ b/scripts/system/libraries/controllers.js @@ -0,0 +1,46 @@ +// handControllerGrab.js +// +// Created by Seth Alves on 2016-9-7 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/* global MyAvatar, Vec3, Controller, Quat */ + + +// var GRAB_POINT_SPHERE_OFFSET = { x: 0, y: 0.2, z: 0 }; +// var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.175, z: 0.04 }; + +// this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.32, z: 0.04 }; + +getGrabPointSphereOffset = function(handController) { + if (handController === Controller.Standard.RightHand) { + return GRAB_POINT_SPHERE_OFFSET; + } + return { + x: GRAB_POINT_SPHERE_OFFSET.x * -1, + y: GRAB_POINT_SPHERE_OFFSET.y, + z: GRAB_POINT_SPHERE_OFFSET.z + }; +}; + +// controllerWorldLocation is where the controller would be, in-world, with an added offset +getControllerWorldLocation = function (handController, doOffset) { + var orientation; + var position; + var pose = Controller.getPoseValue(handController); + if (pose.valid) { + orientation = Quat.multiply(MyAvatar.orientation, pose.rotation); + position = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position); + // add to the real position so the grab-point is out in front of the hand, a bit + if (doOffset) { + position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, getGrabPointSphereOffset(handController))); + } + } + return {position: position, + translation: position, + orientation: orientation, + rotation: orientation, + valid: pose.valid}; +}; diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 448293a2ac..3e9d3b5648 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -9,7 +9,7 @@ EntityListTool = function(opts) { }); - + var filterInView = false; var searchRadius = 100; var visible = false; @@ -48,20 +48,41 @@ EntityListTool = function(opts) { webView.emitScriptEvent(JSON.stringify(data)); }; + function valueIfDefined(value) { + return value !== undefined ? value : ""; + } + that.sendUpdate = function() { var entities = []; - var ids = Entities.findEntities(MyAvatar.position, searchRadius); + + var ids; + if (filterInView) { + ids = Entities.findEntitiesInFrustum(Camera.frustum); + } else { + ids = Entities.findEntities(MyAvatar.position, searchRadius); + } + + var cameraPosition = Camera.position; 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 : "", - locked: properties.locked, - visible: properties.visible - }); + + if (!filterInView || Vec3.distance(properties.position, cameraPosition) <= searchRadius) { + entities.push({ + id: id, + name: properties.name, + type: properties.type, + url: properties.type == "Model" ? properties.modelURL : "", + locked: properties.locked, + visible: properties.visible, + verticesCount: valueIfDefined(properties.renderInfo.verticesCount), + texturesCount: valueIfDefined(properties.renderInfo.texturesCount), + texturesSize: valueIfDefined(properties.renderInfo.texturesSize), + hasTransparent: valueIfDefined(properties.renderInfo.hasTransparent), + drawCalls: valueIfDefined(properties.renderInfo.drawCalls), + hasScript: properties.script !== "" + }); + } } var selectedIDs = []; @@ -105,9 +126,10 @@ EntityListTool = function(opts) { toggleSelectedEntitiesLocked(); } else if (data.type == "toggleVisible") { toggleSelectedEntitiesVisible(); + } else if (data.type === "filterInView") { + filterInView = data.filterInView === true; } else if (data.type === "radius") { searchRadius = data.radius; - that.sendUpdate(); } }); diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 461204b7aa..9d98a19305 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1022,6 +1022,9 @@ SelectionDisplay = (function() { // No switching while the other is already triggered, so no need to release. activeHand = (activeHand === Controller.Standard.RightHand) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; } + if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(Reticle.position)) { + return; + } var eventResult = that.mousePressEvent({}); if (!eventResult || (eventResult === 'selectionBox')) { var pickRay = controllerComputePickRay(); diff --git a/scripts/system/libraries/toolBars.js b/scripts/system/libraries/toolBars.js index 5a84bf9027..e49f8c4004 100644 --- a/scripts/system/libraries/toolBars.js +++ b/scripts/system/libraries/toolBars.js @@ -130,8 +130,6 @@ Tool = function(properties, selectable, selected) { // selectable and selected a if (update) { if (selectable) { this.toggle(); - } else if (properties.showButtonDown) { - this.buttonDown(true); } } return true; diff --git a/scripts/system/marketplaces/clara.js b/scripts/system/marketplaces/clara.js index 67c2d5503c..a04400497f 100644 --- a/scripts/system/marketplaces/clara.js +++ b/scripts/system/marketplaces/clara.js @@ -14,15 +14,15 @@ (function() { // BEGIN LOCAL_SCOPE var toolIconUrl = Script.resolvePath("../assets/images/tools/"); -var qml = Script.resolvePath("../../../resources/qml/MarketplaceComboBox.qml") +var qml = Script.resolvePath("../../../resources/qml/Marketplaces.qml") var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; var marketplaceWindow = new OverlayWindow({ title: "Marketplace", source: qml, - width: 900, - height: 700, + width: 1000, + height: 900, toolWindow: false, visible: false, }); diff --git a/scripts/system/mod.js b/scripts/system/mod.js index b2c9785539..1a7b3b401e 100644 --- a/scripts/system/mod.js +++ b/scripts/system/mod.js @@ -10,9 +10,13 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* global Toolbars, Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation */ + (function() { // BEGIN LOCAL_SCOPE +Script.include("/~/system/libraries/controllers.js"); + // grab the toolbar var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); @@ -144,7 +148,7 @@ AvatarList.avatarRemovedEvent.connect(function(avatarID){ function handleSelectedOverlay(clickedOverlay) { // see this is one of our mod overlays - var modOverlayKeys = Object.keys(modOverlays) + var modOverlayKeys = Object.keys(modOverlays); for (var i = 0; i < modOverlayKeys.length; ++i) { var avatarID = modOverlayKeys[i]; var modOverlay = modOverlays[avatarID]; @@ -187,13 +191,9 @@ Controller.mousePressEvent.connect(function(event){ var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); function controllerComputePickRay(hand) { - var controllerPose = Controller.getPoseValue(hand); + var controllerPose = getControllerWorldLocation(hand, true); if (controllerPose.valid) { - var controllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, controllerPose.translation), - MyAvatar.position); - // This gets point direction right, but if you want general quaternion it would be more complicated: - var controllerDirection = Quat.getUp(Quat.multiply(MyAvatar.orientation, controllerPose.rotation)); - return { origin: controllerPosition, direction: controllerDirection }; + return { origin: controllerPose.position, direction: controllerPose.orientation }; } } diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index f41b0502c8..f3ba466342 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -58,6 +58,8 @@ // } // } +/* global Script, Controller, Overlays, SoundArray, Quat, Vec3, MyAvatar, Menu, HMD, AudioDevice, LODManager, Settings, Camera */ + (function() { // BEGIN LOCAL_SCOPE Script.include("./libraries/soundArray.js"); @@ -76,11 +78,9 @@ var fontSize = 12.0; var PERSIST_TIME_2D = 10.0; // Time in seconds before notification fades var PERSIST_TIME_3D = 15.0; var persistTime = PERSIST_TIME_2D; -var clickedText = false; var frame = 0; var ourWidth = Window.innerWidth; var ourHeight = Window.innerHeight; -var text = "placeholder"; var ctrlIsPressed = false; var ready = true; var MENU_NAME = 'Tools > Notifications'; @@ -97,12 +97,14 @@ var NotificationType = { WINDOW_RESIZE: 3, LOD_WARNING: 4, CONNECTION_REFUSED: 5, + EDIT_ERROR: 6, properties: [ { text: "Mute Toggle" }, { text: "Snapshot" }, { text: "Window Resize" }, { text: "Level of Detail" }, - { text: "Connection Refused" } + { text: "Connection Refused" }, + { text: "Edit error" } ], getTypeFromMenuItem: function(menuItemName) { if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) { @@ -253,6 +255,9 @@ function notify(notice, button, height, imageProperties, image) { positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y); + notice.parentID = MyAvatar.sessionUUID; + notice.parentJointIndex = -2; + if (!image) { notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE; notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE; @@ -270,6 +275,8 @@ function notify(notice, button, height, imageProperties, image) { button.url = button.imageURL; button.scale = button.width * NOTIFICATION_3D_SCALE; button.isFacingAvatar = false; + button.parentID = MyAvatar.sessionUUID; + button.parentJointIndex = -2; buttons.push((Overlays.addOverlay("image3d", button))); overlay3DDetails.push({ @@ -279,6 +286,34 @@ function notify(notice, button, height, imageProperties, image) { width: noticeWidth, height: noticeHeight }); + + + var defaultEyePosition, + avatarOrientation, + notificationPosition, + notificationOrientation, + buttonPosition; + + if (isOnHMD && notifications.length > 0) { + // Update 3D overlays to maintain positions relative to avatar + defaultEyePosition = MyAvatar.getDefaultEyePosition(); + avatarOrientation = MyAvatar.orientation; + + for (i = 0; i < notifications.length; i += 1) { + notificationPosition = Vec3.sum(defaultEyePosition, + Vec3.multiplyQbyV(avatarOrientation, + overlay3DDetails[i].notificationPosition)); + notificationOrientation = Quat.multiply(avatarOrientation, + overlay3DDetails[i].notificationOrientation); + buttonPosition = Vec3.sum(defaultEyePosition, + Vec3.multiplyQbyV(avatarOrientation, + overlay3DDetails[i].buttonPosition)); + Overlays.editOverlay(notifications[i], { position: notificationPosition, + rotation: notificationOrientation }); + Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: notificationOrientation }); + } + } + } else { if (!image) { notificationText = Overlays.addOverlay("text", notice); @@ -429,11 +464,6 @@ function update() { noticeOut, buttonOut, arraysOut, - defaultEyePosition, - avatarOrientation, - notificationPosition, - notificationOrientation, - buttonPosition, positions, i, j, @@ -457,7 +487,8 @@ function update() { Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); if (isOnHMD) { - positions = calculate3DOverlayPositions(overlay3DDetails[i].width, overlay3DDetails[i].height, locationY); + positions = calculate3DOverlayPositions(overlay3DDetails[i].width, + overlay3DDetails[i].height, locationY); overlay3DDetails[i].notificationOrientation = positions.notificationOrientation; overlay3DDetails[i].notificationPosition = positions.notificationPosition; overlay3DDetails[i].buttonPosition = positions.buttonPosition; @@ -480,22 +511,6 @@ function update() { } } } - - if (isOnHMD && notifications.length > 0) { - // Update 3D overlays to maintain positions relative to avatar - defaultEyePosition = MyAvatar.getDefaultEyePosition(); - avatarOrientation = MyAvatar.orientation; - - for (i = 0; i < notifications.length; i += 1) { - notificationPosition = Vec3.sum(defaultEyePosition, - Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].notificationPosition)); - notificationOrientation = Quat.multiply(avatarOrientation, overlay3DDetails[i].notificationOrientation); - buttonPosition = Vec3.sum(defaultEyePosition, - Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].buttonPosition)); - Overlays.editOverlay(notifications[i], { position: notificationPosition, rotation: notificationOrientation }); - Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: notificationOrientation }); - } - } } var STARTUP_TIMEOUT = 500, // ms @@ -532,12 +547,17 @@ function onDomainConnectionRefused(reason) { createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED); } +function onEditError(msg) { + createNotification(wordWrap(msg), NotificationType.EDIT_ERROR); +} + + function onSnapshotTaken(path, notify) { if (notify) { var imageProperties = { path: "file:///" + path, aspectRatio: Window.innerWidth / Window.innerHeight - } + }; createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties); } } @@ -571,8 +591,6 @@ function keyReleaseEvent(key) { // Triggers notification on specific key driven events function keyPressEvent(key) { - var noteString; - if (key.key === 16777249) { ctrlIsPressed = true; } @@ -622,13 +640,13 @@ function menuItemEvent(menuItem) { } LODManager.LODDecreased.connect(function() { - var warningText = "\n" - + "Due to the complexity of the content, the \n" - + "level of detail has been decreased. " - + "You can now see: \n" - + LODManager.getLODFeedbackText(); + var warningText = "\n" + + "Due to the complexity of the content, the \n" + + "level of detail has been decreased. " + + "You can now see: \n" + + LODManager.getLODFeedbackText(); - if (lodTextID == false) { + if (lodTextID === false) { lodTextID = createNotification(warningText, NotificationType.LOD_WARNING); } else { Overlays.editOverlay(lodTextID, { text: warningText }); @@ -644,6 +662,7 @@ Script.scriptEnding.connect(scriptEnding); Menu.menuItemEvent.connect(menuItemEvent); Window.domainConnectionRefused.connect(onDomainConnectionRefused); Window.snapshotTaken.connect(onSnapshotTaken); +Window.notifyEditError = onEditError; setup(); diff --git a/scripts/system/particle_explorer/particleExplorer.html b/scripts/system/particle_explorer/particleExplorer.html index 1de176214c..260d8a7813 100644 --- a/scripts/system/particle_explorer/particleExplorer.html +++ b/scripts/system/particle_explorer/particleExplorer.html @@ -17,7 +17,7 @@ - + + + + +
+

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

+

To restore it, follow these steps: + +

+
+ +
+ + + +
+ Step 2 +

2. Go to your backup directory: + +

+ +
+ Step 1 +

1. Stop your Sandbox server. +

+ +
+ Step 3 +

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

+ +
+ Step 4 +

4. Restart your Sandbox server. +

+ +
+ +
+ +
+ + +
+ + diff --git a/server-console/src/content-update.js b/server-console/src/content-update.js new file mode 100644 index 0000000000..c77cfc92c6 --- /dev/null +++ b/server-console/src/content-update.js @@ -0,0 +1,12 @@ +function ready() { + console.log("Ready"); + + const electron = require('electron'); + window.$ = require('./vendor/jquery/jquery-2.1.4.min.js'); + + electron.ipcRenderer.on('update', function(event, message) { + $('#directory').html(message); + }); + + electron.ipcRenderer.send('ready'); +} diff --git a/server-console/src/images/step1.jpg b/server-console/src/images/step1.jpg new file mode 100644 index 0000000000..cd80ae5537 Binary files /dev/null and b/server-console/src/images/step1.jpg differ diff --git a/server-console/src/images/step2.jpg b/server-console/src/images/step2.jpg new file mode 100644 index 0000000000..6bc285ac47 Binary files /dev/null and b/server-console/src/images/step2.jpg differ diff --git a/server-console/src/images/step3.jpg b/server-console/src/images/step3.jpg new file mode 100644 index 0000000000..a819dd2cdb Binary files /dev/null and b/server-console/src/images/step3.jpg differ diff --git a/server-console/src/images/step4.jpg b/server-console/src/images/step4.jpg new file mode 100644 index 0000000000..27694aee74 Binary files /dev/null and b/server-console/src/images/step4.jpg differ diff --git a/server-console/src/main.js b/server-console/src/main.js index c47308aed6..e297ca3276 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,6 +42,8 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; +const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home.tgz"; + function getBuildInfo() { var buildInfoPath = null; @@ -406,6 +408,13 @@ var labels = { logWindow.open(); } }, + restoreBackup: { + label: 'Restore Backup Instructions', + click: function() { + var folder = getRootHifiDataDirectory() + "/Server Backup"; + openBackupInstructions(folder); + } + }, share: { label: 'Share', click: function() { @@ -441,6 +450,7 @@ function buildMenuArray(serverState) { menuArray.push(labels.stopServer); menuArray.push(labels.settings); menuArray.push(labels.viewLogs); + menuArray.push(labels.restoreBackup); menuArray.push(separator); menuArray.push(labels.share); menuArray.push(separator); @@ -486,6 +496,100 @@ function updateTrayMenu(serverState) { const httpStatusPort = 60332; +function backupResourceDirectories(folder) { + try { + fs.mkdirSync(folder); + console.log("Created directory " + folder); + + var dsBackup = path.join(folder, '/domain-server'); + var acBackup = path.join(folder, '/assignment-client'); + + fs.copySync(getDomainServerClientResourcesDirectory(), dsBackup); + fs.copySync(getAssignmentClientResourcesDirectory(), acBackup); + + fs.removeSync(getDomainServerClientResourcesDirectory()); + fs.removeSync(getAssignmentClientResourcesDirectory()); + + return true; + } catch (e) { + console.log(e); + return false; + } +} + +function openBackupInstructions(folder) { + // Explain user how to restore server + var window = new BrowserWindow({ + icon: appIcon, + width: 800, + height: 520, + }); + window.loadURL('file://' + __dirname + '/content-update.html'); + if (!debug) { + window.setMenu(null); + } + window.show(); + + electron.ipcMain.on('ready', function() { + console.log("got ready"); + window.webContents.send('update', folder); + }); +} +function backupResourceDirectoriesAndRestart() { + homeServer.stop(); + + var folder = getRootHifiDataDirectory() + "/Server Backup - " + Date.now(); + if (backupResourceDirectories(folder)) { + maybeInstallDefaultContentSet(onContentLoaded); + openBackupInstructions(folder); + } else { + dialog.showMessageBox({ + type: 'warning', + buttons: ['Ok'], + title: 'Update Error', + message: 'There was an error updating the content, aborting.' + }, function() {}); + } +} + +function checkNewContent() { + // Start downloading content set + var req = request.head({ + url: HOME_CONTENT_URL + }, function (error, response, body) { + if (error === null) { + var localContent = Date.parse(userConfig.get('homeContentLastModified')); + var remoteContent = Date.parse(response.headers['last-modified']); + + var shouldUpdate = isNaN(localContent) || (!isNaN(remoteContent) && (remoteContent > localContent)); + + var wantDebug = false; + if (wantDebug) { + console.log('Last Modified: ' + response.headers['last-modified']); + console.log(localContent + " " + remoteContent + " " + shouldUpdate + " " + new Date()); + console.log("Remote content is " + (shouldUpdate ? "newer" : "older") + " that local content."); + } + + if (shouldUpdate) { + dialog.showMessageBox({ + type: 'question', + buttons: ['Yes', 'No'], + title: 'New home content', + message: 'A newer version of the home content set is available.\nDo you wish to update?' + }, function(idx) { + if (idx === 0) { + backupResourceDirectoriesAndRestart(); + } else { + // They don't want to update, mark content set as current + userConfig.set('homeContentLastModified', new Date()); + } + }); + } + } + }); +} + + function maybeInstallDefaultContentSet(onComplete) { // Check for existing data const acResourceDirectory = getAssignmentClientResourcesDirectory(); @@ -517,6 +621,8 @@ function maybeInstallDefaultContentSet(onComplete) { if (userHasExistingACData || userHasExistingDSData) { console.log("User has existing data, suppressing downloader"); onComplete(); + + checkNewContent(); return; } @@ -528,6 +634,7 @@ function maybeInstallDefaultContentSet(onComplete) { return console.error(err) } console.log('Copied home content over to: ' + getRootHifiDataDirectory()); + userConfig.set('homeContentLastModified', new Date()); onComplete(); }); return; @@ -566,7 +673,7 @@ function maybeInstallDefaultContentSet(onComplete) { // Start downloading content set var req = progress(request.get({ - url: "http://cachefly.highfidelity.com/home.tgz" + url: HOME_CONTENT_URL }, function(error, responseMessage, responseData) { if (aborted) { return; @@ -607,6 +714,7 @@ function maybeInstallDefaultContentSet(onComplete) { req.pipe(gunzip).pipe(tar.extract(getRootHifiDataDirectory())).on('error', extractError).on('finish', function(){ // response and decompression complete, return console.log("Finished unarchiving home content set"); + userConfig.set('homeContentLastModified', new Date()); sendStateUpdate('complete'); }); @@ -663,6 +771,62 @@ for (var key in trayIcons) { const notificationIcon = path.join(__dirname, '../resources/console-notification.png'); +function onContentLoaded() { + maybeShowSplash(); + + if (buildInfo.releaseType == 'PRODUCTION') { + var currentVersion = null; + try { + currentVersion = parseInt(buildInfo.buildIdentifier); + } catch (e) { + } + + if (currentVersion !== null) { + const CHECK_FOR_UPDATES_INTERVAL_SECONDS = 60 * 30; + var hasShownUpdateNotification = false; + const updateChecker = new updater.UpdateChecker(currentVersion, CHECK_FOR_UPDATES_INTERVAL_SECONDS); + updateChecker.on('update-available', function(latestVersion, url) { + if (!hasShownUpdateNotification) { + notifier.notify({ + icon: notificationIcon, + title: 'An update is available!', + message: 'High Fidelity version ' + latestVersion + ' is available', + wait: true, + url: url + }); + hasShownUpdateNotification = true; + } + }); + notifier.on('click', function(notifierObject, options) { + console.log("Got click", options.url); + shell.openExternal(options.url); + }); + } + } + + deleteOldFiles(logPath, DELETE_LOG_FILES_OLDER_THAN_X_SECONDS, LOG_FILE_REGEX); + + if (dsPath && acPath) { + domainServer = new Process('domain-server', dsPath, ["--get-temp-name"], logPath); + acMonitor = new ACMonitorProcess('ac-monitor', acPath, ['-n6', + '--log-directory', logPath, + '--http-status-port', httpStatusPort], httpStatusPort, logPath); + homeServer = new ProcessGroup('home', [domainServer, acMonitor]); + logWindow = new LogWindow(acMonitor, domainServer); + + var processes = { + home: homeServer + }; + + // handle process updates + homeServer.on('state-update', function(processGroup) { updateTrayMenu(processGroup.state); }); + + // start the home server + homeServer.start(); + } +} + + // This method will be called when Electron has finished // initialization and is ready to create browser windows. app.on('ready', function() { @@ -682,58 +846,5 @@ app.on('ready', function() { updateTrayMenu(ProcessGroupStates.STOPPED); - maybeInstallDefaultContentSet(function() { - maybeShowSplash(); - - if (buildInfo.releaseType == 'PRODUCTION') { - var currentVersion = null; - try { - currentVersion = parseInt(buildInfo.buildIdentifier); - } catch (e) { - } - - if (currentVersion !== null) { - const CHECK_FOR_UPDATES_INTERVAL_SECONDS = 60 * 30; - var hasShownUpdateNotification = false; - const updateChecker = new updater.UpdateChecker(currentVersion, CHECK_FOR_UPDATES_INTERVAL_SECONDS); - updateChecker.on('update-available', function(latestVersion, url) { - if (!hasShownUpdateNotification) { - notifier.notify({ - icon: notificationIcon, - title: 'An update is available!', - message: 'High Fidelity version ' + latestVersion + ' is available', - wait: true, - url: url - }); - hasShownUpdateNotification = true; - } - }); - notifier.on('click', function(notifierObject, options) { - console.log("Got click", options.url); - shell.openExternal(options.url); - }); - } - } - - deleteOldFiles(logPath, DELETE_LOG_FILES_OLDER_THAN_X_SECONDS, LOG_FILE_REGEX); - - if (dsPath && acPath) { - domainServer = new Process('domain-server', dsPath, ["--get-temp-name"], logPath); - acMonitor = new ACMonitorProcess('ac-monitor', acPath, ['-n6', - '--log-directory', logPath, - '--http-status-port', httpStatusPort], httpStatusPort, logPath); - homeServer = new ProcessGroup('home', [domainServer, acMonitor]); - logWindow = new LogWindow(acMonitor, domainServer); - - var processes = { - home: homeServer - }; - - // handle process updates - homeServer.on('state-update', function(processGroup) { updateTrayMenu(processGroup.state); }); - - // start the home server - homeServer.start(); - } - }); + maybeInstallDefaultContentSet(onContentLoaded); }); diff --git a/tests/gpu-test/src/TestFbx.cpp b/tests/gpu-test/src/TestFbx.cpp index cea356e125..538bb0a973 100644 --- a/tests/gpu-test/src/TestFbx.cpp +++ b/tests/gpu-test/src/TestFbx.cpp @@ -56,6 +56,7 @@ public: explicit FileDownloader(QUrl imageUrl, QObject *parent = 0) : QObject(parent) { connect(&m_WebCtrl, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*))); QNetworkRequest request(imageUrl); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); m_WebCtrl.get(request); } diff --git a/tests/render-perf/src/Camera.hpp b/tests/render-perf/src/Camera.hpp index ebfcfb9e32..6e1d95bfe1 100644 --- a/tests/render-perf/src/Camera.hpp +++ b/tests/render-perf/src/Camera.hpp @@ -14,9 +14,10 @@ protected: public: glm::quat getOrientation() const { - return glm::angleAxis(yaw, Vectors::UP); + return glm::angleAxis(yawPitch.x, Vectors::UP) * glm::angleAxis(yawPitch.y, Vectors::RIGHT); } - float yaw { 0 }; + + vec2 yawPitch { 0 }; glm::vec3 position; float rotationSpeed { 1.0f }; float movementSpeed { 1.0f }; @@ -76,7 +77,12 @@ public: }; void rotate(const float delta) { - yaw += delta; + yawPitch.x += delta; + updateViewMatrix(); + } + + void rotate(const glm::vec2& delta) { + yawPitch += delta; updateViewMatrix(); } @@ -84,7 +90,11 @@ public: glm::vec3 f = rotation * Vectors::UNIT_NEG_Z; f.y = 0; f = glm::normalize(f); - yaw = angleBetween(Vectors::UNIT_NEG_Z, f); + yawPitch.x = angleBetween(Vectors::UNIT_NEG_Z, f); + f = rotation * Vectors::UNIT_NEG_Z; + f.x = 0; + f = glm::normalize(f); + yawPitch.y = angleBetween(Vectors::UNIT_NEG_Z, f); updateViewMatrix(); } diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index c6cca74c69..987fbe33d5 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -32,12 +33,12 @@ #include +#include +#include +#include +#include #include -//#include -//#include -//#include - #include #include #include @@ -99,56 +100,6 @@ public: } }; -#if 0 -class GlfwCamera : public Camera { - Key forKey(int key) { - switch (key) { - case GLFW_KEY_W: return FORWARD; - case GLFW_KEY_S: return BACK; - case GLFW_KEY_A: return LEFT; - case GLFW_KEY_D: return RIGHT; - case GLFW_KEY_E: return UP; - case GLFW_KEY_C: return DOWN; - case GLFW_MOUSE_BUTTON_LEFT: return MLEFT; - case GLFW_MOUSE_BUTTON_RIGHT: return MRIGHT; - case GLFW_MOUSE_BUTTON_MIDDLE: return MMIDDLE; - default: break; - } - return INVALID; - } - - vec2 _lastMouse; -public: - void keyHandler(int key, int scancode, int action, int mods) { - Key k = forKey(key); - if (k == INVALID) { - return; - } - if (action == GLFW_PRESS) { - keys.set(k); - } else if (action == GLFW_RELEASE) { - keys.reset(k); - } - } - - //static void MouseMoveHandler(GLFWwindow* window, double posx, double posy); - //static void MouseScrollHandler(GLFWwindow* window, double xoffset, double yoffset); - void onMouseMove(double posx, double posy) { - vec2 mouse = vec2(posx, posy); - vec2 delta = mouse - _lastMouse; - if (keys.at(Key::MRIGHT)) { - dolly(delta.y * 0.01f); - } else if (keys.at(Key::MLEFT)) { - rotate(delta.x * -0.01f); - } else if (keys.at(Key::MMIDDLE)) { - delta.y *= -1.0f; - translate(delta * -0.01f); - } - _lastMouse = mouse; - } - -}; -#else class QWindowCamera : public Camera { Key forKey(int key) { switch (key) { @@ -188,7 +139,8 @@ public: if (buttons & Qt::RightButton) { dolly(delta.y * 0.01f); } else if (buttons & Qt::LeftButton) { - rotate(delta.x * -0.01f); + //rotate(delta.x * -0.01f); + rotate(delta * -0.01f); } else if (buttons & Qt::MiddleButton) { delta.y *= -1.0f; translate(delta * -0.01f); @@ -197,7 +149,6 @@ public: _lastMouse = mouse; } }; -#endif static QString toHumanSize(size_t size, size_t maxUnit = std::numeric_limits::max()) { static const std::vector SUFFIXES{ { "B", "KB", "MB", "GB", "TB", "PB" } }; @@ -240,7 +191,7 @@ public: std::mutex _mutex; std::shared_ptr _backend; std::vector _frameTimes; - size_t _frameIndex; + size_t _frameIndex { 0 }; std::mutex _frameLock; std::queue _pendingFrames; gpu::FramePointer _activeFrame; @@ -252,7 +203,6 @@ public: _pendingFrames.push(frame); } - void initialize(QWindow* window, gl::Context& initContext) { setObjectName("RenderThread"); _context.setWindow(window); @@ -286,10 +236,6 @@ public: } _context.makeCurrent(); - glewExperimental = true; - glewInit(); - glGetError(); - _frameTimes.resize(FRAME_TIME_BUFFER_SIZE, 0); { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); @@ -375,7 +321,6 @@ public: } } - bool process() override { std::queue pendingFrames; { @@ -420,6 +365,7 @@ public: }; render::ItemID BackgroundRenderData::_item = 0; +QSharedPointer logger; namespace render { template <> const ItemKey payloadGetKey(const BackgroundRenderData::Pointer& stuff) { @@ -497,13 +443,17 @@ protected: _postUpdateLambdas[key] = func; } + bool isHMDMode() const override { + return false; + } + public: //"/-17.2049,-8.08629,-19.4153/0,0.881994,0,-0.47126" static void setup() { DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); DependencyManager::set(); - DependencyManager::set(NodeType::Agent, 0); + DependencyManager::set(NodeType::Agent); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -548,36 +498,6 @@ public: _renderThread.initialize(this, _initContext); _initContext.makeCurrent(); -#if 0 - glfwInit(); - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - resizeWindow(QSize(800, 600)); - _window = glfwCreateWindow(_size.width(), _size.height(), "Window Title", NULL, NULL); - if (!_window) { - throw std::runtime_error("Could not create window"); - } - - glfwSetWindowUserPointer(_window, this); - glfwSetKeyCallback(_window, KeyboardHandler); - glfwSetMouseButtonCallback(_window, MouseHandler); - glfwSetCursorPosCallback(_window, MouseMoveHandler); - glfwSetWindowCloseCallback(_window, CloseHandler); - glfwSetFramebufferSizeCallback(_window, FramebufferSizeHandler); - glfwSetScrollCallback(_window, MouseScrollHandler); - - - glfwMakeContextCurrent(_window); - GLDebug::setupLogger(this); -#endif - -#ifdef Q_OS_WIN - //wglSwapIntervalEXT(0); -#endif - // FIXME use a wait condition QThread::msleep(1000); _renderThread.submitFrame(gpu::FramePointer()); @@ -618,6 +538,16 @@ public: DependencyManager::destroy(); } + void loadCommands(const QString& filename) { + QFileInfo fileInfo(filename); + if (!fileInfo.exists()) { + return; + } + _commandPath = fileInfo.absolutePath(); + _commands = FileUtils::readLines(filename); + _commandIndex = 0; + } + protected: bool eventFilter(QObject *obj, QEvent *event) override { @@ -662,6 +592,14 @@ protected: toggleCulling(); return; + case Qt::Key_Home: + gpu::Texture::setAllowedGPUMemoryUsage(0); + return; + + case Qt::Key_End: + gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(256)); + return; + default: break; @@ -772,10 +710,11 @@ private: }; void updateText() { - QString title = QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4") + QString title = QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4 Max GPU %5") .arg(_fps).arg(_cullingEnabled) .arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2)) - .arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2)); + .arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2)) + .arg(toHumanSize(gpu::Texture::getAllowedGPUMemoryUsage(), 2)); setTitle(title); #if 0 { @@ -799,10 +738,77 @@ private: #endif } + void runCommand(const QString& command) { + qDebug() << "Running command: " << command; + QStringList commandParams = command.split(QRegularExpression(QString("\\s"))); + QString verb = commandParams[0].toLower(); + if (verb == "loop") { + if (commandParams.length() > 1) { + int maxLoops = commandParams[1].toInt(); + if (maxLoops < ++_commandLoops) { + qDebug() << "Exceeded loop count"; + return; + } + } + _commandIndex = 0; + } else if (verb == "wait") { + if (commandParams.length() < 2) { + qDebug() << "No wait time specified"; + return; + } + int seconds = commandParams[1].toInt(); + _nextCommandTime = usecTimestampNow() + seconds * USECS_PER_SECOND; + } else if (verb == "load") { + if (commandParams.length() < 2) { + qDebug() << "No load file specified"; + return; + } + QString file = commandParams[1]; + if (QFileInfo(file).isRelative()) { + file = _commandPath + "/" + file; + } + if (!QFileInfo(file).exists()) { + qDebug() << "Cannot find scene file " + file; + return; + } + + importScene(file); + } else if (verb == "go") { + if (commandParams.length() < 2) { + qDebug() << "No destination specified for go command"; + return; + } + parsePath(commandParams[1]); + } else { + qDebug() << "Unknown command " << command; + } + } + + void runNextCommand(quint64 now) { + if (_commands.empty()) { + return; + } + + if (_commandIndex >= _commands.size()) { + _commands.clear(); + return; + } + + if (now < _nextCommandTime) { + return; + } + + _nextCommandTime = 0; + QString command = _commands[_commandIndex++]; + runCommand(command); + } + void update() { auto now = usecTimestampNow(); static auto last = now; + runNextCommand(now); + float delta = now - last; // Update the camera _camera.update(delta / USECS_PER_SECOND); @@ -955,7 +961,6 @@ private: QString atpUrl = QUrl::fromLocalFile(atpPath).toString(); ResourceManager::setUrlPrefixOverride("atp:/", atpUrl + "/"); } - _settings.setValue(LAST_SCENE_KEY, fileName); _octree->clear(); _octree->getTree()->readFromURL(fileName); } @@ -974,6 +979,7 @@ private: if (fileName.isNull()) { return; } + _settings.setValue(LAST_SCENE_KEY, fileName); importScene(fileName); } @@ -1011,7 +1017,7 @@ private: } void resetPosition() { - _camera.yaw = 0; + _camera.yawPitch = vec3(0); _camera.setPosition(vec3()); } @@ -1073,6 +1079,13 @@ private: model::SunSkyStage _sunSkyStage; model::LightPointer _globalLight { std::make_shared() }; bool _ready { false }; + + QStringList _commands; + QString _commandPath; + int _commandLoops { 0 }; + int _commandIndex { -1 }; + uint64_t _nextCommandTime { 0 }; + //TextOverlay* _textOverlay; static bool _cullingEnabled; @@ -1089,12 +1102,14 @@ private: bool QTestWindow::_cullingEnabled = true; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - if (!message.isEmpty()) { + QString logMessage = LogHandler::getInstance().printMessage((LogMsgType)type, context, message); + + if (!logMessage.isEmpty()) { #ifdef Q_OS_WIN - OutputDebugStringA(message.toLocal8Bit().constData()); + OutputDebugStringA(logMessage.toLocal8Bit().constData()); OutputDebugStringA("\n"); #endif - std::cout << message.toLocal8Bit().constData() << std::endl; + logger->addMessage(qPrintable(logMessage + "\n")); } } @@ -1102,16 +1117,19 @@ const char * LOG_FILTER_RULES = R"V0G0N( hifi.gpu=true )V0G0N"; + int main(int argc, char** argv) { QApplication app(argc, argv); QCoreApplication::setApplicationName("RenderPerf"); QCoreApplication::setOrganizationName("High Fidelity"); QCoreApplication::setOrganizationDomain("highfidelity.com"); + logger.reset(new FileLogger()); qInstallMessageHandler(messageHandler); QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow::setup(); QTestWindow window; + //window.loadCommands("C:/Users/bdavis/Git/dreaming/exports/commands.txt"); app.exec(); return 0; } diff --git a/tests/render-texture-load/CMakeLists.txt b/tests/render-texture-load/CMakeLists.txt new file mode 100644 index 0000000000..0e5f32f46a --- /dev/null +++ b/tests/render-texture-load/CMakeLists.txt @@ -0,0 +1,31 @@ + +set(TARGET_NAME render-texture-load) + +if (WIN32) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217") +endif() + +# This is not a testcase -- just set it up as a regular hifi project +setup_hifi_project(Quick Gui OpenGL) +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") + +# link in the shared libraries +link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics) + +package_libraries_for_deployment() + +target_gli() +target_glm() + +target_zlib() +add_dependency_external_projects(quazip) +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}) +endif () + + +target_bullet() diff --git a/tests/render-texture-load/src/GLIHelpers.cpp b/tests/render-texture-load/src/GLIHelpers.cpp new file mode 100644 index 0000000000..8067993b49 --- /dev/null +++ b/tests/render-texture-load/src/GLIHelpers.cpp @@ -0,0 +1,70 @@ +// +// Created by Bradley Austin Davis on 2016/09/03 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLIHelpers.h" + +#include +#include + +#include +#include +#include +#include + +gli::format fromQImageFormat(QImage::Format format) { + switch (format) { + case QImage::Format_RGB32: + return gli::format::FORMAT_BGRA8_UNORM_PACK8; + + case QImage::Format_ARGB32: + return gli::format::FORMAT_BGRA8_UNORM_PACK8; + + case QImage::Format_Grayscale8: + return gli::format::FORMAT_L8_UNORM_PACK8; + + default: + return gli::format::FORMAT_UNDEFINED; + } +} + +QString getKtxFileName(const QString& sourceFileName) { + QFileInfo fileInfo(sourceFileName); + QString name = fileInfo.completeBaseName(); + QString ext = fileInfo.suffix(); + QString path = fileInfo.absolutePath(); + return path + "/" + name + ".ktx"; +} + +QString convertTexture(const QString& sourceFile) { + if (sourceFile.endsWith(".ktx") || sourceFile.endsWith(".dds")) { + return sourceFile; + } + QImage sourceImage(sourceFile); + gli::texture2d workTexture( + fromQImageFormat(sourceImage.format()), + gli::extent2d(sourceImage.width(), sourceImage.height())); + auto sourceSize = sourceImage.byteCount(); + assert(sourceSize == workTexture[workTexture.base_level()].size()); + memcpy(workTexture[workTexture.base_level()].data(), sourceImage.constBits(), sourceSize); + + QString resultFile = getKtxFileName(sourceFile) ; + gli::texture2d TextureMipmaped = gli::generate_mipmaps(workTexture, gli::FILTER_LINEAR); + gli::save(TextureMipmaped, resultFile.toLocal8Bit().data()); + gli::texture loaded = gli::load(resultFile.toLocal8Bit().data()); + return sourceFile; +} + + +gpu::TexturePointer processTexture(const QString& sourceFile) { + auto ktxFile = convertTexture(sourceFile); + gli::texture texture = gli::load(ktxFile.toLocal8Bit().data()); + if (texture.empty()) { + return gpu::TexturePointer(); + } + // FIXME load the actual KTX texture + return gpu::TexturePointer(); +} diff --git a/tests/render-texture-load/src/GLIHelpers.h b/tests/render-texture-load/src/GLIHelpers.h new file mode 100644 index 0000000000..c2841311a9 --- /dev/null +++ b/tests/render-texture-load/src/GLIHelpers.h @@ -0,0 +1,47 @@ +// +// Created by Bradley Austin Davis on 2016/09/03 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once +#ifndef _GLIHelpers_H_ +#define _GLIHelpers_H_ + +#include +#include + +// Work around for a bug in the MSVC compiler that chokes when you use GLI and Qt headers together. +#define gli glm + +#ifdef Q_OS_MAC +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-variable" +#pragma clang diagnostic ignored "-Wignored-qualifiers" +#endif + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wempty-body" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#pragma GCC diagnostic ignored "-Wunused-result" +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + +#include + +#ifdef Q_OS_MAC +#pragma clang diagnostic pop +#endif + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#include + +gpu::TexturePointer processTexture(const QString& file); + +#endif diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp new file mode 100644 index 0000000000..fb0b16d16c --- /dev/null +++ b/tests/render-texture-load/src/main.cpp @@ -0,0 +1,666 @@ +// +// Created by Bradley Austin Davis on 2016/07/01 +// 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 +#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 "GLIHelpers.h" +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +extern QThread* RENDER_THREAD; + +static const QString DATA_SET = "https://hifi-content.s3.amazonaws.com/austin/textures.zip"; +static QDir DATA_DIR = QDir(QString("h:/textures")); +static QTemporaryDir* DOWNLOAD_DIR = nullptr; + +class FileDownloader : public QObject { + Q_OBJECT +public: + using Handler = std::function; + + FileDownloader(QUrl url, const Handler& handler, QObject *parent = 0) : QObject(parent), _handler(handler) { + connect(&_accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*))); + _accessManager.get(QNetworkRequest(url)); + } + + void waitForDownload() { + while (!_complete) { + QCoreApplication::processEvents(); + } + } + + private slots: + void fileDownloaded(QNetworkReply* pReply) { + _handler(pReply->readAll()); + pReply->deleteLater(); + _complete = true; + } + +private: + QNetworkAccessManager _accessManager; + Handler _handler; + bool _complete { false }; +}; + +class RenderThread : public GenericThread { + using Parent = GenericThread; +public: + gl::Context _context; + gpu::PipelinePointer _presentPipeline; + gpu::ContextPointer _gpuContext; // initialized during window creation + std::atomic _presentCount; + QElapsedTimer _elapsed; + std::atomic _fps{ 1 }; + RateCounter<200> _fpsCounter; + std::mutex _mutex; + std::shared_ptr _backend; + std::vector _frameTimes; + size_t _frameIndex; + std::mutex _frameLock; + std::queue _pendingFrames; + gpu::FramePointer _activeFrame; + QSize _size; + static const size_t FRAME_TIME_BUFFER_SIZE{ 1024 }; + + void submitFrame(const gpu::FramePointer& frame) { + std::unique_lock lock(_frameLock); + _pendingFrames.push(frame); + } + + + void initialize(QWindow* window, gl::Context& initContext) { + setObjectName("RenderThread"); + _context.setWindow(window); + _context.create(); + _context.makeCurrent(); + window->setSurfaceType(QSurface::OpenGLSurface); + _context.makeCurrent(_context.qglContext(), window); + // GPU library init + gpu::Context::init(); + _gpuContext = std::make_shared(); + _backend = _gpuContext->getBackend(); + _context.makeCurrent(); + initContext.create(); + _context.doneCurrent(); + std::unique_lock lock(_mutex); + Parent::initialize(); + _context.moveToThread(_thread); + } + + void setup() override { + RENDER_THREAD = QThread::currentThread(); + + // Wait until the context has been moved to this thread + { + std::unique_lock lock(_mutex); + } + + _context.makeCurrent(); + glewExperimental = true; + glewInit(); + glGetError(); + + //wglSwapIntervalEXT(0); + _frameTimes.resize(FRAME_TIME_BUFFER_SIZE, 0); + { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::StandardShaderLib::getDrawTexturePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + _presentPipeline = gpu::Pipeline::create(program, state); + } + + //_textOverlay = new TextOverlay(glm::uvec2(800, 600)); + glViewport(0, 0, 800, 600); + (void)CHECK_GL_ERROR(); + _elapsed.start(); + } + + void shutdown() override { + _activeFrame.reset(); + while (!_pendingFrames.empty()) { + _gpuContext->consumeFrameUpdates(_pendingFrames.front()); + _pendingFrames.pop(); + } + _presentPipeline.reset(); + _gpuContext.reset(); + } + + void renderFrame(gpu::FramePointer& frame) { + ++_presentCount; + _context.makeCurrent(); + _backend->recycle(); + _backend->syncCache(); + if (frame && !frame->batches.empty()) { + _gpuContext->executeFrame(frame); + + { + + auto geometryCache = DependencyManager::get(); + gpu::Batch presentBatch; + presentBatch.setViewportTransform({ 0, 0, _size.width(), _size.height() }); + presentBatch.enableStereo(false); + presentBatch.resetViewTransform(); + presentBatch.setFramebuffer(gpu::FramebufferPointer()); + presentBatch.setResourceTexture(0, frame->framebuffer->getRenderBuffer(0)); + presentBatch.setPipeline(_presentPipeline); + presentBatch.draw(gpu::TRIANGLE_STRIP, 4); + _gpuContext->executeBatch(presentBatch); + } + (void)CHECK_GL_ERROR(); + } + _context.makeCurrent(); + _context.swapBuffers(); + _fpsCounter.increment(); + static size_t _frameCount{ 0 }; + ++_frameCount; + if (_elapsed.elapsed() >= 500) { + _fps = _fpsCounter.rate(); + _frameCount = 0; + _elapsed.restart(); + } + (void)CHECK_GL_ERROR(); + _context.doneCurrent(); + } + + void report() { + uint64_t total = 0; + for (const auto& t : _frameTimes) { + total += t; + } + auto averageFrameTime = total / FRAME_TIME_BUFFER_SIZE; + qDebug() << "Average frame " << averageFrameTime; + + std::list> sortedHighFrames; + for (size_t i = 0; i < _frameTimes.size(); ++i) { + const auto& t = _frameTimes[i]; + if (t > averageFrameTime * 6) { + sortedHighFrames.push_back({ t, i } ); + } + } + + sortedHighFrames.sort(); + for (const auto& p : sortedHighFrames) { + qDebug() << "Long frame " << p.first << " " << p.second; + } + } + + + bool process() override { + std::queue pendingFrames; + { + std::unique_lock lock(_frameLock); + pendingFrames.swap(_pendingFrames); + } + + while (!pendingFrames.empty()) { + _activeFrame = pendingFrames.front(); + if (_activeFrame) { + _gpuContext->consumeFrameUpdates(_activeFrame); + } + pendingFrames.pop(); + } + + if (!_activeFrame) { + QThread::msleep(1); + return true; + } + + { + auto start = usecTimestampNow(); + renderFrame(_activeFrame); + auto duration = usecTimestampNow() - start; + auto frameBufferIndex = _frameIndex % FRAME_TIME_BUFFER_SIZE; + _frameTimes[frameBufferIndex] = duration; + ++_frameIndex; + if (0 == _frameIndex % FRAME_TIME_BUFFER_SIZE) { + report(); + } + } + return true; + } +}; + +// Create a simple OpenGL window that renders text in various ways +class QTestWindow : public QWindow { +public: + //"/-17.2049,-8.08629,-19.4153/0,0.881994,0,-0.47126" + static void setup() { + DependencyManager::registerInheritance(); + //DependencyManager::registerInheritance(); + DependencyManager::set(); + DependencyManager::set(NodeType::Agent); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + } + + struct TextureLoad { + uint32_t time; + QString file; + QString src; + }; + + QTestWindow() { + { + QStringList stringList; + QFile textFile(DATA_DIR.path() + "/loads.txt"); + textFile.open(QFile::ReadOnly); + //... (open the file for reading, etc.) + QTextStream textStream(&textFile); + while (true) { + QString line = textStream.readLine(); + if (line.isNull()) + break; + else + stringList.append(line); + } + + for (QString s : stringList) { + auto index = s.indexOf(" "); + QString timeStr = s.left(index); + auto time = timeStr.toUInt(); + QString path = DATA_DIR.path() + "/" + s.right(s.length() - index).trimmed(); + if (!QFileInfo(path).exists()) { + continue; + } + qDebug() << "Path " << path; + _texturesFiles.push_back({ time, path, s }); + } + _textures.resize(_texturesFiles.size()); + } + + installEventFilter(this); + QThreadPool::globalInstance()->setMaxThreadCount(2); + QThread::currentThread()->setPriority(QThread::HighestPriority); + ResourceManager::init(); + setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint); + _size = QSize(800, 600); + _renderThread._size = _size; + setGeometry(QRect(QPoint(), _size)); + create(); + show(); + QCoreApplication::processEvents(); + // Create the initial context + _renderThread.initialize(this, _initContext); + _initContext.makeCurrent(); + // FIXME use a wait condition + QThread::msleep(1000); + _renderThread.submitFrame(gpu::FramePointer()); + _initContext.makeCurrent(); + { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::StandardShaderLib::getDrawTexturePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + state->setScissorEnable(true); + _simplePipeline = gpu::Pipeline::create(program, state); + } + + QTimer* timer = new QTimer(this); + timer->setInterval(0); + connect(timer, &QTimer::timeout, this, [this] { + draw(); + }); + timer->start(); + _ready = true; + } + + virtual ~QTestWindow() { + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + ResourceManager::cleanup(); + } + +protected: + + bool eventFilter(QObject *obj, QEvent *event) override { + if (event->type() == QEvent::Close) { + _renderThread.terminate(); + } + + return QWindow::eventFilter(obj, event); + } + + void keyPressEvent(QKeyEvent* event) override { + switch (event->key()) { + case Qt::Key_Left: + prevTexture(); + break; + case Qt::Key_Right: + nextTexture(); + break; + case Qt::Key_Return: + reportMemory(); + break; + case Qt::Key_PageDown: + derezTexture(); + break; + case Qt::Key_Home: + unloadAll(); + break; + case Qt::Key_End: + loadAll(); + break; + case Qt::Key_Down: + loadTexture(); + break; + case Qt::Key_Up: + unloadTexture(); + break; + } + QWindow::keyPressEvent(event); + } + + void keyReleaseEvent(QKeyEvent* event) override { + } + + void mouseMoveEvent(QMouseEvent* event) override { + } + + void resizeEvent(QResizeEvent* ev) override { + resizeWindow(ev->size()); + } + + void nextTexture() { + if (_textures.empty()) { + return; + } + auto textureCount = _textures.size(); + _currentTextureIndex = (_currentTextureIndex + 1) % textureCount; + loadTexture(); + } + + void prevTexture() { + if (_textures.empty()) { + return; + } + auto textureCount = _textures.size(); + _currentTextureIndex = (_currentTextureIndex + textureCount - 1) % textureCount; + loadTexture(); + } + + void reportMemory() { + static GLint lastMemory = 0; + GLint availableMem; + glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &availableMem); + qDebug() << "Memory available " << availableMem; + if (lastMemory != 0) { + qDebug() << "Delta " << availableMem - lastMemory; + } + lastMemory = availableMem; + } + + void derezTexture() { + if (!_textures[_currentTextureIndex]) { + return; + } + auto texture = _textures[_currentTextureIndex]; + texture->setMinMip(texture->minMip() + 1); + } + + void loadTexture() { + if (_textures[_currentTextureIndex]) { + return; + } + auto file = _texturesFiles[_currentTextureIndex].file; + qDebug() << "Loading texture " << file; + _textures[_currentTextureIndex] = DependencyManager::get()->getImageTexture(file); + } + + void unloadTexture() { + if (_textures.empty()) { + return; + } + _textures[_currentTextureIndex].reset(); + } + + void loadAll() { + for (size_t i = 0; i < _texturesFiles.size(); ++i) { + if (_textures[i]) { + continue; + } + auto file = _texturesFiles[i].file; + qDebug() << "Loading texture " << file; + _textures[i] = DependencyManager::get()->getImageTexture(file); + } + } + + void unloadAll() { + for (auto& texture : _textures) { + texture.reset(); + } + } + +private: + size_t _currentTextureIndex { 0 }; + std::vector _texturesFiles; + std::vector _textures; + + uint16_t _fps; + gpu::PipelinePointer _simplePipeline; + + void draw() { + if (!_ready) { + return; + } + if (!isVisible()) { + return; + } + if (_renderCount.load() != 0 && _renderCount.load() >= _renderThread._presentCount.load()) { + QThread::usleep(1); + return; + } + _renderCount = _renderThread._presentCount.load(); + update(); + + QSize windowSize = _size; + auto framebufferCache = DependencyManager::get(); + framebufferCache->setFrameBufferSize(windowSize); + + // Final framebuffer that will be handled to the display-plugin + render(); + + if (_fps != _renderThread._fps) { + _fps = _renderThread._fps; + updateText(); + } + } + + void updateText() { + setTitle(QString("FPS %1").arg(_fps)); + } + + void update() { + auto now = usecTimestampNow(); + static auto last = now; + auto delta = (now - last) / USECS_PER_MSEC; + Q_UNUSED(delta); +#if 0 + if (!_textures.empty()) { + const auto& front = _textureLoads.front(); + if (delta >= front.time) { + QFileInfo fileInfo(front.file); + if (!fileInfo.exists()) { + qDebug() << "Missing file " << front.file; + } else { + qDebug() << "Loading " << front.src; + auto file = front.file.toLocal8Bit().toStdString(); + processTexture(file.c_str()); + _textures.push_back(DependencyManager::get()->getImageTexture(front.file)); + } + _textureLoads.pop(); + if (_textureLoads.empty()) { + qDebug() << "Done"; + } + } + } +#endif + } + + void render() { + auto& gpuContext = _renderThread._gpuContext; + gpuContext->beginFrame(); + gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) { + batch.resetStages(); + }); + PROFILE_RANGE(__FUNCTION__); + auto framebuffer = DependencyManager::get()->getFramebuffer(); + + gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setFramebuffer(framebuffer); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(1, 0, 0, 1)); + auto vpsize = framebuffer->getSize(); + auto vppos = ivec2(0); + batch.setViewportTransform(ivec4(vppos, vpsize)); + if (!_textures.empty()) { + batch.setResourceTexture(0, _textures[_currentTextureIndex]); + } + batch.setPipeline(_simplePipeline); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + + auto frame = gpuContext->endFrame(); + frame->framebuffer = framebuffer; + frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer) { + DependencyManager::get()->releaseFramebuffer(framebuffer); + }; + _renderThread.submitFrame(frame); + if (!_renderThread.isThreaded()) { + _renderThread.process(); + } + } + + void resizeWindow(const QSize& size) { + _size = size; + if (!_ready) { + return; + } + _renderThread._size = size; + } + +private: + QSize _size; + std::atomic _renderCount; + gl::OffscreenContext _initContext; + RenderThread _renderThread; + ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. + bool _ready { false }; +}; + +void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { + if (!message.isEmpty()) { +#ifdef Q_OS_WIN + OutputDebugStringA(message.toLocal8Bit().constData()); + OutputDebugStringA("\n"); +#endif + std::cout << message.toLocal8Bit().constData() << std::endl; + } +} + +const char * LOG_FILTER_RULES = R"V0G0N( +hifi.gpu=true +)V0G0N"; + +void unzipTestData(const QByteArray& zipData) { + DOWNLOAD_DIR = new QTemporaryDir(); + QTemporaryDir& tempDir = *DOWNLOAD_DIR; + QTemporaryFile zipFile; + + if (zipFile.open()) { + zipFile.write(zipData); + zipFile.close(); + } + qDebug() << zipFile.fileName(); + if (!tempDir.isValid()) { + qFatal("Unable to create temp dir"); + } + DATA_DIR = QDir(tempDir.path()); + + //auto files = JlCompress::getFileList(zipData); + auto files = JlCompress::extractDir(zipFile.fileName(), DATA_DIR.path()); + qDebug() << DATA_DIR.path(); +} + +int main(int argc, char** argv) { + QApplication app(argc, argv); + QCoreApplication::setApplicationName("RenderPerf"); + QCoreApplication::setOrganizationName("High Fidelity"); + QCoreApplication::setOrganizationDomain("highfidelity.com"); + qInstallMessageHandler(messageHandler); + QLoggingCategory::setFilterRules(LOG_FILTER_RULES); + + if (!DATA_DIR.exists()) { + FileDownloader(DATA_SET, [&](const QByteArray& data) { + qDebug() << "Fetched size " << data.size(); + unzipTestData(data); + }).waitForDownload(); + } + + QTestWindow::setup(); + QTestWindow window; + app.exec(); + return 0; +} + +#include "main.moc" diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 2056044a4b..a077efc335 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,7 +1,4 @@ # add the tool directories -add_subdirectory(mtc) -set_target_properties(mtc PROPERTIES FOLDER "Tools") - add_subdirectory(scribe) set_target_properties(scribe PROPERTIES FOLDER "Tools") @@ -10,3 +7,6 @@ set_target_properties(udt-test PROPERTIES FOLDER "Tools") add_subdirectory(vhacd-util) set_target_properties(vhacd-util PROPERTIES FOLDER "Tools") + +add_subdirectory(ice-client) +set_target_properties(ice-client PROPERTIES FOLDER "Tools") diff --git a/tools/ice-client/CMakeLists.txt b/tools/ice-client/CMakeLists.txt new file mode 100644 index 0000000000..a80145974c --- /dev/null +++ b/tools/ice-client/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME ice-client) +setup_hifi_project(Core Widgets) +link_hifi_libraries(shared networking) diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp new file mode 100644 index 0000000000..992014ad7d --- /dev/null +++ b/tools/ice-client/src/ICEClientApp.cpp @@ -0,0 +1,387 @@ +// +// ICEClientApp.cpp +// tools/ice-client/src +// +// Created by Seth Alves on 3/5/15. +// 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 "ICEClientApp.h" + +ICEClientApp::ICEClientApp(int argc, char* argv[]) : + QCoreApplication(argc, argv) +{ + // parse command-line + QCommandLineParser parser; + parser.setApplicationDescription("High Fidelity ICE client"); + parser.addHelpOption(); + + const QCommandLineOption helpOption = parser.addHelpOption(); + + const QCommandLineOption verboseOutput("v", "verbose output"); + parser.addOption(verboseOutput); + + const QCommandLineOption iceServerAddressOption("i", "ice-server address", "IP:PORT or HOSTNAME:PORT"); + parser.addOption(iceServerAddressOption); + + const QCommandLineOption howManyTimesOption("n", "how many times to cycle", "1"); + parser.addOption(howManyTimesOption); + + const QCommandLineOption domainIDOption("d", "domain-server uuid", "00000000-0000-0000-0000-000000000000"); + parser.addOption(domainIDOption); + + const QCommandLineOption cacheSTUNOption("s", "cache stun-server response"); + parser.addOption(cacheSTUNOption); + + if (!parser.parse(QCoreApplication::arguments())) { + qCritical() << parser.errorText() << endl; + parser.showHelp(); + Q_UNREACHABLE(); + } + + if (parser.isSet(helpOption)) { + parser.showHelp(); + Q_UNREACHABLE(); + } + + _verbose = parser.isSet(verboseOutput); + if (!_verbose) { + const_cast(&networking())->setEnabled(QtDebugMsg, false); + const_cast(&networking())->setEnabled(QtInfoMsg, false); + const_cast(&networking())->setEnabled(QtWarningMsg, false); + } + + _stunSockAddr = HifiSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT, true); + + _cacheSTUNResult = parser.isSet(cacheSTUNOption); + + if (parser.isSet(howManyTimesOption)) { + _actionMax = parser.value(howManyTimesOption).toInt(); + } else { + _actionMax = 1; + } + + if (parser.isSet(domainIDOption)) { + _domainID = QUuid(parser.value(domainIDOption)); + if (_verbose) { + qDebug() << "domain-server ID is" << _domainID; + } + } + + _iceServerAddr = HifiSockAddr("127.0.0.1", ICE_SERVER_DEFAULT_PORT); + if (parser.isSet(iceServerAddressOption)) { + // parse the IP and port combination for this target + QString hostnamePortString = parser.value(iceServerAddressOption); + + QHostAddress address { hostnamePortString.left(hostnamePortString.indexOf(':')) }; + quint16 port { (quint16) hostnamePortString.mid(hostnamePortString.indexOf(':') + 1).toUInt() }; + if (port == 0) { + port = ICE_SERVER_DEFAULT_PORT; + } + + if (address.isNull()) { + qCritical() << "Could not parse an IP address and port combination from" << hostnamePortString << "-" << + "The parsed IP was" << address.toString() << "and the parsed port was" << port; + + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + } else { + _iceServerAddr = HifiSockAddr(address, port); + } + } + + if (_verbose) { + qDebug() << "ICE-server address is" << _iceServerAddr; + } + + setState(lookUpStunServer); + + QTimer* doTimer = new QTimer(this); + connect(doTimer, &QTimer::timeout, this, &ICEClientApp::doSomething); + doTimer->start(200); +} + +ICEClientApp::~ICEClientApp() { + delete _socket; +} + +void ICEClientApp::setState(int newState) { + _state = newState; +} + +void ICEClientApp::closeSocket() { + _domainServerPeerSet = false; + delete _socket; + _socket = nullptr; +} + +void ICEClientApp::openSocket() { + if (_socket) { + return; + } + + _socket = new udt::Socket(); + unsigned int localPort = 0; + _socket->bind(QHostAddress::AnyIPv4, localPort); + _socket->setPacketHandler([this](std::unique_ptr packet) { processPacket(std::move(packet)); }); + _socket->addUnfilteredHandler(_stunSockAddr, + [this](std::unique_ptr packet) { + processSTUNResponse(std::move(packet)); + }); + + if (_verbose) { + qDebug() << "local port is" << _socket->localPort(); + } + _localSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); + _publicSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); + _domainPingCount = 0; +} + +void ICEClientApp::doSomething() { + if (_actionMax > 0 && _actionCount >= _actionMax) { + // time to stop. + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + + } else if (_state == lookUpStunServer) { + // lookup STUN server address + if (!_stunSockAddr.getAddress().isNull()) { + if (_verbose) { + qDebug() << "stun server is" << _stunSockAddr; + } + setState(sendStunRequestPacket); + } else { + if (_verbose) { + qDebug() << "_stunSockAddr is" << _stunSockAddr.getAddress(); + } + QCoreApplication::exit(stunFailureExitStatus); + } + + } else if (_state == sendStunRequestPacket) { + // send STUN request packet + closeSocket(); + openSocket(); + + if (!_cacheSTUNResult || !_stunResultSet) { + const int NUM_BYTES_STUN_HEADER = 20; + char stunRequestPacket[NUM_BYTES_STUN_HEADER]; + LimitedNodeList::makeSTUNRequestPacket(stunRequestPacket); + if (_verbose) { + qDebug() << "sending STUN request"; + } + _socket->writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); + _stunResponseTimerCanceled = false; + _stunResponseTimer.singleShot(stunResponseTimeoutMilliSeconds, this, [&] { + if (_stunResponseTimerCanceled) { + return; + } + if (_verbose) { + qDebug() << "timeout waiting for stun-server response"; + } + QCoreApplication::exit(stunFailureExitStatus); + }); + + setState(waitForStunResponse); + } else { + if (_verbose) { + qDebug() << "using cached STUN response"; + } + _publicSockAddr.setPort(_socket->localPort()); + setState(talkToIceServer); + } + + } else if (_state == talkToIceServer) { + QUuid peerID; + if (_domainID == QUuid()) { + // pick a random domain-id which will fail + peerID = QUuid::createUuid(); + setState(pause0); + } else { + // use the domain UUID given on the command-line + peerID = _domainID; + setState(waitForIceReply); + } + _sessionUUID = QUuid::createUuid(); + if (_verbose) { + qDebug() << "I am" << _sessionUUID; + } + + sendPacketToIceServer(PacketType::ICEServerQuery, _iceServerAddr, _sessionUUID, peerID); + _iceResponseTimerCanceled = false; + _iceResponseTimer.singleShot(iceResponseTimeoutMilliSeconds, this, [=] { + if (_iceResponseTimerCanceled) { + return; + } + if (_verbose) { + qDebug() << "timeout waiting for ice-server response"; + } + QCoreApplication::exit(iceFailureExitStatus); + }); + } else if (_state == pause0) { + setState(pause1); + } else if (_state == pause1) { + if (_verbose) { + qDebug() << ""; + } + closeSocket(); + setState(sendStunRequestPacket); + _actionCount++; + } +} + +void ICEClientApp::sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, + const QUuid& clientID, const QUuid& peerID) { + std::unique_ptr icePacket = NLPacket::create(packetType); + + QDataStream iceDataStream(icePacket.get()); + iceDataStream << clientID << _publicSockAddr << _localSockAddr; + + if (packetType == PacketType::ICEServerQuery) { + assert(!peerID.isNull()); + + iceDataStream << peerID; + + if (_verbose) { + qDebug() << "Sending packet to ICE server to request connection info for peer with ID" + << uuidStringWithoutCurlyBraces(peerID); + } + } + + // fillPacketHeader(packet, connectionSecret); + _socket->writePacket(*icePacket, _iceServerAddr); +} + +void ICEClientApp::checkDomainPingCount() { + _domainPingCount++; + if (_domainPingCount > 5) { + if (_verbose) { + qDebug() << "too many unanswered pings to domain-server."; + } + QCoreApplication::exit(domainPingExitStatus); + } +} + +void ICEClientApp::icePingDomainServer() { + if (!_domainServerPeerSet) { + return; + } + + if (_verbose) { + qDebug() << "ice-pinging domain-server: " << _domainServerPeer; + } + + auto localPingPacket = LimitedNodeList::constructICEPingPacket(PingType::Local, _sessionUUID); + _socket->writePacket(*localPingPacket, _domainServerPeer.getLocalSocket()); + + auto publicPingPacket = LimitedNodeList::constructICEPingPacket(PingType::Public, _sessionUUID); + _socket->writePacket(*publicPingPacket, _domainServerPeer.getPublicSocket()); + checkDomainPingCount(); +} + +void ICEClientApp::processSTUNResponse(std::unique_ptr packet) { + if (_verbose) { + qDebug() << "got stun response"; + } + if (_state != waitForStunResponse) { + if (_verbose) { + qDebug() << "got unexpected stun response"; + } + QCoreApplication::exit(stunFailureExitStatus); + } + + _stunResponseTimer.stop(); + _stunResponseTimerCanceled = true; + + uint16_t newPublicPort; + QHostAddress newPublicAddress; + if (LimitedNodeList::parseSTUNResponse(packet.get(), newPublicAddress, newPublicPort)) { + _publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort); + if (_verbose) { + qDebug() << "My public address is" << _publicSockAddr; + } + _stunResultSet = true; + setState(talkToIceServer); + } else { + QCoreApplication::exit(stunFailureExitStatus); + } +} + + +void ICEClientApp::processPacket(std::unique_ptr packet) { + std::unique_ptr nlPacket = NLPacket::fromBase(std::move(packet)); + + if (nlPacket->getPayloadSize() < NLPacket::localHeaderSize(PacketType::ICEServerHeartbeat)) { + if (_verbose) { + qDebug() << "got a short packet."; + } + return; + } + + QSharedPointer message = QSharedPointer::create(*nlPacket); + const HifiSockAddr& senderAddr = message->getSenderSockAddr(); + + if (nlPacket->getType() == PacketType::ICEServerPeerInformation) { + // cancel the timeout timer + _iceResponseTimer.stop(); + _iceResponseTimerCanceled = true; + + QDataStream iceResponseStream(message->getMessage()); + if (!_domainServerPeerSet) { + iceResponseStream >> _domainServerPeer; + if (_verbose) { + qDebug() << "got ICEServerPeerInformation from" << _domainServerPeer; + } + _domainServerPeerSet = true; + + icePingDomainServer(); + _pingDomainTimer = new QTimer(this); + connect(_pingDomainTimer, &QTimer::timeout, this, &ICEClientApp::icePingDomainServer); + _pingDomainTimer->start(500); + } else { + NetworkPeer domainServerPeer; + iceResponseStream >> domainServerPeer; + if (_verbose) { + qDebug() << "got repeat ICEServerPeerInformation from" << domainServerPeer; + } + } + + } else if (nlPacket->getType() == PacketType::ICEPing) { + if (_verbose) { + qDebug() << "got packet: " << nlPacket->getType(); + } + auto replyPacket = LimitedNodeList::constructICEPingReplyPacket(*message, _sessionUUID); + _socket->writePacket(*replyPacket, senderAddr); + checkDomainPingCount(); + + } else if (nlPacket->getType() == PacketType::ICEPingReply) { + if (_verbose) { + qDebug() << "got packet: " << nlPacket->getType(); + } + if (_domainServerPeerSet && _state == waitForIceReply && + (senderAddr == _domainServerPeer.getLocalSocket() || + senderAddr == _domainServerPeer.getPublicSocket())) { + + delete _pingDomainTimer; + _pingDomainTimer = nullptr; + + setState(pause0); + } else { + if (_verbose) { + qDebug() << "got unexpected ICEPingReply" << senderAddr; + } + } + + } else { + if (_verbose) { + qDebug() << "got unexpected packet: " << nlPacket->getType(); + } + } +} diff --git a/tools/ice-client/src/ICEClientApp.h b/tools/ice-client/src/ICEClientApp.h new file mode 100644 index 0000000000..3635bc07f4 --- /dev/null +++ b/tools/ice-client/src/ICEClientApp.h @@ -0,0 +1,96 @@ +// +// ICEClientApp.h +// tools/ice-client/src +// +// Created by Seth Alves on 2016-9-16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#ifndef hifi_ICEClientApp_h +#define hifi_ICEClientApp_h + +#include +#include +#include +#include +#include + + +class ICEClientApp : public QCoreApplication { + Q_OBJECT +public: + ICEClientApp(int argc, char* argv[]); + ~ICEClientApp(); + + const int stunFailureExitStatus { 1 }; + const int iceFailureExitStatus { 2 }; + const int domainPingExitStatus { 3 }; + + const int stunResponseTimeoutMilliSeconds { 2000 }; + const int iceResponseTimeoutMilliSeconds { 2000 }; + +private: + enum State { + lookUpStunServer, // 0 + sendStunRequestPacket, // 1 + waitForStunResponse, // 2 + talkToIceServer, // 3 + waitForIceReply, // 4 + pause0, // 5 + pause1 // 6 + }; + + void closeSocket(); + void openSocket(); + + void setState(int newState); + + void doSomething(); + void sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, + const QUuid& clientID, const QUuid& peerID); + void icePingDomainServer(); + void processSTUNResponse(std::unique_ptr packet); + void processPacket(std::unique_ptr packet); + void checkDomainPingCount(); + + bool _verbose; + bool _cacheSTUNResult; // should we only talk to stun server once? + bool _stunResultSet { false }; // have we already talked to stun server? + + HifiSockAddr _stunSockAddr; + + unsigned int _actionCount { 0 }; + unsigned int _actionMax { 0 }; + + QUuid _sessionUUID; + QUuid _domainID; + + QTimer* _pingDomainTimer { nullptr }; + + HifiSockAddr _iceServerAddr; + + HifiSockAddr _localSockAddr; + HifiSockAddr _publicSockAddr; + udt::Socket* _socket { nullptr }; + + bool _domainServerPeerSet { false }; + NetworkPeer _domainServerPeer; + + int _state { 0 }; + + QTimer _stunResponseTimer; + bool _stunResponseTimerCanceled { false }; + QTimer _iceResponseTimer; + bool _iceResponseTimerCanceled { false }; + int _domainPingCount { 0 }; +}; + + + + + +#endif //hifi_ICEClientApp_h diff --git a/tools/ice-client/src/main.cpp b/tools/ice-client/src/main.cpp new file mode 100644 index 0000000000..c70a7eb7d7 --- /dev/null +++ b/tools/ice-client/src/main.cpp @@ -0,0 +1,23 @@ +// +// main.cpp +// tools/ice-client/src +// +// Created by Seth Alves on 2016-9-16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +#include +#include +#include +#include + +#include "ICEClientApp.h" + +using namespace std; + +int main(int argc, char * argv[]) { + ICEClientApp app(argc, argv); + return app.exec(); +} diff --git a/tools/mtc/CMakeLists.txt b/tools/mtc/CMakeLists.txt deleted file mode 100644 index c7134a869f..0000000000 --- a/tools/mtc/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -set(TARGET_NAME mtc) -setup_hifi_project() - -package_libraries_for_deployment() diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp deleted file mode 100644 index dd0ab837a5..0000000000 --- a/tools/mtc/src/main.cpp +++ /dev/null @@ -1,362 +0,0 @@ -// -// main.cpp -// tools/mtc/src -// -// Created by Andrzej Kapolka on 12/31/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -class Class { -public: - QString name; - QStringList bases; -}; - -class Field { -public: - QString type; - QString name; -}; - -class Streamable { -public: - Class clazz; - QList fields; -}; - -void processInput(QTextStream& in, QList* streamables) { - Class clazz; - Streamable currentStreamable; - - QRegExp exp( - "(/\\*.*\\*/)|" // multi-line comments - "(//.*\n)|" // single-line comments - "(\\s*#.*\n)|" // preprocessor definitions - "(\\s*STREAMABLE\\s+)|" // STREAMABLE tag for classes - "(\\s*STREAM\\s+.*;)|" // STREAM tag for fields - "(\\s*class\\s+[^;]+\\{)" // class definition - ); - exp.setMinimal(true); - - QRegExp classExp("class (\\w+) ?:?([^:]*)\\{"); - - // read in the entire input and look for matches with our expression - QString all = in.readAll(); - for (int off = 0; (off = exp.indexIn(all, off)) != -1; off += exp.matchedLength()) { - QString match = exp.cap().simplified(); - if (match.startsWith("/*") || match.startsWith("//") || match.startsWith('#')) { - continue; // comment, preprocessor definition - } - if (match.startsWith("STREAMABLE")) { - if (clazz.name.isEmpty()) { - cerr << "Found STREAMABLE marker before class definition." << endl; - continue; - } - if (!currentStreamable.clazz.name.isEmpty()) { - streamables->append(currentStreamable); - } - currentStreamable.clazz = clazz; - currentStreamable.fields.clear(); - - } else if (match.startsWith("STREAM")) { - match.chop(1); // get rid of the semicolon - match = match.mid(match.indexOf(' ') + 1).trimmed(); // and STREAM, and any space before it - int index = match.lastIndexOf(' '); - Field field = { match.left(index).simplified(), match.mid(index + 1) }; - currentStreamable.fields.append(field); - - } else { // match.startsWith("class") - classExp.exactMatch(match); - clazz.name = classExp.cap(1); - clazz.bases.clear(); - foreach (const QString& bstr, classExp.cap(2).split(',')) { - QString base = bstr.trimmed(); - if (!base.isEmpty() && base.startsWith("STREAM")) { - clazz.bases.append(base.mid(base.lastIndexOf(' ') + 1)); - } - } - } - } - if (!currentStreamable.clazz.name.isEmpty()) { - streamables->append(currentStreamable); - } -} - -void generateOutput (QTextStream& out, const QList& streamables) { - foreach (const Streamable& str, streamables) { - const QString& name = str.clazz.name; - - out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n"; - - out << "const QVector& " << name << "::getMetaFields() {\n"; - out << " static QVector metaFields = QVector()"; - foreach (const QString& base, str.clazz.bases) { - out << " << " << base << "::getMetaFields()"; - } - foreach (const Field& field, str.fields) { - out << "\n << MetaField(\"" << field.name << "\", Bitstream::getTypeStreamer(qMetaTypeId<" << - field.type << ">()))"; - } - out << ";\n"; - out << " return metaFields;\n"; - out << "}\n"; - - out << "int " << name << "::getFieldIndex(const QByteArray& name) {\n"; - out << " static QHash fieldIndices = createFieldIndices();\n"; - out << " return fieldIndices.value(name) - 1;\n"; - out << "}\n"; - - out << "QHash " << name << "::createFieldIndices() {\n"; - out << " QHash indices;\n"; - out << " int index = 1;\n"; - out << " foreach (const MetaField& field, getMetaFields()) {\n"; - out << " indices.insert(field.getName(), index++);\n"; - out << " }\n"; - out << " return indices;\n"; - out << "}\n"; - - out << "void " << name << "::setField(int index, const QVariant& value) {\n"; - if (!str.clazz.bases.isEmpty()) { - out << " int nextIndex;\n"; - } - foreach (const QString& base, str.clazz.bases) { - out << " if ((nextIndex = index - " << base << "::getMetaFields().size()) < 0) {\n"; - out << " " << base << "::setField(index, value);\n"; - out << " return;\n"; - out << " }\n"; - out << " index = nextIndex;\n"; - } - if (!str.fields.isEmpty()) { - out << " switch (index) {\n"; - for (int i = 0; i < str.fields.size(); i++) { - out << " case " << i << ":\n"; - out << " this->" << str.fields.at(i).name << " = value.value<" << str.fields.at(i).type << ">();\n"; - out << " break;\n"; - } - out << " }\n"; - } - out << "}\n"; - - out << "QVariant " << name << "::getField(int index) const {\n"; - if (!str.clazz.bases.isEmpty()) { - out << " int nextIndex;\n"; - } - foreach (const QString& base, str.clazz.bases) { - out << " if ((nextIndex = index - " << base << "::getMetaFields().size()) < 0) {\n"; - out << " return " << base << "::getField(index);\n"; - out << " }\n"; - out << " index = nextIndex;\n"; - } - if (!str.fields.isEmpty()) { - out << " switch (index) {\n"; - for (int i = 0; i < str.fields.size(); i++) { - out << " case " << i << ":\n"; - out << " return QVariant::fromValue(this->" << str.fields.at(i).name << ");\n"; - } - out << " }\n"; - } - out << " return QVariant();\n"; - out << "}\n"; - - out << "Bitstream& operator<<(Bitstream& out, const " << name << "& obj) {\n"; - foreach (const QString& base, str.clazz.bases) { - out << " out << static_cast(obj);\n"; - } - foreach (const Field& field, str.fields) { - out << " out << obj." << field.name << ";\n"; - } - out << " return out;\n"; - out << "}\n"; - - out << "Bitstream& operator>>(Bitstream& in, " << name << "& obj) {\n"; - foreach (const QString& base, str.clazz.bases) { - out << " in >> static_cast<" << base << "&>(obj);\n"; - } - foreach (const Field& field, str.fields) { - out << " in >> obj." << field.name << ";\n"; - } - out << " return in;\n"; - out << "}\n"; - - out << "template<> void Bitstream::writeRawDelta(const " << name << "& value, const " << name << "& reference) {\n"; - foreach (const QString& base, str.clazz.bases) { - out << " writeRawDelta(static_cast(value), static_cast(reference));\n"; - } - foreach (const Field& field, str.fields) { - out << " writeDelta(value." << field.name << ", reference." << field.name << ");\n"; - } - out << "}\n"; - - out << "template<> void Bitstream::readRawDelta(" << name << "& value, const " << name << "& reference) {\n"; - foreach (const QString& base, str.clazz.bases) { - out << " readRawDelta(static_cast<" << base << "&>(value), static_cast(reference));\n"; - } - foreach (const Field& field, str.fields) { - out << " readDelta(value." << field.name << ", reference." << field.name << ");\n"; - } - out << "}\n"; - - out << "template<> QJsonValue JSONWriter::getData(const " << name << "& value) {\n"; - out << " QJsonArray array;\n"; - foreach (const QString& base, str.clazz.bases) { - out << " foreach (const QJsonValue& element, getData(static_cast(value)).toArray()) {\n"; - out << " array.append(element);\n"; - out << " }\n"; - } - foreach (const Field& field, str.fields) { - out << " array.append(getData(value." << field.name << "));\n"; - } - out << " return array;\n"; - out << "}\n"; - - out << "template<> void JSONReader::putData(const QJsonValue& data, " << name << "& value) {\n"; - if (!(str.clazz.bases.isEmpty() && str.fields.isEmpty())) { - out << " QJsonArray array = data.toArray(), subarray;\n"; - out << " QJsonArray::const_iterator it = array.constBegin();\n"; - foreach (const QString& base, str.clazz.bases) { - out << " subarray = QJsonArray();\n"; - out << " for (int i = 0; i < " << base << "::getMetaFields().size(); i++) {\n"; - out << " subarray.append(*it++);\n"; - out << " }\n"; - out << " putData(subarray, static_cast<" << base << "&>(value));\n"; - } - foreach (const Field& field, str.fields) { - out << " putData(*it++, value." << field.name << ");\n"; - } - } - out << "}\n"; - - out << "bool operator==(const " << name << "& first, const " << name << "& second) {\n"; - if (str.clazz.bases.isEmpty() && str.fields.isEmpty()) { - out << " return true"; - } else { - out << " return "; - bool first = true; - foreach (const QString& base, str.clazz.bases) { - if (!first) { - out << " &&\n"; - out << " "; - } - out << "static_cast(first) == static_cast(second)"; - first = false; - } - foreach (const Field& field, str.fields) { - if (!first) { - out << " &&\n"; - out << " "; - } - out << "first." << field.name << " == second." << field.name; - first = false; - } - } - out << ";\n"; - out << "}\n"; - - out << "bool operator!=(const " << name << "& first, const " << name << "& second) {\n"; - if (str.clazz.bases.isEmpty() && str.fields.isEmpty()) { - out << " return false"; - } else { - out << " return "; - bool first = true; - foreach (const QString& base, str.clazz.bases) { - if (!first) { - out << " ||\n"; - out << " "; - } - out << "static_cast(first) != static_cast(second)"; - first = false; - } - foreach (const Field& field, str.fields) { - if (!first) { - out << " ||\n"; - out << " "; - } - out << "first." << field.name << " != second." << field.name; - first = false; - } - } - out << ";\n"; - out << "}\n\n"; - } -} - -int main (int argc, char** argv) { - // process the command line arguments - QStringList inputs; - QString output; - for (int ii = 1; ii < argc; ii++) { - QString arg(argv[ii]); - if (!arg.startsWith('-')) { - inputs.append(arg); - continue; - } - QStringRef name = arg.midRef(1); - if (name == "o") { - if (++ii == argc) { - cerr << "Missing file name argument for -o" << endl; - return 1; - } - output = argv[ii]; - - } else { - cerr << "Unknown option " << arg.toStdString() << endl; - return 1; - } - } - if (inputs.isEmpty()) { - cerr << "Usage: mtc [OPTION]... input files" << endl; - cerr << "Where options include:" << endl; - cerr << " -o filename: Send output to filename rather than standard output." << endl; - return 0; - } - - QList streamables; - foreach (const QString& input, inputs) { - QFile ifile(input); - if (!ifile.open(QIODevice::ReadOnly | QIODevice::Text)) { - cerr << ("Couldn't open " + input + ": " + ifile.errorString()).toStdString() << endl; - continue; - } - QTextStream istream(&ifile); - int oldSize = streamables.size(); - processInput(istream, &streamables); - if (streamables.size() == oldSize) { - // no streamables; remove from list - inputs.removeOne(input); - } - } - - QFile ofile(output); - if (output.isNull()) { - ofile.open(stdout, QIODevice::WriteOnly | QIODevice::Text); - - } else if (!ofile.open(QIODevice::WriteOnly | QIODevice::Text)) { - cerr << ("Couldn't open " + output + ": " + ofile.errorString()).toStdString() << endl; - return 1; - } - - QTextStream ostream(&ofile); - ostream << "// generated by mtc\n"; - foreach (const QString& input, inputs) { - ostream << "#include \"" << input << "\"\n"; - } - generateOutput(ostream, streamables); - - return 0; -}