diff --git a/CMakeLists.txt b/CMakeLists.txt
index d0a2e57dd5..6956fd22c3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -108,8 +108,10 @@ set(PLATFORM_QT_GL OpenGL)
if (USE_GLES)
add_definitions(-DUSE_GLES)
+ add_definitions(-DGPU_POINTER_STORAGE_SHARED)
set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gles)
else()
+ add_definitions(-DGPU_POINTER_STORAGE_RAW)
set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gl)
endif()
diff --git a/android/apps/framePlayer/CMakeLists.txt b/android/apps/framePlayer/CMakeLists.txt
new file mode 100644
index 0000000000..327c2dd1bd
--- /dev/null
+++ b/android/apps/framePlayer/CMakeLists.txt
@@ -0,0 +1,5 @@
+set(TARGET_NAME framePlayer)
+setup_hifi_library(AndroidExtras)
+link_hifi_libraries(shared ktx shaders qml gpu gl ${PLATFORM_GL_BACKEND})
+target_link_libraries(${TARGET_NAME} android log m)
+target_opengl()
diff --git a/android/apps/framePlayer/build.gradle b/android/apps/framePlayer/build.gradle
new file mode 100644
index 0000000000..fc8651fce1
--- /dev/null
+++ b/android/apps/framePlayer/build.gradle
@@ -0,0 +1,50 @@
+apply plugin: 'com.android.application'
+
+android {
+ signingConfigs {
+ release {
+ keyAlias 'key0'
+ keyPassword 'password'
+ storeFile file('C:/android/keystore.jks')
+ storePassword 'password'
+ }
+ }
+
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "io.highfidelity.frameplayer"
+ minSdkVersion 25
+ targetSdkVersion 28
+ ndk { abiFilters 'arm64-v8a' }
+ externalNativeBuild {
+ cmake {
+ arguments '-DHIFI_ANDROID=1',
+ '-DHIFI_ANDROID_APP=framePlayer',
+ '-DANDROID_TOOLCHAIN=clang',
+ '-DANDROID_STL=c++_shared',
+ '-DCMAKE_VERBOSE_MAKEFILE=ON'
+ targets = ['framePlayer']
+ }
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ signingConfig signingConfigs.release
+ }
+ }
+
+ externalNativeBuild.cmake.path '../../../CMakeLists.txt'
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
+ //implementation project(':oculus')
+ implementation project(':qt')
+}
diff --git a/android/apps/framePlayer/proguard-rules.pro b/android/apps/framePlayer/proguard-rules.pro
new file mode 100644
index 0000000000..b3c0078513
--- /dev/null
+++ b/android/apps/framePlayer/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\Android\SDK/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/android/apps/framePlayer/src/main/AndroidManifest.xml b/android/apps/framePlayer/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..ed576387d2
--- /dev/null
+++ b/android/apps/framePlayer/src/main/AndroidManifest.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/apps/framePlayer/src/main/cpp/FramePlayer.qrc b/android/apps/framePlayer/src/main/cpp/FramePlayer.qrc
new file mode 100644
index 0000000000..44fdac2666
--- /dev/null
+++ b/android/apps/framePlayer/src/main/cpp/FramePlayer.qrc
@@ -0,0 +1,6 @@
+
+
+
+ qml/main.qml
+
+
diff --git a/android/apps/framePlayer/src/main/cpp/PlayerWindow.cpp b/android/apps/framePlayer/src/main/cpp/PlayerWindow.cpp
new file mode 100644
index 0000000000..7f0ec67639
--- /dev/null
+++ b/android/apps/framePlayer/src/main/cpp/PlayerWindow.cpp
@@ -0,0 +1,91 @@
+//
+// Created by Bradley Austin Davis on 2018/10/21
+// 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 "PlayerWindow.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+PlayerWindow::PlayerWindow() {
+ setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
+ setSurfaceType(QSurface::OpenGLSurface);
+ create();
+ showFullScreen();
+
+ // Make sure the window has been created by processing events
+ QCoreApplication::processEvents();
+
+ // Start the rendering thread
+ _renderThread.initialize(this, &_surface);
+
+ // Start the UI
+ _surface.resize(size());
+ connect(&_surface, &hifi::qml::OffscreenSurface::rootContextCreated, this, [](QQmlContext* context){
+ context->setContextProperty("FRAMES_FOLDER", "file:assets:/frames");
+ });
+ _surface.load("qrc:///qml/main.qml");
+
+ // Connect the UI handler
+ QObject::connect(_surface.getRootItem(), SIGNAL(loadFile(QString)),
+ this, SLOT(loadFile(QString))
+ );
+
+ // Turn on UI input events
+ installEventFilter(&_surface);
+}
+
+PlayerWindow::~PlayerWindow() {
+}
+
+// static const char* FRAME_FILE = "assets:/frames/20190110_1635.json";
+
+static void textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer) {
+ QImage image;
+ QImageReader(filename.c_str()).read(&image);
+ if (layer > 0) {
+ return;
+ }
+ texture->assignStoredMip(0, image.byteCount(), image.constBits());
+}
+
+void PlayerWindow::loadFile(QString filename) {
+ QString realFilename = QUrl(filename).toLocalFile();
+ if (QFileInfo(realFilename).exists()) {
+ auto frame = gpu::readFrame(realFilename.toStdString(), _renderThread._externalTexture, &textureLoader);
+ _surface.pause();
+ _renderThread.submitFrame(frame);
+ }
+}
+
+void PlayerWindow::touchEvent(QTouchEvent* event) {
+ // Super basic input handling when the 3D scene is active.... tap with two finders to return to the
+ // QML UI
+ static size_t touches = 0;
+ switch (event->type()) {
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ touches = std::max(touches, event->touchPoints().size());
+ break;
+
+ case QEvent::TouchEnd:
+ if (touches >= 2) {
+ _renderThread.submitFrame(nullptr);
+ _surface.resume();
+ }
+ touches = 0;
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/android/apps/framePlayer/src/main/cpp/PlayerWindow.h b/android/apps/framePlayer/src/main/cpp/PlayerWindow.h
new file mode 100644
index 0000000000..b1cc7da5cd
--- /dev/null
+++ b/android/apps/framePlayer/src/main/cpp/PlayerWindow.h
@@ -0,0 +1,35 @@
+//
+// Created by Bradley Austin Davis on 2018/10/21
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#pragma once
+#include
+#include
+
+#include
+#include
+#include "RenderThread.h"
+
+// Create a simple OpenGL window that renders text in various ways
+class PlayerWindow : public QWindow {
+ Q_OBJECT
+
+public:
+ PlayerWindow();
+ virtual ~PlayerWindow();
+
+protected:
+ void touchEvent(QTouchEvent *ev) override;
+
+public slots:
+ void loadFile(QString filename);
+
+private:
+ hifi::qml::OffscreenSurface _surface;
+ QSettings _settings;
+ RenderThread _renderThread;
+};
diff --git a/android/apps/framePlayer/src/main/cpp/RenderThread.cpp b/android/apps/framePlayer/src/main/cpp/RenderThread.cpp
new file mode 100644
index 0000000000..76da789baa
--- /dev/null
+++ b/android/apps/framePlayer/src/main/cpp/RenderThread.cpp
@@ -0,0 +1,162 @@
+//
+// Created by Bradley Austin Davis on 2018/10/21
+// 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 "RenderThread.h"
+
+#include
+
+void RenderThread::submitFrame(const gpu::FramePointer& frame) {
+ std::unique_lock lock(_frameLock);
+ _pendingFrames.push(frame);
+}
+
+void RenderThread::move(const glm::vec3& v) {
+ std::unique_lock lock(_frameLock);
+ _correction = glm::inverse(glm::translate(mat4(), v)) * _correction;
+}
+
+void RenderThread::setup() {
+ // Wait until the context has been moved to this thread
+ { std::unique_lock lock(_frameLock); }
+
+ makeCurrent();
+ // Disable vsync for profiling
+ ::gl::setSwapInterval(0);
+
+ glClearColor(1, 1, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ _glContext.swapBuffers();
+
+ // GPU library init
+ gpu::Context::init();
+ _gpuContext = std::make_shared();
+ _backend = _gpuContext->getBackend();
+ _gpuContext->beginFrame();
+ _gpuContext->endFrame();
+ makeCurrent();
+
+
+ glGenFramebuffers(1, &_uiFbo);
+ glGenTextures(1, &_externalTexture);
+ glBindTexture(GL_TEXTURE_2D, _externalTexture);
+ static const glm::u8vec4 color{ 0 };
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &color);
+
+ glClearColor(0, 1, 1, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ _glContext.swapBuffers();
+}
+
+void RenderThread::initialize(QWindow* window, hifi::qml::OffscreenSurface* offscreen) {
+ std::unique_lock lock(_frameLock);
+ setObjectName("RenderThread");
+ Parent::initialize();
+
+ _offscreen = offscreen;
+ _window = window;
+ _glContext.setWindow(_window);
+ _glContext.create();
+ _glContext.makeCurrent();
+
+ hifi::qml::OffscreenSurface::setSharedContext(_glContext.qglContext());
+ glClearColor(1, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ _glContext.swapBuffers();
+ _glContext.doneCurrent();
+ _glContext.moveToThread(_thread);
+ _thread->setObjectName("RenderThread");
+}
+
+void RenderThread::shutdown() {
+ _activeFrame.reset();
+ while (!_pendingFrames.empty()) {
+ _gpuContext->consumeFrameUpdates(_pendingFrames.front());
+ _pendingFrames.pop();
+ }
+ _gpuContext->shutdown();
+ _gpuContext.reset();
+}
+
+void RenderThread::renderFrame() {
+ auto windowSize = _window->geometry().size();
+ uvec2 readFboSize;
+ uint32_t readFbo{ 0 };
+
+ if (_activeFrame) {
+ const auto &frame = _activeFrame;
+ _backend->recycle();
+ _backend->syncCache();
+ _gpuContext->enableStereo(frame->stereoState._enable);
+ if (frame && !frame->batches.empty()) {
+ _gpuContext->executeFrame(frame);
+ }
+ auto &glBackend = static_cast(*_backend);
+ readFbo = glBackend.getFramebufferID(frame->framebuffer);
+ readFboSize = frame->framebuffer->getSize();
+ CHECK_GL_ERROR();
+ } else {
+ hifi::qml::OffscreenSurface::TextureAndFence newTextureAndFence;
+ if (_offscreen->fetchTexture(newTextureAndFence)) {
+ if (_uiTexture != 0) {
+ auto readFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ glFlush();
+ _offscreen->getDiscardLambda()(_uiTexture, readFence);
+ _uiTexture = 0;
+ }
+
+ glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
+ glDeleteSync((GLsync)newTextureAndFence.second);
+ _uiTexture = newTextureAndFence.first;
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, _uiFbo);
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _uiTexture, 0);
+ }
+
+ if (_uiTexture != 0) {
+ readFbo = _uiFbo;
+ readFboSize = { windowSize.width(), windowSize.height() };
+ }
+ }
+
+ if (readFbo) {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, readFbo);
+ glBlitFramebuffer(
+ 0, 0, readFboSize.x, readFboSize.y,
+ 0, 0, windowSize.width(), windowSize.height(),
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ } else {
+ glClearColor(1, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ _glContext.swapBuffers();
+}
+
+void RenderThread::updateFrame() {
+ std::queue pendingFrames;
+ {
+ std::unique_lock lock(_frameLock);
+ pendingFrames.swap(_pendingFrames);
+ }
+
+ while (!pendingFrames.empty()) {
+ _activeFrame = pendingFrames.front();
+ pendingFrames.pop();
+ if (_activeFrame) {
+ _gpuContext->consumeFrameUpdates(_activeFrame);
+ }
+ }
+}
+
+bool RenderThread::process() {
+ updateFrame();
+ makeCurrent();
+ renderFrame();
+ return true;
+}
diff --git a/android/apps/framePlayer/src/main/cpp/RenderThread.h b/android/apps/framePlayer/src/main/cpp/RenderThread.h
new file mode 100644
index 0000000000..c514874724
--- /dev/null
+++ b/android/apps/framePlayer/src/main/cpp/RenderThread.h
@@ -0,0 +1,54 @@
+//
+// Created by Bradley Austin Davis on 2018/10/21
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#pragma once
+
+#include
+
+#include
+#include
+#include
+
+class RenderThread : public GenericThread {
+ using Parent = GenericThread;
+public:
+ QWindow* _window{ nullptr };
+ std::mutex _mutex;
+ gpu::ContextPointer _gpuContext; // initialized during window creation
+ std::shared_ptr _backend;
+ std::atomic _presentCount{ 0 };
+ std::mutex _frameLock;
+ std::queue _pendingFrames;
+ gpu::FramePointer _activeFrame;
+ uint32_t _externalTexture{ 0 };
+ glm::mat4 _correction;
+ hifi::qml::OffscreenSurface* _offscreen{ nullptr };
+
+ gl::Context _glContext;
+ uint32_t _uiTexture{ 0 };
+ uint32_t _uiFbo{ 0 };
+
+ void move(const glm::vec3& v);
+ void setup() override;
+ bool process() override;
+ void shutdown() override;
+
+ void initialize(QWindow* window, hifi::qml::OffscreenSurface* offscreen);
+
+ void submitFrame(const gpu::FramePointer& frame);
+ void updateFrame();
+ void renderFrame();
+
+ bool makeCurrent() {
+ return _glContext.makeCurrent();
+ }
+
+ void doneCurrent() {
+ _glContext.doneCurrent();
+ }
+};
diff --git a/android/apps/framePlayer/src/main/cpp/main.cpp b/android/apps/framePlayer/src/main/cpp/main.cpp
new file mode 100644
index 0000000000..3843583e5e
--- /dev/null
+++ b/android/apps/framePlayer/src/main/cpp/main.cpp
@@ -0,0 +1,54 @@
+//
+// Created by Bradley Austin Davis on 2018/11/22
+// 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 "PlayerWindow.h"
+
+void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
+ if (!message.isEmpty()) {
+ const char * local=message.toStdString().c_str();
+ switch (type) {
+ case QtDebugMsg:
+ __android_log_write(ANDROID_LOG_DEBUG,"Interface",local);
+ break;
+ case QtInfoMsg:
+ __android_log_write(ANDROID_LOG_INFO,"Interface",local);
+ break;
+ case QtWarningMsg:
+ __android_log_write(ANDROID_LOG_WARN,"Interface",local);
+ break;
+ case QtCriticalMsg:
+ __android_log_write(ANDROID_LOG_ERROR,"Interface",local);
+ break;
+ case QtFatalMsg:
+ default:
+ __android_log_write(ANDROID_LOG_FATAL,"Interface",local);
+ abort();
+ }
+ }
+}
+
+int main(int argc, char** argv) {
+ setupHifiApplication("gpuFramePlayer");
+ QGuiApplication app(argc, argv);
+ auto oldMessageHandler = qInstallMessageHandler(messageHandler);
+ DependencyManager::set();
+ PlayerWindow window;
+ app.exec();
+ qInstallMessageHandler(oldMessageHandler);
+ return 0;
+}
+
+
diff --git a/android/apps/framePlayer/src/main/cpp/qml/main.qml b/android/apps/framePlayer/src/main/cpp/qml/main.qml
new file mode 100644
index 0000000000..34c4507f9d
--- /dev/null
+++ b/android/apps/framePlayer/src/main/cpp/qml/main.qml
@@ -0,0 +1,36 @@
+import QtQuick 2.2
+import QtQuick.Dialogs 1.1
+import Qt.labs.folderlistmodel 2.11
+
+Item {
+ id: root
+ width: 640
+ height: 480
+
+ ListView {
+ anchors.fill: parent
+
+ FolderListModel {
+ id: folderModel
+ folder: FRAMES_FOLDER
+ nameFilters: ["*.json"]
+ }
+
+ Component {
+ id: fileDelegate
+ Text {
+ text: fileName
+ font.pointSize: 36
+ MouseArea {
+ anchors.fill: parent
+ onClicked: root.loadFile(folderModel.folder + "/" + fileName);
+ }
+ }
+ }
+
+ model: folderModel
+ delegate: fileDelegate
+ }
+
+ signal loadFile(string filename);
+}
diff --git a/android/apps/framePlayer/src/main/res/drawable/ic_launcher.xml b/android/apps/framePlayer/src/main/res/drawable/ic_launcher.xml
new file mode 100644
index 0000000000..03b1edc4e9
--- /dev/null
+++ b/android/apps/framePlayer/src/main/res/drawable/ic_launcher.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/apps/framePlayer/src/main/res/values/strings.xml b/android/apps/framePlayer/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..8bf550f74e
--- /dev/null
+++ b/android/apps/framePlayer/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ GPU Frame Player
+
diff --git a/android/settings.gradle b/android/settings.gradle
index 40b5eb44bf..1e7b3c768a 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -3,3 +3,6 @@ project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
include ':interface'
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
+
+//include ':framePlayer'
+//project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
diff --git a/cmake/macros/TargetGlad.cmake b/cmake/macros/TargetGlad.cmake
index 929f61c3f2..c9a2529986 100644
--- a/cmake/macros/TargetGlad.cmake
+++ b/cmake/macros/TargetGlad.cmake
@@ -7,6 +7,7 @@
#
macro(TARGET_GLAD)
if (ANDROID)
+ include(SelectLibraryConfigurations)
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/glad)
set(GLAD_INCLUDE_DIRS "${INSTALL_DIR}/include")
set(GLAD_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libglad_d.a)
@@ -31,8 +32,8 @@ macro(TARGET_GLAD)
set(GLAD_INCLUDE_DIRS ${${GLAD_UPPER}_INCLUDE_DIRS})
set(GLAD_LIBRARY ${${GLAD_UPPER}_LIBRARY})
endif()
-
+
target_include_directories(${TARGET_NAME} PUBLIC ${GLAD_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${GLAD_LIBRARY})
- target_link_libraries(${TARGET_NAME} ${GLAD_EXTRA_LIBRARIES})
+ target_link_libraries(${TARGET_NAME} ${GLAD_EXTRA_LIBRARIES})
endmacro()
diff --git a/cmake/macros/TargetZlib.cmake b/cmake/macros/TargetZlib.cmake
index 79dce01a3d..172e5bd13b 100644
--- a/cmake/macros/TargetZlib.cmake
+++ b/cmake/macros/TargetZlib.cmake
@@ -6,8 +6,13 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_ZLIB)
- # using VCPKG for zlib
- find_package(ZLIB REQUIRED)
- target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS})
- target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
+ if (ANDROID)
+ # zlib is part of the NDK
+ target_link_libraries(${TARGET_NAME} z)
+ else()
+ # using VCPKG for zlib
+ find_package(ZLIB REQUIRED)
+ target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS})
+ target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
+ endif()
endmacro()
diff --git a/cmake/ports/nvtt/CONTROL b/cmake/ports/nvtt/CONTROL
index 59ba36d830..94d6193116 100644
--- a/cmake/ports/nvtt/CONTROL
+++ b/cmake/ports/nvtt/CONTROL
@@ -1,3 +1,3 @@
Source: nvtt
-Version: 8c7e6b40ee5095f227b75880fabd89c99d6f34c0
+Version: 330c4d56274a0f602a5c70596e2eb670a4ed56c2
Description: Texture processing tools with support for Direct3D 10 and 11 formats.
\ No newline at end of file
diff --git a/cmake/ports/nvtt/portfile.cmake b/cmake/ports/nvtt/portfile.cmake
index 13b1253322..4cbe05b692 100644
--- a/cmake/ports/nvtt/portfile.cmake
+++ b/cmake/ports/nvtt/portfile.cmake
@@ -10,8 +10,8 @@ include(vcpkg_common_functions)
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO highfidelity/nvidia-texture-tools
- REF 8c7e6b40ee5095f227b75880fabd89c99d6f34c0
- SHA512 f107d19dbbd6651ef2126b1422a5db8db291bf70311ac4fb1dbacb5ceaa8752fee38becbd32964f57596f0b84e1223bb2c3ff9d9c4fdc65c3e77a47836657cef
+ REF 330c4d56274a0f602a5c70596e2eb670a4ed56c2
+ SHA512 4c0bc2f369120d696cc27710b6d33086b27eef55f537ec66b9a5c8b1839bc2426c0413670b0f65be52c5d353468f0126dfe024be1f0690611d4d7e33ac530127
HEAD_REF master
)
diff --git a/interface/resources/qml/androidAppForce/ForFolderListModel.qml b/interface/resources/qml/androidAppForce/ForFolderListModel.qml
new file mode 100644
index 0000000000..3fba3f11fa
--- /dev/null
+++ b/interface/resources/qml/androidAppForce/ForFolderListModel.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.2
+import QtQuick.Dialogs 1.1
+import Qt.labs.folderlistmodel 2.11
+
+Item {
+ width: 640
+ height: 480
+
+ ListView {
+ width: 200; height: 400
+
+ FolderListModel {
+ id: folderModel
+ folder: "assets:/frames/"
+ nameFilters: ["*.json"]
+ }
+
+ Component {
+ id: fileDelegate
+ Text { text: fileName }
+ }
+
+ model: folderModel
+ delegate: fileDelegate
+ }
+}
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 66f39a926a..bad1d40be9 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -3551,8 +3551,10 @@ void Application::resizeGL() {
auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration();
assert(renderConfig);
auto mainView = renderConfig->getConfig("RenderMainView.RenderDeferredTask");
- assert(mainView);
- mainView->setProperty("resolutionScale", renderResolutionScale);
+ // mainView can be null if we're rendering in forward mode
+ if (mainView) {
+ mainView->setProperty("resolutionScale", renderResolutionScale);
+ }
displayPlugin->setRenderResolutionScale(renderResolutionScale);
}
@@ -4100,6 +4102,19 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
+ case Qt::Key_G:
+ if (isShifted && isMeta && Menu::getInstance() && Menu::getInstance()->getMenu("Developer")->isVisible()) {
+ static const QString HIFI_FRAMES_FOLDER_VAR = "HIFI_FRAMES_FOLDER";
+ static const QString GPU_FRAME_FOLDER = QProcessEnvironment::systemEnvironment().contains(HIFI_FRAMES_FOLDER_VAR)
+ ? QProcessEnvironment::systemEnvironment().value(HIFI_FRAMES_FOLDER_VAR)
+ : "hifiFrames";
+ static QString GPU_FRAME_TEMPLATE = GPU_FRAME_FOLDER + "/{DATE}_{TIME}";
+ QString fullPath = FileUtils::computeDocumentPath(FileUtils::replaceDateTimeTokens(GPU_FRAME_TEMPLATE));
+ if (FileUtils::canCreateFile(fullPath)) {
+ getActiveDisplayPlugin()->captureFrame(fullPath.toStdString());
+ }
+ }
+ break;
case Qt::Key_X:
if (isShifted && isMeta) {
auto offscreenUi = getOffscreenUI();
diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp
index dd99907b8e..5fb9c9a0ee 100644
--- a/interface/src/avatar/AvatarActionHold.cpp
+++ b/interface/src/avatar/AvatarActionHold.cpp
@@ -288,39 +288,37 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) {
glm::vec3 oneFrameVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep;
_measuredLinearVelocities[_measuredLinearVelocitiesIndex++] = oneFrameVelocity;
- if (_measuredLinearVelocitiesIndex >= AvatarActionHold::velocitySmoothFrames) {
- _measuredLinearVelocitiesIndex = 0;
+ _measuredLinearVelocitiesIndex %= AvatarActionHold::velocitySmoothFrames;
+ }
+
+ if (_kinematicSetVelocity) {
+ glm::vec3 measuredLinearVelocity = _measuredLinearVelocities[0];
+ for (int i = 1; i < AvatarActionHold::velocitySmoothFrames; i++) {
+ // there is a bit of lag between when someone releases the trigger and when the software reacts to
+ // the release. we calculate the velocity from previous frames but we don't include several
+ // of the most recent.
+ //
+ // if _measuredLinearVelocitiesIndex is
+ // 0 -- ignore i of 3 4 5
+ // 1 -- ignore i of 4 5 0
+ // 2 -- ignore i of 5 0 1
+ // 3 -- ignore i of 0 1 2
+ // 4 -- ignore i of 1 2 3
+ // 5 -- ignore i of 2 3 4
+
+ // This code is now disabled, but I'm leaving it commented-out because I suspect it will come back.
+ // if ((i + 1) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
+ // (i + 2) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
+ // (i + 3) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex) {
+ // continue;
+ // }
+
+ measuredLinearVelocity += _measuredLinearVelocities[i];
}
- }
-
- glm::vec3 measuredLinearVelocity;
- for (int i = 0; i < AvatarActionHold::velocitySmoothFrames; i++) {
- // there is a bit of lag between when someone releases the trigger and when the software reacts to
- // the release. we calculate the velocity from previous frames but we don't include several
- // of the most recent.
- //
- // if _measuredLinearVelocitiesIndex is
- // 0 -- ignore i of 3 4 5
- // 1 -- ignore i of 4 5 0
- // 2 -- ignore i of 5 0 1
- // 3 -- ignore i of 0 1 2
- // 4 -- ignore i of 1 2 3
- // 5 -- ignore i of 2 3 4
-
- // This code is now disabled, but I'm leaving it commented-out because I suspect it will come back.
- // if ((i + 1) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
- // (i + 2) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
- // (i + 3) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex) {
- // continue;
- // }
-
- measuredLinearVelocity += _measuredLinearVelocities[i];
- }
- measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames
+ measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames
// - 3 // 3 because of the 3 we skipped, above
);
- if (_kinematicSetVelocity) {
rigidBody->setLinearVelocity(glmToBullet(measuredLinearVelocity));
rigidBody->setAngularVelocity(glmToBullet(_angularVelocityTarget));
}
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index d883aafe2c..1eb87c16f0 100755
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -475,6 +475,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
_spaceProxiesToDelete.push_back(avatar->getSpaceIndex());
}
AvatarHashMap::handleRemovedAvatar(avatar, removalReason);
+ avatar->tearDownGrabs();
avatar->die();
queuePhysicsChange(avatar);
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 6b89ba9687..92d9270d20 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -866,7 +866,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
// and all of its joints, now update our attachements.
Avatar::simulateAttachments(deltaTime);
relayJointDataToChildren();
- if (updateGrabs()) {
+ if (applyGrabChanges()) {
_cauterizationNeedsUpdate = true;
}
@@ -5301,7 +5301,7 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
_avatarGrabsLock.withWriteLock([&] {
if (_avatarGrabData.remove(grabID)) {
- _deletedAvatarGrabs.insert(grabID);
+ _grabsToDelete.push_back(grabID);
tellHandler = true;
}
});
diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp
index 7ff6a294d3..a3950c8e96 100755
--- a/interface/src/avatar/OtherAvatar.cpp
+++ b/interface/src/avatar/OtherAvatar.cpp
@@ -363,7 +363,7 @@ void OtherAvatar::simulate(float deltaTime, bool inView) {
{
PROFILE_RANGE(simulation, "grabs");
- updateGrabs();
+ applyGrabChanges();
}
updateFadingStatus();
diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
index 0ecd9e9fce..07c1ca9a32 100644
--- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
+++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
@@ -326,88 +326,79 @@ void Avatar::removeAvatarEntitiesFromTree() {
}
}
-bool Avatar::updateGrabs() {
+bool Avatar::applyGrabChanges() {
+ if (!_avatarGrabDataChanged && _grabsToChange.empty() && _grabsToDelete.empty()) {
+ // early exit for most common case: nothing to do
+ return false;
+ }
+
bool grabAddedOrRemoved = false;
- // update the Grabs according to any changes in _avatarGrabData
_avatarGrabsLock.withWriteLock([&] {
if (_avatarGrabDataChanged) {
+ // collect changes in _avatarGrabData
foreach (auto grabID, _avatarGrabData.keys()) {
- AvatarGrabMap::iterator grabItr = _avatarGrabs.find(grabID);
- if (grabItr == _avatarGrabs.end()) {
+ MapOfGrabs::iterator itr = _avatarGrabs.find(grabID);
+ if (itr == _avatarGrabs.end()) {
GrabPointer grab = std::make_shared();
grab->fromByteArray(_avatarGrabData.value(grabID));
_avatarGrabs[grabID] = grab;
- _changedAvatarGrabs.insert(grabID);
+ _grabsToChange.insert(grabID);
} else {
- GrabPointer grab = grabItr.value();
- bool changed = grab->fromByteArray(_avatarGrabData.value(grabID));
+ bool changed = itr->second->fromByteArray(_avatarGrabData.value(grabID));
if (changed) {
- _changedAvatarGrabs.insert(grabID);
+ _grabsToChange.insert(grabID);
}
}
}
_avatarGrabDataChanged = false;
}
- auto treeRenderer = DependencyManager::get();
- auto entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
- EntityEditPacketSender* packetSender = treeRenderer ? treeRenderer->getPacketSender() : nullptr;
- auto sessionID = DependencyManager::get()->getSessionUUID();
-
- QMutableSetIterator delItr(_deletedAvatarGrabs);
- while (delItr.hasNext()) {
- QUuid grabID = delItr.next();
- GrabPointer grab = _avatarGrabs[grabID];
- if (!grab) {
- delItr.remove();
+ // delete _avatarGrabs
+ VectorOfIDs undeleted;
+ for (const auto& id : _grabsToDelete) {
+ MapOfGrabs::iterator itr = _avatarGrabs.find(id);
+ if (itr == _avatarGrabs.end()) {
continue;
}
bool success;
+ const GrabPointer& grab = itr->second;
SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
-
- // only clear this entry from the _deletedAvatarGrabs if we found the entity.
if (success && target) {
- bool iShouldTellServer = target->getEditSenderID() == sessionID;
-
- EntityItemPointer entity = std::dynamic_pointer_cast(target);
- if (entity && entity->isAvatarEntity() && (entity->getOwningAvatarID() == sessionID ||
- entity->getOwningAvatarID() == AVATAR_SELF_ID)) {
- // this is our own avatar-entity, so we always tell the server about the release
- iShouldTellServer = true;
- }
-
target->removeGrab(grab);
- delItr.remove();
- // in case this is the last grab on an entity, we need to shrink the queryAACube and tell the server
- // about the final position.
- if (entityTree) {
- bool force = true;
- entityTree->withWriteLock([&] {
- entityTree->updateEntityQueryAACube(target, packetSender, force, iShouldTellServer);
- });
- }
+ _avatarGrabs.erase(itr);
grabAddedOrRemoved = true;
+ } else {
+ undeleted.push_back(id);
}
- _avatarGrabs.remove(grabID);
- _changedAvatarGrabs.remove(grabID);
}
+ _grabsToDelete = std::move(undeleted);
- QMutableSetIterator changeItr(_changedAvatarGrabs);
- while (changeItr.hasNext()) {
- QUuid grabID = changeItr.next();
- GrabPointer& grab = _avatarGrabs[grabID];
+ // change _avatarGrabs and add Actions to target
+ SetOfIDs unchanged;
+ for (const auto& id : _grabsToChange) {
+ MapOfGrabs::iterator itr = _avatarGrabs.find(id);
+ if (itr == _avatarGrabs.end()) {
+ continue;
+ }
bool success;
+ const GrabPointer& grab = itr->second;
SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
-
if (success && target) {
target->addGrab(grab);
- // only clear this entry from the _changedAvatarGrabs if we found the entity.
- changeItr.remove();
+ if (isMyAvatar()) {
+ EntityItemPointer entity = std::dynamic_pointer_cast(target);
+ if (entity) {
+ entity->upgradeScriptSimulationPriority(PERSONAL_SIMULATION_PRIORITY);
+ }
+ }
grabAddedOrRemoved = true;
+ } else {
+ unchanged.insert(id);
}
}
+ _grabsToChange = std::move(unchanged);
});
return grabAddedOrRemoved;
}
@@ -415,8 +406,8 @@ bool Avatar::updateGrabs() {
void Avatar::accumulateGrabPositions(std::map& grabAccumulators) {
// relay avatar's joint position to grabbed target in a way that allows for averaging
_avatarGrabsLock.withReadLock([&] {
- foreach (auto grabID, _avatarGrabs.keys()) {
- const GrabPointer& grab = _avatarGrabs.value(grabID);
+ for (const auto& entry : _avatarGrabs) {
+ const GrabPointer& grab = entry.second;
if (!grab || !grab->getActionID().isNull()) {
continue; // the accumulated value isn't used, in this case.
@@ -434,6 +425,20 @@ void Avatar::accumulateGrabPositions(std::map& g
});
}
+void Avatar::tearDownGrabs() {
+ _avatarGrabsLock.withWriteLock([&] {
+ for (const auto& entry : _avatarGrabs) {
+ _grabsToDelete.push_back(entry.first);
+ }
+ _grabsToChange.clear();
+ });
+ applyGrabChanges();
+ if (!_grabsToDelete.empty()) {
+ // some grabs failed to delete, which is a possible "leak", so we log about it
+ qWarning() << "Failed to tearDown" << _grabsToDelete.size() << "grabs for Avatar" << getID();
+ }
+}
+
void Avatar::relayJointDataToChildren() {
forEachChild([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
@@ -2063,3 +2068,12 @@ scriptable::ScriptableModelBase Avatar::getScriptableModel() {
}
return result;
}
+
+void Avatar::clearAvatarGrabData(const QUuid& id) {
+ AvatarData::clearAvatarGrabData(id);
+ _avatarGrabsLock.withWriteLock([&] {
+ if (_avatarGrabs.find(id) != _avatarGrabs.end()) {
+ _grabsToDelete.push_back(id);
+ }
+ });
+}
diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h
index 7abd6ebae7..b43fe012b7 100644
--- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h
+++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h
@@ -14,6 +14,9 @@
#include
#include
#include
+#include