diff --git a/.gitignore b/.gitignore index dfffe2ac51..f45572c388 100644 --- a/.gitignore +++ b/.gitignore @@ -77,3 +77,12 @@ TAGS # ignore node files for the console node_modules npm-debug.log + +# Android studio files +*___jb_old___ + +# Generated assets for Android +android/app/src/main/assets + +# Resource binary file +interface/compiledResources \ No newline at end of file diff --git a/BUILD_LINUX_CHEATSHEET.md b/BUILD_LINUX_CHEATSHEET.md index 7d77f5d685..9e7534418a 100644 --- a/BUILD_LINUX_CHEATSHEET.md +++ b/BUILD_LINUX_CHEATSHEET.md @@ -1,5 +1,7 @@ - # this guide is specific to Ubuntu 16.04. - # deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com +## This guide is specific to Ubuntu 16.04. +Deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com + +``` sudo su - apt-get -y update apt-get install -y software-properties-common @@ -8,20 +10,27 @@ add-apt-repository "deb http://debian.highfidelity.com stable main" apt-get -y update apt-get install -y hifi-domain-server apt-get install -y hifi-assignment-client +``` - # When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to: +When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to: +``` apt-get install -y hifi-dev-domain-server apt-get install -y hifi-dev-assignment-client +``` - # domain server and assignment clients should already be running. The processes are controlled via: +Domain server and assignment clients should already be running. The processes are controlled via: +``` systemctl start hifi-domain-server systemctl stop hifi-domain-server +``` - # Once the machine is setup and processes are running you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (further customizations can be done via http://IPAddress:40100). - - # The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time. - # As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly). +Once the machine is setup and processes are running, you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (Further customizations can be done via http://IPAddress:40100). + +The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time. +As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly). To do this you can modify /etc/crontab by adding the following lines +``` 0 */1 * * * root apt-get update 1 */1 * * * root apt-get install --only-upgrade -y hifi-domain-server 2 */1 * * * root apt-get install --only-upgrade -y hifi-assignment-client +``` diff --git a/CMakeLists.txt b/CMakeLists.txt index 34ff672067..bbec4a0884 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,9 +26,11 @@ endif() if (ANDROID OR UWP) option(BUILD_SERVER "Build server components" OFF) option(BUILD_TOOLS "Build tools" OFF) + option(BUILD_INSTALLER "Build installer" OFF) else() option(BUILD_SERVER "Build server components" ON) option(BUILD_TOOLS "Build tools" ON) + option(BUILD_INSTALLER "Build installer" ON) endif() if (SERVER_ONLY) @@ -40,25 +42,53 @@ else() endif() if (ANDROID) - set(PLATFORM_QT_COMPONENTS AndroidExtras WebView) - set(PLATFORM_GL_BACKEND gpu-gles) + option(USE_GLES "Use OpenGL ES" ON) + set(PLATFORM_QT_COMPONENTS AndroidExtras WebView) else () - set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) - set(PLATFORM_GL_BACKEND gpu-gl) + option(USE_GLES "Use OpenGL ES" OFF) + set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) endif () +if (USE_GLES AND (NOT ANDROID)) + option(DISABLE_QML "Disable QML" ON) +else() + option(DISABLE_QML "Disable QML" OFF) +endif() +option(DISABLE_KTX_CACHE "Disable KTX Cache" OFF) + + + +set(PLATFORM_QT_GL OpenGL) + +if (USE_GLES) + add_definitions(-DUSE_GLES) + set(PLATFORM_GL_BACKEND gpu-gles) +else() + set(PLATFORM_GL_BACKEND gpu-gl) +endif() + + foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS}) list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}") endforeach() -option(BUILD_INSTALLER "Build installer" ON) - MESSAGE(STATUS "Build server: " ${BUILD_SERVER}) MESSAGE(STATUS "Build client: " ${BUILD_CLIENT}) MESSAGE(STATUS "Build tests: " ${BUILD_TESTS}) MESSAGE(STATUS "Build tools: " ${BUILD_TOOLS}) MESSAGE(STATUS "Build installer: " ${BUILD_INSTALLER}) +MESSAGE(STATUS "GL ES: " ${USE_GLES}) + +if (DISABLE_QML) +MESSAGE(STATUS "QML disabled!") +add_definitions(-DDISABLE_QML) +endif() + +if (DISABLE_KTX_CACHE) +MESSAGE(STATUS "KTX cache disabled!") +add_definitions(-DDISABLE_KTX_CACHE) +endif() if (UNIX AND DEFINED ENV{HIFI_MEMORY_DEBUGGING}) MESSAGE(STATUS "Memory debugging is enabled") diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt index 5682611151..4c50cd8609 100644 --- a/android/app/CMakeLists.txt +++ b/android/app/CMakeLists.txt @@ -1,26 +1,28 @@ set(TARGET_NAME native-lib) -setup_hifi_library(Gui Qml Quick) - -# Minimal dependencies for testing UI compositing -#link_hifi_libraries(shared networking gl ui) - -link_hifi_libraries( - shared networking octree - script-engine recording trackers - gl ktx image gpu gpu-gles render render-utils - physics - audio audio-client - ui midi controllers pointers - graphics model-networking fbx animation - entities entities-renderer - avatars avatars-renderer - ui-plugins input-plugins - # display-plugins - # auto-updater -) - - -target_link_libraries(native-lib android log m) +setup_hifi_library() +link_hifi_libraries(shared networking gl gpu image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND}) target_opengl() target_bullet() +set(INTERFACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../interface") +add_subdirectory("${INTERFACE_DIR}" "libraries/interface") +include_directories("${INTERFACE_DIR}/src") + +target_link_libraries(native-lib android log m interface) + +set(GVR_ROOT "${HIFI_ANDROID_PRECOMPILED}/gvr/gvr-android-sdk-1.101.0/") +target_include_directories(native-lib PRIVATE "${GVR_ROOT}/libraries/headers" "libraries/ui/src") +target_link_libraries(native-lib "${GVR_ROOT}/libraries/libgvr.so" ui) + +# finished libraries +# core -> qt +# networking -> openssl, tbb +# fbx -> draco +# physics -> bullet +# entities-renderer -> polyvox + +# unfinished libraries +# image -> nvtt (doesn't look good, but can be made optional) +# script-engine -> quazip (probably not required for the android client) + + diff --git a/android/app/build.gradle b/android/app/build.gradle index 066eb7da3d..1cff708c3b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,10 +1,11 @@ apply plugin: 'com.android.application' - android { compileSdkVersion 26 + //buildToolsVersion '27.0.3' + defaultConfig { - applicationId "com.highfidelity.iface" + applicationId "io.highfidelity.hifiinterface" minSdkVersion 24 targetSdkVersion 26 versionCode 1 @@ -21,24 +22,19 @@ android { '-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED, '-DRELEASE_NUMBER=' + RELEASE_NUMBER, '-DRELEASE_TYPE=' + RELEASE_TYPE, - '-DBUILD_BRANCH=' + BUILD_BRANCH + '-DBUILD_BRANCH=' + BUILD_BRANCH, + '-DDISABLE_QML=ON', + '-DDISABLE_KTX_CACHE=ON' } } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } } - buildTypes { - applicationVariants.all { variant -> - variant.outputs.all { - if (RELEASE_NUMBER != '0') { - outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk" - } - } - } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' @@ -50,11 +46,58 @@ android { path '../../CMakeLists.txt' } } + + applicationVariants.all { variant -> + // Our asset contents depend on items produced in the CMake build + // so our merge has to depend on the external native build + variant.externalNativeBuildTasks.each { task -> + variant.mergeResources.dependsOn(task) + } + + variant.mergeAssets.doLast { + def assetList = new LinkedList() + def youngestLastModified = 0 + + // Copy the compiled resources generated by the external native build + copy { + from new File(projectDir, "../../interface/compiledResources") + into outputDir + duplicatesStrategy DuplicatesStrategy.INCLUDE + eachFile { details -> + youngestLastModified = Math.max(youngestLastModified, details.lastModified) + assetList.add(details.path) + } + } + + // Copy the scripts directory + copy { + from new File(projectDir, "../../scripts") + into new File(outputDir, "scripts") + duplicatesStrategy DuplicatesStrategy.INCLUDE + eachFile { details-> + youngestLastModified = Math.max(youngestLastModified, details.lastModified) + assetList.add("scripts/" + details.path) + } + } + + // Write a list of files to be unpacked to the cache folder + new File(outputDir, 'cache_assets.txt').withWriter { out -> + out.println(Long.toString(youngestLastModified)) + assetList.each { file -> out.println(file) } + } + } + + variant.outputs.all { + if (RELEASE_NUMBER != '0') { + outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk" + } + } + } } dependencies { implementation 'com.google.vr:sdk-audio:1.80.0' implementation 'com.google.vr:sdk-base:1.80.0' - implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation fileTree(include: ['*.jar'], dir: 'libs') } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 2160f7b591..efb7abbb93 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - + + @@ -7,11 +8,13 @@ + - + + + + + + + + + android:launchMode="singleTop" + android:enableVrMode="com.google.vr.vrcore/com.google.vr.vrcore.common.VrCoreListenerService" + > - - + + @@ -43,4 +62,8 @@ - \ No newline at end of file + + + + + diff --git a/android/app/src/main/cpp/+android/simple.qml b/android/app/src/main/cpp/+android/simple.qml deleted file mode 100644 index fc722d7e2c..0000000000 --- a/android/app/src/main/cpp/+android/simple.qml +++ /dev/null @@ -1,19 +0,0 @@ -import QtQuick 2.7 -import QtWebView 1.1 - -Rectangle { - id: window - anchors.fill: parent - color: "red" - ColorAnimation on color { from: "blue"; to: "yellow"; duration: 1000; loops: Animation.Infinite } - - Text { - text: "Hello" - anchors.top: parent.top - } - WebView { - anchors.fill: parent - anchors.margins: 10 - url: "http://doc.qt.io/qt-5/qml-qtwebview-webview.html" - } -} diff --git a/android/app/src/main/cpp/main.cpp b/android/app/src/main/cpp/main.cpp deleted file mode 100644 index b947323720..0000000000 --- a/android/app/src/main/cpp/main.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -Q_LOGGING_CATEGORY(gpugllogging, "hifi.gl") - -bool checkGLError(const char* name) { - GLenum error = glGetError(); - if (!error) { - return false; - } else { - switch (error) { - case GL_INVALID_ENUM: - qCDebug(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; - break; - case GL_INVALID_VALUE: - qCDebug(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; - break; - case GL_INVALID_OPERATION: - qCDebug(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - qCDebug(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; - break; - case GL_OUT_OF_MEMORY: - qCDebug(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; - break; - default: - qCDebug(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error; - break; - } - return true; - } -} - -bool checkGLErrorDebug(const char* name) { - return checkGLError(name); -} - - -int QtMsgTypeToAndroidPriority(QtMsgType type) { - int priority = ANDROID_LOG_UNKNOWN; - switch (type) { - case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break; - case QtWarningMsg: priority = ANDROID_LOG_WARN; break; - case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break; - case QtFatalMsg: priority = ANDROID_LOG_FATAL; break; - case QtInfoMsg: priority = ANDROID_LOG_INFO; break; - default: break; - } - return priority; -} - -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - __android_log_write(QtMsgTypeToAndroidPriority(type), "Interface", message.toStdString().c_str()); -} - -void qt_gl_set_global_share_context(QOpenGLContext *context); - -int main(int argc, char* argv[]) -{ - qInstallMessageHandler(messageHandler); - QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); - QGuiApplication app(argc,argv); - app.setOrganizationName("QtProject"); - app.setOrganizationDomain("qt-project.org"); - app.setApplicationName(QFileInfo(app.applicationFilePath()).baseName()); - QtWebView::initialize(); - qputenv("QSG_RENDERER_DEBUG", (QStringList() << "render" << "build" << "change" << "upload" << "roots" << "dump").join(';').toUtf8()); - - OffscreenGLCanvas sharedCanvas; - if (!sharedCanvas.create()) { - qFatal("Unable to create primary offscreen context"); - } - qt_gl_set_global_share_context(sharedCanvas.getContext()); - auto globalContext = QOpenGLContext::globalShareContext(); - - GLWindow window; - window.create(); - window.setGeometry(qApp->primaryScreen()->availableGeometry()); - window.createContext(globalContext); - if (!window.makeCurrent()) { - qFatal("Unable to make primary window GL context current"); - } - - GLuint fbo = 0; - glGenFramebuffers(1, &fbo); - - static const ivec2 offscreenSize { 640, 480 }; - - OffscreenQmlSurface::setSharedContext(sharedCanvas.getContext()); - OffscreenQmlSurface* qmlSurface = new OffscreenQmlSurface(); - qmlSurface->create(); - qmlSurface->resize(fromGlm(offscreenSize)); - qmlSurface->load("qrc:///simple.qml"); - qmlSurface->resume(); - - auto discardLambda = qmlSurface->getDiscardLambda(); - - window.showFullScreen(); - QTimer timer; - timer.setInterval(10); - timer.setSingleShot(false); - OffscreenQmlSurface::TextureAndFence currentTextureAndFence; - timer.connect(&timer, &QTimer::timeout, &app, [&]{ - window.makeCurrent(); - glClearColor(0, 1, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - - OffscreenQmlSurface::TextureAndFence newTextureAndFence; - if (qmlSurface->fetchTexture(newTextureAndFence)) { - if (currentTextureAndFence.first) { - discardLambda(currentTextureAndFence.first, currentTextureAndFence.second); - } - currentTextureAndFence = newTextureAndFence; - } - checkGLErrorDebug(__FUNCTION__); - - if (currentTextureAndFence.second) { - glWaitSync((GLsync)currentTextureAndFence.second, 0, GL_TIMEOUT_IGNORED); - glDeleteSync((GLsync)currentTextureAndFence.second); - currentTextureAndFence.second = nullptr; - } - - if (currentTextureAndFence.first) { - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, currentTextureAndFence.first, 0); - glBlitFramebuffer(0, 0, offscreenSize.x, offscreenSize.y, 100, 100, offscreenSize.x + 100, offscreenSize.y + 100, GL_COLOR_BUFFER_BIT, GL_NEAREST); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - } - - window.swapBuffers(); - window.doneCurrent(); - }); - timer.start(); - return app.exec(); -} diff --git a/android/app/src/main/cpp/main.qrc b/android/app/src/main/cpp/main.qrc deleted file mode 100644 index 81cf8ef111..0000000000 --- a/android/app/src/main/cpp/main.qrc +++ /dev/null @@ -1,6 +0,0 @@ - - - simple.qml - +android/simple.qml - - diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp new file mode 100644 index 0000000000..13daf4c471 --- /dev/null +++ b/android/app/src/main/cpp/native.cpp @@ -0,0 +1,160 @@ +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +void tempMessageHandler(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(); + } + } +} + +AAssetManager* g_assetManager = nullptr; + +void withAssetData(const char* filename, const std::function& callback) { + auto asset = AAssetManager_open(g_assetManager, filename, AASSET_MODE_BUFFER); + if (!asset) { + throw std::runtime_error("Failed to open file"); + } + auto buffer = AAsset_getBuffer(asset); + off64_t size = AAsset_getLength64(asset); + callback(size, buffer); + AAsset_close(asset); +} + +QStringList readAssetLines(const char* filename) { + QStringList result; + withAssetData(filename, [&](off64_t size, const void* data){ + QByteArray buffer = QByteArray::fromRawData((const char*)data, size); + { + QTextStream in(&buffer); + while (!in.atEnd()) { + QString line = in.readLine(); + result << line; + } + } + }); + return result; +} + +void copyAsset(const char* sourceAsset, const QString& destFilename) { + withAssetData(sourceAsset, [&](off64_t size, const void* data){ + QFile file(destFilename); + if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { + throw std::runtime_error("Unable to open output file for writing"); + } + if (!file.resize(size)) { + throw std::runtime_error("Unable to resize output file"); + } + if (size != 0) { + auto mapped = file.map(0, size); + if (!mapped) { + throw std::runtime_error("Unable to map output file"); + } + memcpy(mapped, data, size); + } + file.close(); + }); +} + +void unpackAndroidAssets() { + const QString DEST_PREFIX = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/"; + + QStringList filesToCopy = readAssetLines("cache_assets.txt"); + QString dateStamp = filesToCopy.takeFirst(); + QString dateStampFilename = DEST_PREFIX + dateStamp; + qDebug() << "Checking date stamp" << dateStamp; + if (QFileInfo(dateStampFilename).exists()) { + return; + } + + auto rootDir = QDir::root(); + for (const auto& fileToCopy : filesToCopy) { + auto destFileName = DEST_PREFIX + fileToCopy; + auto destFolder = QFileInfo(destFileName).absoluteDir(); + if (!destFolder.exists()) { + qDebug() << "Creating folder" << destFolder.absolutePath(); + if (!rootDir.mkpath(destFolder.absolutePath())) { + throw std::runtime_error("Error creating output path"); + } + } + if (QFile::exists(destFileName)) { + qDebug() << "Removing old file" << destFileName; + if (!QFile::remove(destFileName)) { + throw std::runtime_error("Failed to remove existing file"); + } + } + + qDebug() << "Copying asset " << fileToCopy << "to" << destFileName; + copyAsset(fileToCopy.toStdString().c_str(), destFileName); + } + + { + qDebug() << "Writing date stamp" << dateStamp; + QFile file(dateStampFilename); + if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { + throw std::runtime_error("Can't write date stamp"); + } + QTextStream(&file) << "touch" << endl; + file.close(); + } +} + +extern "C" { + +JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCreate(JNIEnv* env, jobject obj, jobject instance, jobject asset_mgr) { + qDebug() << "nativeOnCreate On thread " << QThread::currentThreadId(); + g_assetManager = AAssetManager_fromJava(env, asset_mgr); + auto oldMessageHandler = qInstallMessageHandler(tempMessageHandler); + unpackAndroidAssets(); + qInstallMessageHandler(oldMessageHandler); +} + +JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) { + qDebug() << "nativeOnPause"; +} + +JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnResume(JNIEnv* env, jobject obj) { + qDebug() << "nativeOnResume"; +} + +JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnExitVr(JNIEnv* env, jobject obj) { + qDebug() << "nativeOnCreate On thread " << QThread::currentThreadId(); +} + +} + + diff --git a/android/app/src/main/cpp/simple.qml b/android/app/src/main/cpp/simple.qml deleted file mode 100644 index 38f3c80374..0000000000 --- a/android/app/src/main/cpp/simple.qml +++ /dev/null @@ -1,10 +0,0 @@ -import QtQuick 2.0 - -Rectangle { - id: window - width: 320 - height: 480 - focus: true - color: "red" - ColorAnimation on color { from: "red"; to: "yellow"; duration: 1000; loops: Animation.Infinite } -} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java new file mode 100644 index 0000000000..de3bcee88d --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -0,0 +1,178 @@ +// +// InterfaceActivity.java +// android/app/src/main/java +// +// Created by Stephen Birarda on 1/26/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 +// + +package io.highfidelity.hifiinterface; + +import android.content.Intent; +import android.content.res.AssetManager; +import android.net.Uri; +import android.os.Bundle; +import android.view.WindowManager; +import android.util.Log; +import org.qtproject.qt5.android.bindings.QtActivity; + +/*import com.google.vr.cardboard.DisplaySynchronizer; +import com.google.vr.cardboard.DisplayUtils; +import com.google.vr.ndk.base.GvrApi;*/ +import android.graphics.Point; +import android.content.res.Configuration; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.view.View; + +public class InterfaceActivity extends QtActivity { + + //public static native void handleHifiURL(String hifiURLString); + private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager); + //private native void nativeOnPause(); + //private native void nativeOnResume(); + //private native void nativeOnStop(); + //private native void nativeOnStart(); + //private native void saveRealScreenSize(int width, int height); + //private native void setAppVersion(String version); + private native long nativeOnExitVr(); + + private AssetManager assetManager; + + private static boolean inVrMode; +// private GvrApi gvrApi; + // Opaque native pointer to the Application C++ object. + // This object is owned by the InterfaceActivity instance and passed to the native methods. + //private long nativeGvrApi; + + public void enterVr() { + //Log.d("[VR]", "Entering Vr mode (java)"); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + inVrMode = true; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + // Get the intent that started this activity in case we have a hifi:// URL to parse + Intent intent = getIntent(); + if (intent.getAction() == Intent.ACTION_VIEW) { + Uri data = intent.getData(); + +// if (data.getScheme().equals("hifi")) { +// handleHifiURL(data.toString()); +// } + } + +/* DisplaySynchronizer displaySynchronizer = new DisplaySynchronizer(this, DisplayUtils.getDefaultDisplay(this)); + gvrApi = new GvrApi(this, displaySynchronizer); + */ +// Log.d("GVR", "gvrApi.toString(): " + gvrApi.toString()); + + assetManager = getResources().getAssets(); + + //nativeGvrApi = + nativeOnCreate(this, assetManager /*, gvrApi.getNativeGvrContext()*/); + + Point size = new Point(); + getWindowManager().getDefaultDisplay().getRealSize(size); +// saveRealScreenSize(size.x, size.y); + + try { + PackageInfo pInfo = this.getPackageManager().getPackageInfo(getPackageName(), 0); + String version = pInfo.versionName; +// setAppVersion(version); + } catch (PackageManager.NameNotFoundException e) { + Log.e("GVR", "Error getting application version", e); + } + + final View rootView = getWindow().getDecorView().findViewById(android.R.id.content); + + // This is a workaround to hide the menu bar when the virtual keyboard is shown from Qt + rootView.getViewTreeObserver().addOnGlobalLayoutListener(new android.view.ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + int heightDiff = rootView.getRootView().getHeight() - rootView.getHeight(); + if (getActionBar().isShowing()) { + getActionBar().hide(); + } + } + }); + } + + @Override + protected void onPause() { + super.onPause(); + //nativeOnPause(); + //gvrApi.pauseTracking(); + } + + @Override + protected void onStart() { + super.onStart(); +// nativeOnStart(); + } + + @Override + protected void onStop() { + Log.d("[Background]","Calling nativeOnStop from InterfaceActivity"); +// nativeOnStop(); + super.onStop(); + } + + @Override + protected void onResume() { + super.onResume(); + //nativeOnResume(); + //gvrApi.resumeTracking(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + // Checks the orientation of the screen + if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){ +// Log.d("[VR]", "Portrait, forcing landscape"); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + if (inVrMode) { + inVrMode = false; +// Log.d("[VR]", "Starting VR exit"); + nativeOnExitVr(); + } else { + Log.w("[VR]", "Portrait detected but not in VR mode. Should not happen"); + } + } + } + + public void openUrlInAndroidWebView(String urlString) { + Log.d("openUrl", "Received in open " + urlString); + Intent openUrlIntent = new Intent(this, WebViewActivity.class); + openUrlIntent.putExtra(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_URL, urlString); + startActivity(openUrlIntent); + } + + /** + * Called when view focus is changed + */ + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) { + final int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + + getWindow().getDecorView().setSystemUiVisibility(uiOptions); + } + } + +} \ No newline at end of file diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java b/android/app/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java new file mode 100644 index 0000000000..34b087ca25 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java @@ -0,0 +1,130 @@ +package io.highfidelity.hifiinterface; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.app.Activity; + +import android.content.DialogInterface; +import android.app.AlertDialog; + +import org.json.JSONException; +import org.json.JSONObject; +import android.util.Log; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Arrays; +import java.util.List; + +public class PermissionChecker extends Activity { + private static final int REQUEST_PERMISSIONS = 20; + + private static final boolean CHOOSE_AVATAR_ON_STARTUP = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (CHOOSE_AVATAR_ON_STARTUP) { + showMenu(); + } + this.requestAppPermissions(new + String[]{ + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.RECORD_AUDIO, + Manifest.permission.CAMERA} + ,2,REQUEST_PERMISSIONS); + + } + + public void requestAppPermissions(final String[] requestedPermissions, + final int stringId, final int requestCode) { + int permissionCheck = PackageManager.PERMISSION_GRANTED; + boolean shouldShowRequestPermissionRationale = false; + for (String permission : requestedPermissions) { + permissionCheck = permissionCheck + checkSelfPermission(permission); + shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale || shouldShowRequestPermissionRationale(permission); + } + if (permissionCheck != PackageManager.PERMISSION_GRANTED) { + System.out.println("Permission was not granted. Ask for permissions"); + if (shouldShowRequestPermissionRationale) { + requestPermissions(requestedPermissions, requestCode); + } else { + requestPermissions(requestedPermissions, requestCode); + } + } else { + System.out.println("Launching the other activity.."); + launchActivityWithPermissions(); + } + } + + private void launchActivityWithPermissions(){ + finish(); + Intent i = new Intent(this, InterfaceActivity.class); + startActivity(i); + finish(); + } + + private void showMenu(){ + final List avatarOptions = Arrays.asList("\uD83D\uDC66\uD83C\uDFFB Cody","\uD83D\uDC66\uD83C\uDFFF Will","\uD83D\uDC68\uD83C\uDFFD Albert", "\uD83D\uDC7D Being of Light"); + final String[] avatarPaths = { + "http://mpassets.highfidelity.com/8c859fca-4cbd-4e82-aad1-5f4cb0ca5d53-v1/cody.fst", + "http://mpassets.highfidelity.com/d029ae8d-2905-4eb7-ba46-4bd1b8cb9d73-v1/4618d52e711fbb34df442b414da767bb.fst", + "http://mpassets.highfidelity.com/1e57c395-612e-4acd-9561-e79dbda0bc49-v1/albert.fst" }; + + final String pathForJson = "/data/data/io.highfidelity.hifiinterface/files/.config/High Fidelity - dev/"; + new AlertDialog.Builder(this) + .setTitle("Pick an avatar") + .setItems(avatarOptions.toArray(new CharSequence[avatarOptions.size()]),new DialogInterface.OnClickListener(){ + + @Override + public void onClick(DialogInterface dialog, int which) { + if(which < avatarPaths.length ) { + JSONObject obj = new JSONObject(); + try { + obj.put("firstRun",false); + obj.put("Avatar/fullAvatarURL", avatarPaths[which]); + File directory = new File(pathForJson); + + if(!directory.exists()) directory.mkdirs(); + + File file = new File(pathForJson + "Interface.json"); + Writer output = new BufferedWriter(new FileWriter(file)); + output.write(obj.toString().replace("\\","")); + output.close(); + System.out.println("I Could write config file expect to see the selected avatar"+obj.toString().replace("\\","")); + + } catch (JSONException e) { + System.out.println("JSONException something weired went wrong"); + } catch (IOException e) { + System.out.println("Could not write file :("); + } + } else { + System.out.println("Default avatar selected..."); + } + launchActivityWithPermissions(); + } + }).show(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + int permissionCheck = PackageManager.PERMISSION_GRANTED; + for (int permission : grantResults) { + permissionCheck = permissionCheck + permission; + } + if ((grantResults.length > 0) && permissionCheck == PackageManager.PERMISSION_GRANTED) { + launchActivityWithPermissions(); + } else if (grantResults.length > 0) { + System.out.println("User has deliberately denied Permissions. Launching anyways"); + launchActivityWithPermissions(); + } + } + + +} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java new file mode 100644 index 0000000000..4d706248d8 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java @@ -0,0 +1,242 @@ +// +// WebViewActivity.java +// interface/java +// +// Created by Cristian Duarte and Gabriel Calero on 8/17/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +//package hifiinterface.highfidelity.io.mybrowserapplication; +package io.highfidelity.hifiinterface; + +import android.app.ActionBar; +import android.app.Activity; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.net.http.SslError; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.webkit.SslErrorHandler; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ProgressBar; +import android.widget.Toast; +import android.widget.Toolbar; +import android.os.Looper; +import java.lang.Thread; +import java.lang.Runnable; + +import java.net.MalformedURLException; +import java.net.URL; + +public class WebViewActivity extends Activity { + + public static final String WEB_VIEW_ACTIVITY_EXTRA_URL = "url"; + + private native void nativeProcessURL(String url); + + private WebView myWebView; + private ProgressBar mProgressBar; + private ActionBar mActionBar; + private String mUrl; + + enum SafenessLevel { + NOT_ANALYZED_YET(""), + NOT_SECURE(""), + SECURE("\uD83D\uDD12 "), + BAD_SECURE("\uD83D\uDD13 "); + + String icon; + SafenessLevel(String icon) { + this.icon = icon; + } + } + + private SafenessLevel safenessLevel = SafenessLevel.NOT_ANALYZED_YET; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_web_view); + + setActionBar((Toolbar) findViewById(R.id.toolbar_actionbar)); + mActionBar = getActionBar(); + mActionBar.setDisplayHomeAsUpEnabled(true); + + mProgressBar = (ProgressBar) findViewById(R.id.toolbarProgressBar); + + mUrl = getIntent().getStringExtra(WEB_VIEW_ACTIVITY_EXTRA_URL); + myWebView = (WebView) findViewById(R.id.web_view); + myWebView.setWebViewClient(new HiFiWebViewClient()); + myWebView.setWebChromeClient(new HiFiWebChromeClient()); + WebSettings webSettings = myWebView.getSettings(); + webSettings.setJavaScriptEnabled(true); + webSettings.setBuiltInZoomControls(true); + webSettings.setDisplayZoomControls(false); + myWebView.loadUrl(mUrl); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + // Check if the key event was the Back button and if there's history + if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) { + myWebView.goBack(); + return true; + } + // If it wasn't the Back key or there's no web page history, bubble up to the default + // system behavior (probably exit the activity) + return super.onKeyDown(keyCode, event); + } + + private void showSubtitleWithUrl(String url) { + try { + mActionBar.setSubtitle(safenessLevel.icon + new URL(url.toString()).getHost()); + } catch (MalformedURLException e) { + Toast.makeText(WebViewActivity.this, "Error loading page: " + "bad url", Toast.LENGTH_LONG).show(); + Log.e("openUrl", "bad url"); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.web_view_menu, menu); + return true; + } + + private String intentUrlOrWebUrl() { + return myWebView==null || myWebView.getUrl()==null?mUrl:myWebView.getUrl(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item){ + switch (item.getItemId()) { + // Respond to the action bar's Up/Home/back button + case android.R.id.home: + finish(); + break; + case R.id.action_browser: + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(intentUrlOrWebUrl())); + startActivity(i); + return true; + case R.id.action_share: + Intent is = new Intent(Intent.ACTION_SEND); + is.setType("text/plain"); + is.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.web_view_action_share_subject)); + is.putExtra(Intent.EXTRA_TEXT, intentUrlOrWebUrl()); + startActivity(Intent.createChooser(is, getString(R.string.web_view_action_share_chooser))); + return true; + } + return super.onOptionsItemSelected(item); + } + + class HiFiWebViewClient extends WebViewClient { + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + mProgressBar.setVisibility(View.GONE); + if (safenessLevel!=SafenessLevel.BAD_SECURE) { + if (url.startsWith("https:")) { + safenessLevel=SafenessLevel.SECURE; + } else { + safenessLevel=SafenessLevel.NOT_SECURE; + } + } + showSubtitleWithUrl(url); + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + safenessLevel = SafenessLevel.NOT_ANALYZED_YET; + mProgressBar.setVisibility(View.VISIBLE); + mProgressBar.setProgress(0); + showSubtitleWithUrl(url); + } + + @Override + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + Toast.makeText(WebViewActivity.this, "Error loading page: " + error.getDescription(), Toast.LENGTH_LONG).show(); + if (ERROR_FAILED_SSL_HANDSHAKE == error.getErrorCode()) { + safenessLevel = SafenessLevel.BAD_SECURE; + } + } + + @Override + public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { + Toast.makeText(WebViewActivity.this, "Network Error loading page: " + errorResponse.getReasonPhrase(), Toast.LENGTH_LONG).show(); + } + + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + super.onReceivedSslError(view, handler, error); + Toast.makeText(WebViewActivity.this, "SSL error loading page: " + error.toString(), Toast.LENGTH_LONG).show(); + safenessLevel = SafenessLevel.BAD_SECURE; + } + + private boolean isFst(WebResourceRequest request) { + return isFst(request.getUrl().toString()); + } + + private boolean isFst(String url) { + return url.endsWith(".fst"); + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + // managing avatar selections + if (isFst(request)) { + final String url = request.getUrl().toString(); + new Thread(new Runnable() { + public void run() { + nativeProcessURL(url); + } + }).start(); // Avoid deadlock in Qt dialog + WebViewActivity.this.finish(); + return true; + } + return super.shouldOverrideUrlLoading(view, request); + } + + @Override + public void onLoadResource(WebView view, String url) { + if (isFst(url)) { + // processed separately + } else { + super.onLoadResource(view, url); + } + } + } + + class HiFiWebChromeClient extends WebChromeClient { + + @Override + public void onProgressChanged(WebView view, int newProgress) { + super.onProgressChanged(view, newProgress); + mProgressBar.setProgress(newProgress); + } + + @Override + public void onReceivedTitle(WebView view, String title) { + super.onReceivedTitle(view, title); + mActionBar.setTitle(title); + } + + } +} diff --git a/android/app/src/main/res/drawable/ic_close_black_24dp.xml b/android/app/src/main/res/drawable/ic_close_black_24dp.xml new file mode 100644 index 0000000000..ede4b7108d --- /dev/null +++ b/android/app/src/main/res/drawable/ic_close_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/layout/activity_web_view.xml b/android/app/src/main/res/layout/activity_web_view.xml new file mode 100644 index 0000000000..1e30b58676 --- /dev/null +++ b/android/app/src/main/res/layout/activity_web_view.xml @@ -0,0 +1,34 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/menu/web_view_menu.xml b/android/app/src/main/res/menu/web_view_menu.xml new file mode 100644 index 0000000000..074e1608c5 --- /dev/null +++ b/android/app/src/main/res/menu/web_view_menu.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bccce..d376d7af88 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index 9a078e3e1a..81a137957d 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index c133a0cbd3..88b1be5903 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index efc028a636..d032c30014 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index bfa42f0e7b..4a018d4ed9 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 3af2608a44..3cccf1037b 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 324e72cdd7..e97062e0ee 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 9bec2e6231..8d142881da 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index aee44e1384..f01203c738 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 34947cd6bb..211e298961 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..a9ec657aa9 --- /dev/null +++ b/android/app/src/main/res/values/dimens.xml @@ -0,0 +1,12 @@ + + + + 16dp + 16dp + + + + 14dp + + 12dp + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 5d6a4c1b99..b8080fae0f 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,3 +1,8 @@ - TestApp + Interface + Open in browser + Share link + Shared a link + Share link + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 033324ac58..23fe67f029 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -11,5 +11,15 @@ - + + + + + diff --git a/android/build.gradle b/android/build.gradle index c7eefd051b..1f2c563e1b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -40,6 +40,7 @@ ext { BUILD_BRANCH = project.hasProperty('BUILD_BRANCH') ? project.getProperty('BUILD_BRANCH') : '' EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : '' QT5_DEPS = [ + 'Qt5Concurrent', 'Qt5Core', 'Qt5Gui', 'Qt5Multimedia', @@ -47,8 +48,11 @@ ext { 'Qt5OpenGL', 'Qt5Qml', 'Qt5Quick', + 'Qt5QuickControls2', + 'Qt5QuickTemplates2', 'Qt5Script', 'Qt5ScriptTools', + 'Qt5Svg', 'Qt5WebChannel', 'Qt5WebSockets', 'Qt5Widgets', @@ -93,6 +97,11 @@ def packages = [ versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m', checksum: '617a80d213a5ec69fbfa21a1f2f738cd', ], + glad: [ + file: 'glad_armv8-libcpp.zip', + versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE', + checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449', + ], glm: [ file: 'glm-0.9.8.tgz', versionId: 'BlkJNwaYV2Gfy5XwMeU7K0uzPDRKFMt2', @@ -145,16 +154,18 @@ def options = [ def qmlRoot = new File(HIFI_ANDROID_PRECOMPILED, 'qt') -def captureOutput = { String command -> - def proc = command.execute() - def sout = new StringBuilder(), serr = new StringBuilder() - proc.consumeProcessOutput(sout, serr) - proc.waitForOrKill(10000) - def errorOutput = serr.toString() - if (!errorOutput.isEmpty()) { - throw new GradleException("Command '${command}' failed with error ${errorOutput}") +def captureOutput = { String command, List commandArgs -> + def result + new ByteArrayOutputStream().withStream { os -> + def execResult = exec { + executable = command + args = commandArgs + standardOutput = os + errorOutput = new ByteArrayOutputStream() + } + result = os.toString() } - return sout.toString() + return result; } def relativize = { File root, File absolute -> @@ -168,11 +179,13 @@ def scanQmlImports = { File qmlRootPath -> throw new GradleException('Unable to find required qmlimportscanner executable at ' + qmlImportCommandFile.parent.toString()) } - def command = qmlImportCommandFile.absolutePath + - " -rootPath ${qmlRootPath.absolutePath}" + - " -importPath ${qmlRoot.absolutePath}/qml" + def command = qmlImportCommandFile.absolutePath + def args = [ + '-rootPath', qmlRootPath.absolutePath, + '-importPath', "${qmlRoot.absolutePath}/qml" + ] - def commandResult = captureOutput(command) + def commandResult = captureOutput(command, args) new JsonSlurper().parseText(commandResult).each { if (!it.containsKey('path')) { println "Warning: QML import could not be resolved in any of the import paths: ${it.name}" @@ -257,7 +270,7 @@ def parseQtDependencies = { List qtLibs -> def generateLibsXml = { def libDestinationDirectory = jniFolder def jarDestinationDirectory = new File(appDir, 'libs') - def assetDestinationDirectory = new File(appDir, 'src/main/assets/bundled'); + def assetDestinationDirectory = new File(appDir, 'src/main/assets/--Added-by-androiddeployqt--'); def libsXmlFile = new File(appDir, 'src/main/res/values/libs.xml') def libPrefix = 'lib' + File.separator def jarPrefix = 'jar' + File.separator @@ -293,7 +306,7 @@ def generateLibsXml = { } else if (relativePath.startsWith('jar')) { destinationFile = new File(jarDestinationDirectory, relativePath.substring(jarPrefix.size())) } else { - xmlParser.createNode(bundledAssetsNode, 'item', [:]).setValue("bundled/${relativePath}:${relativePath}".replace(File.separator, '/')) + xmlParser.createNode(bundledAssetsNode, 'item', [:]).setValue("--Added-by-androiddeployqt--/${relativePath}:${relativePath}".replace(File.separator, '/')) destinationFile = new File(assetDestinationDirectory, relativePath) } @@ -427,9 +440,55 @@ task extractGvrBinaries(dependsOn: extractDependencies) { } } } - } +def generateAssetsFileList = { + def assetsPath = "${appDir}/src/main/assets/" + def addedByAndroidDeployQtName = "--Added-by-androiddeployqt--/" + + def addedByAndroidDeployQtPath = assetsPath + addedByAndroidDeployQtName + + def addedByAndroidDeployQt = new File(addedByAndroidDeployQtPath) + if (!addedByAndroidDeployQt.exists() && !addedByAndroidDeployQt.mkdirs()) { + throw new GradleScriptException("Failed to create directory " + addedByAndroidDeployQtPath, null); + } + def outputFilename = "/qt_cache_pregenerated_file_list" + def outputFile = new File(addedByAndroidDeployQtPath + outputFilename); + Map> directoryContents = new TreeMap<>(); + + def dir = new File(assetsPath) + dir.eachFileRecurse (FileType.ANY) { file -> + + def name = file.path.substring(assetsPath.length()) + int slashIndex = name.lastIndexOf('/') + def pathName = slashIndex >= 0 ? name.substring(0, slashIndex) : "/" + def fileName = slashIndex >= 0 ? name.substring(pathName.length() + 1) : name + if (!fileName.isEmpty() && file.isDirectory() && !fileName.endsWith("/")) { + fileName += "/" + } + + if (!directoryContents.containsKey(pathName)) { + directoryContents[pathName] = new ArrayList() + } + if (!fileName.isEmpty()) { + directoryContents[pathName].add(fileName); + } + } + DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile)); + for (Map.Entry> e: directoryContents.entrySet()) { + def entryList = e.getValue() + fos.writeInt(e.key.length()*2); // 2 bytes per char + fos.writeChars(e.key); + fos.writeInt(entryList.size()); + for (String entry: entryList) { + fos.writeInt(entry.length()*2); + fos.writeChars(entry); + } + } + fos.close(); +} + + // Copy required Qt main libraries and required plugins based on the predefined list here // FIXME eventually we would like to use the readelf functionality to automatically detect dependencies // from our built applications and use that during the full build process. However doing so would mean @@ -438,10 +497,11 @@ task extractGvrBinaries(dependsOn: extractDependencies) { task qtBundle { doLast { parseQtDependencies(QT5_DEPS) - //def qmlImportFolder = new File("${appDir}/../../interface/resources/qml/") - def qmlImportFolder = new File("${projectDir}/app/src/main/cpp") + def qmlImportFolder = new File("${appDir}/../../interface/resources/qml/") + //def qmlImportFolder = new File("${projectDir}/app/src/main/cpp") scanQmlImports(qmlImportFolder) generateLibsXml() + generateAssetsFileList() } } @@ -450,10 +510,12 @@ task setupDependencies(dependsOn: [setupScribe, copyDependencies, extractGvrBina task cleanDependencies(type: Delete) { delete HIFI_ANDROID_PRECOMPILED delete 'app/src/main/jniLibs/arm64-v8a' - delete 'app/src/main/assets/bundled' + delete 'app/src/main/assets/--Added-by-androiddeployqt--' delete 'app/src/main/res/values/libs.xml' } + + // FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution. // See the comment on the qtBundle task above /* diff --git a/android/build_recipes.md b/android/build_recipes.md index 78ca961f5c..cd7b0413bb 100644 --- a/android/build_recipes.md +++ b/android/build_recipes.md @@ -43,15 +43,17 @@ export LDFLAGS="-pie" * Install Python 3.6 for Windows * Open a Git Bash command prompt * Ensure the following commands are visible in the path with `which ` - * gcc - * javac - * python - * gmake + * gcc + * javac + * python + * gmake * If any of them fail, fix your path and restart the bash prompt * Fetch the pre-built OpenSSL binaries for Android/iOS from here: https://github.com/leenjewel/openssl_for_ios_and_android/releases * Grab the latest release of the 1.0.2 series * Open the archive and extract the `/android/openssl-arm64-v8a` folder +### All platforms + * Download the Qt sources * `git clone git://code.qt.io/qt/qt5.git` * `cd qt5` @@ -60,7 +62,11 @@ export LDFLAGS="-pie" * `git submodule update --recursive` * `cd ..` * Create a build directory with the command `mkdir qt5build` -* Configure the Qt5 build with the command `../qt5/configure -xplatform android-clang -android-ndk-host windows-x86_64 -confirm-license -opensource --disable-rpath -nomake tests -nomake examples -skip qttranslations -skip qtserialport -skip qt3d -skip qtwebengine -skip qtlocation -skip qtwayland -skip qtsensors -skip qtgamepad -skip qtgamepad -skip qtspeech -skip qtcharts -skip qtx11extras -skip qtmacextras -skip qtvirtualkeyboard -skip qtpurchasing -skip qtdatavis3d -android-ndk C:/Android/NDK -android-toolchain-version 4.9 -android-arch arm64-v8a -no-warnings-are-errors -android-ndk-platform android-24 -v -platform win32-g++ -prefix C:/qt5build_debug -android-sdk C:/Android/SDK -c++std c++14 -openssl-linked -L/lib -I/include` +* Configure the Qt5 build with the command `../qt5/configure -opensource -confirm-license -xplatform android-clang --disable-rpath -nomake tests -nomake examples -skip qttranslations -skip qtserialport -skip qt3d -skip qtwebengine -skip qtlocation -skip qtwayland -skip qtsensors -skip qtgamepad -skip qtgamepad -skip qtspeech -skip qtcharts -skip qtx11extras -skip qtmacextras -skip qtvirtualkeyboard -skip qtpurchasing -skip qtdatavis3d -android-toolchain-version 4.9 -android-ndk $HOME/Android/NDK -android-arch arm64-v8a -no-warnings-are-errors -android-ndk-platform android-24 -v -android-ndk-host windows-x86_64 -platform win32-g++ -prefix C:/qt5build_debug -android-sdk $HOME/Android/SDK -c++std c++14 -openssl-linked -L/lib -I/include` +* Some of those entries must be customized depending on platform. + * `-platform win32-g++` + * `-android-ndk-host windows-x86_64` + * `-prefix C:/qt5build_debug` diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index aac7c9b461..0000000000 --- a/android/gradle.properties +++ /dev/null @@ -1,17 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index c9ded2d6fb..1f3f770867 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -123,12 +123,12 @@ void ScriptableAvatar::update(float deltatime) { AnimPose& absPose = absPoses[i]; if (data.rotation != absPose.rot()) { data.rotation = absPose.rot(); - data.rotationSet = true; + data.rotationIsDefaultPose = false; } AnimPose& relPose = poses[i]; if (data.translation != relPose.trans()) { data.translation = relPose.trans(); - data.translationSet = true; + data.translationIsDefaultPose = false; } } diff --git a/cmake/externals/glew/CMakeLists.txt b/cmake/externals/glad32es/CMakeLists.txt similarity index 50% rename from cmake/externals/glew/CMakeLists.txt rename to cmake/externals/glad32es/CMakeLists.txt index 6c3208981d..8ca4d44021 100644 --- a/cmake/externals/glew/CMakeLists.txt +++ b/cmake/externals/glad32es/CMakeLists.txt @@ -1,27 +1,22 @@ -set(EXTERNAL_NAME glew) - -if (ANDROID) - set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") -endif () +set(EXTERNAL_NAME glad32es) include(ExternalProject) +include(SelectLibraryConfigurations) + ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple_1.13.0.zip - URL_MD5 73f833649e904257b35bf4e84f8bdfb5 - CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON + URL https://hifi-public.s3.amazonaws.com/austin/glad/glad32es.zip + URL_MD5 6a641d8c49dee4c895fa59315f5682a6 + CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 ) # Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glew include directories") +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) if (UNIX) set(LIB_PREFIX "lib") @@ -30,5 +25,9 @@ elseif (WIN32) set(LIB_EXT "lib") 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") +set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT}) +set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT}) +select_library_configurations(${EXTERNAL_NAME_UPPER}) + +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories") +set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries") diff --git a/cmake/externals/glad41/CMakeLists.txt b/cmake/externals/glad41/CMakeLists.txt new file mode 100644 index 0000000000..2371044362 --- /dev/null +++ b/cmake/externals/glad41/CMakeLists.txt @@ -0,0 +1,33 @@ +set(EXTERNAL_NAME glad41) + +include(ExternalProject) +include(SelectLibraryConfigurations) + +ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://hifi-public.s3.amazonaws.com/austin/glad/glad41.zip + URL_MD5 1324eeec33abe91e67d19ae551ba624d + CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 +) + +# Hide this external target (for ide users) +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +if (UNIX) + set(LIB_PREFIX "lib") + set(LIB_EXT "a") +elseif (WIN32) + set(LIB_EXT "lib") +endif () + +set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT}) +set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT}) +select_library_configurations(${EXTERNAL_NAME_UPPER}) + +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories") +set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries") diff --git a/cmake/externals/glad45/CMakeLists.txt b/cmake/externals/glad45/CMakeLists.txt new file mode 100644 index 0000000000..8ad455fa7e --- /dev/null +++ b/cmake/externals/glad45/CMakeLists.txt @@ -0,0 +1,33 @@ +set(EXTERNAL_NAME glad45) + +include(ExternalProject) +include(SelectLibraryConfigurations) + +ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://hifi-public.s3.amazonaws.com/austin/glad/glad45.zip + URL_MD5 cfb19b3cb5b2f8f1d1669fb3150e5f05 + CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 +) + +# Hide this external target (for ide users) +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +if (UNIX) + set(LIB_PREFIX "lib") + set(LIB_EXT "a") +elseif (WIN32) + set(LIB_EXT "lib") +endif () + +set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT}) +set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT}) +select_library_configurations(${EXTERNAL_NAME_UPPER}) + +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories") +set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries") diff --git a/cmake/macros/GenerateQrc.cmake b/cmake/macros/GenerateQrc.cmake index 0283b3ea9b..9bf530b2a2 100644 --- a/cmake/macros/GenerateQrc.cmake +++ b/cmake/macros/GenerateQrc.cmake @@ -12,9 +12,14 @@ function(GENERATE_QRC) foreach(GLOB ${GENERATE_QRC_GLOBS}) file(GLOB_RECURSE FOUND_FILES RELATIVE ${GENERATE_QRC_PATH} ${GLOB}) foreach(FILENAME ${FOUND_FILES}) + if (${FILENAME} MATCHES "^\\.\\.") + continue() + endif() + list(APPEND ALL_FILES "${GENERATE_QRC_PATH}/${FILENAME}") set(QRC_CONTENTS "${QRC_CONTENTS}${GENERATE_QRC_PATH}/${FILENAME}\n") endforeach() endforeach() - + + set(GENERATE_QRC_DEPENDS ${ALL_FILES} PARENT_SCOPE) configure_file("${HF_CMAKE_DIR}/templates/resources.qrc.in" ${GENERATE_QRC_OUTPUT}) endfunction() diff --git a/cmake/macros/TargetGlad.cmake b/cmake/macros/TargetGlad.cmake new file mode 100644 index 0000000000..929f61c3f2 --- /dev/null +++ b/cmake/macros/TargetGlad.cmake @@ -0,0 +1,38 @@ +# +# 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_GLAD) + if (ANDROID) + set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/glad) + set(GLAD_INCLUDE_DIRS "${INSTALL_DIR}/include") + set(GLAD_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libglad_d.a) + set(GLAD_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libglad.a) + select_library_configurations(GLAD) + find_library(EGL EGL) + target_link_libraries(${TARGET_NAME} ${EGL}) + else() + if (USE_GLES) + set(GLAD_VER "32es") + else() + set(GLAD_VER "45") + endif() + find_package(OpenGL REQUIRED) + list(APPEND GLAD_EXTRA_LIBRARIES ${OPENGL_LIBRARY}) + if (NOT WIN32) + list(APPEND GLAD_EXTRA_LIBRARIES dl) + endif() + set(GLAD "glad${GLAD_VER}") + string(TOUPPER ${GLAD} GLAD_UPPER) + add_dependency_external_projects(${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}) +endmacro() diff --git a/cmake/macros/TargetGlew.cmake b/cmake/macros/TargetGlew.cmake deleted file mode 100644 index bc4d5cb033..0000000000 --- a/cmake/macros/TargetGlew.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# -# 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_GLEW) - if (NOT ANDROID) - add_definitions(-DGLEW_STATIC) - add_dependency_external_projects(glew) - find_package(GLEW REQUIRED) - target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARY}) - endif() -endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetOpenGL.cmake b/cmake/macros/TargetOpenGL.cmake index 6ad92259bb..84fe5a7a5a 100644 --- a/cmake/macros/TargetOpenGL.cmake +++ b/cmake/macros/TargetOpenGL.cmake @@ -6,20 +6,6 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # macro(TARGET_OPENGL) - if (APPLE) - # link in required OS X frameworks and include the right GL headers - find_library(OpenGL OpenGL) - target_link_libraries(${TARGET_NAME} ${OpenGL}) - elseif(ANDROID) - target_link_libraries(${TARGET_NAME} GLESv3 EGL) - else() - find_package(OpenGL REQUIRED) - if (${OPENGL_INCLUDE_DIR}) - include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") - endif() - target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") - target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) - endif() + target_glad() target_nsight() - target_glew() endmacro() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 21225756b4..383c50ed44 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -11,9 +11,20 @@ function(JOIN VALUES GLUE OUTPUT) set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE) endfunction() +set(RESOURCES_QRC ${CMAKE_CURRENT_BINARY_DIR}/resources.qrc) +set(RESOURCES_RCC ${CMAKE_CURRENT_SOURCE_DIR}/compiledResources/resources.rcc) +generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *) + +add_custom_command( + OUTPUT ${RESOURCES_RCC} + DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} + COMMAND "${QT_DIR}/bin/rcc" + ARGS ${RESOURCES_QRC} -binary -o ${RESOURCES_RCC} +) + +list(APPEND GENERATE_QRC_DEPENDS ${RESOURCES_RCC}) +add_custom_target(resources ALL DEPENDS ${GENERATE_QRC_DEPENDS}) -set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc) -generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg) # set a default root dir for each of our optional externals if it was not passed set(OPTIONAL_EXTERNALS "LeapMotion") @@ -40,6 +51,7 @@ endif() # grab the implementation and header files from src dirs file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h") GroupSources("src") +list(APPEND INTERFACE_SRCS ${RESOURCES_RCC}) # Add SpeechRecognizer if on Windows or OS X, otherwise remove if (WIN32) @@ -72,16 +84,6 @@ qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}") # add them to the interface source files set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}") -list(APPEND INTERFACE_SRCS ${INTERFACE_QML_QRC}) - -if (UNIX) - install( - DIRECTORY "${CMAKE_SOURCE_DIR}/interface/resources/qml" - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/resources - COMPONENT ${CLIENT_COMPONENT} - ) -endif() - # translation disabled until we strip out the line numbers # set(QM ${TARGET_NAME}_en.qm) # set(TS ${TARGET_NAME}_en.ts) @@ -89,21 +91,7 @@ endif() # setup the android parameters that will help us produce an APK if (ANDROID) - set(ANDROID_APK_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk-build") - set(ANDROID_APK_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk") - - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}") - set(ANDROID_SDK_ROOT $ENV{ANDROID_HOME}) - set(ANDROID_APP_DISPLAY_NAME Interface) - set(ANDROID_API_LEVEL 19) - set(ANDROID_APK_PACKAGE io.highfidelity.interface) - set(ANDROID_ACTIVITY_NAME io.highfidelity.interface.InterfaceActivity) - set(ANDROID_APK_VERSION_NAME "0.1") - set(ANDROID_APK_VERSION_CODE 1) - set(ANDROID_APK_FULLSCREEN TRUE) - set(ANDROID_DEPLOY_QT_INSTALL "--install") - set(BUILD_SHARED_LIBS ON) endif () @@ -175,6 +163,8 @@ else () add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) endif () +add_dependencies(${TARGET_NAME} resources) + if (WIN32) # These are external plugins, but we need to do the 'add dependency' here so that their # binary directories get added to the fixup path @@ -258,15 +248,20 @@ endforeach() # include headers for interface and InterfaceConfig. include_directories("${PROJECT_SOURCE_DIR}/src") +if (ANDROID) + find_library(ANDROID_LOG_LIB log) + target_link_libraries(${TARGET_NAME} ${ANDROID_LOG_LIB}) +endif () + target_link_libraries( ${TARGET_NAME} Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg - Qt5::WebChannel Qt5::WebEngine + Qt5::WebChannel ${PLATFORM_QT_LIBRARIES} ) -if (UNIX) +if (UNIX AND NOT ANDROID) if (CMAKE_SYSTEM_NAME MATCHES "Linux") # Linux target_link_libraries(${TARGET_NAME} pthread atomic) @@ -274,7 +269,7 @@ if (UNIX) # OSX target_link_libraries(${TARGET_NAME} pthread) endif () -endif(UNIX) +endif() # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) @@ -304,12 +299,20 @@ if (APPLE) # call the fixup_interface macro to add required bundling commands for installation fixup_interface() -else (APPLE) +else() # copy the resources files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "${RESOURCES_RCC}" + "$" + # FIXME, the edit script code loads HTML from the scripts folder + # which in turn relies on CSS that refers to the fonts. In theory + # we should be able to modify the CSS to reference the QRC path to + # the ttf files, but doing so generates a CORS policy violation, + # so we have to retain a copy of the fonts outside of the resources binary COMMAND "${CMAKE_COMMAND}" -E copy_directory - "${PROJECT_SOURCE_DIR}/resources" - "$/resources" + "${PROJECT_SOURCE_DIR}/resources/fonts" + "$/resources/fonts" COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/scripts" @@ -335,7 +338,7 @@ else (APPLE) optional_win_executable_signing() endif() -endif (APPLE) +endif() if (SCRIPTS_INSTALL_DIR) @@ -359,20 +362,6 @@ if (WIN32) package_libraries_for_deployment() endif() -if (ANDROID) - set(HIFI_URL_INTENT "\ - \n \ - \n \ - \n \ - \n \ - \n " - ) - - set(ANDROID_EXTRA_ACTIVITY_XML "${HIFI_URL_INTENT}") - - qt_create_apk() -endif () - add_dependency_external_projects(GifCreator) find_package(GifCreator REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GIFCREATOR_INCLUDE_DIRS}) diff --git a/interface/compiledResources/.placeholder b/interface/compiledResources/.placeholder new file mode 100644 index 0000000000..e69de29bb2 diff --git a/interface/resources/icons/tablet-icons/EmoteAppIcon.svg b/interface/resources/icons/tablet-icons/EmoteAppIcon.svg new file mode 100644 index 0000000000..340f0fcd2f --- /dev/null +++ b/interface/resources/icons/tablet-icons/EmoteAppIcon.svg @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/interface/resources/images/+gles/Default-Sky-9-cubemap.ktx b/interface/resources/images/+gles/Default-Sky-9-cubemap.ktx new file mode 100644 index 0000000000..6d46791fa2 Binary files /dev/null and b/interface/resources/images/+gles/Default-Sky-9-cubemap.ktx differ diff --git a/interface/resources/images/preview-privacy.png b/interface/resources/images/preview-privacy.png new file mode 100644 index 0000000000..1a718de233 Binary files /dev/null and b/interface/resources/images/preview-privacy.png differ diff --git a/interface/resources/meshes/+gles/defaultAvatar_full.fst b/interface/resources/meshes/+gles/defaultAvatar_full.fst new file mode 100644 index 0000000000..7d917318dd --- /dev/null +++ b/interface/resources/meshes/+gles/defaultAvatar_full.fst @@ -0,0 +1,135 @@ +name = being_of_light +type = body+head +scale = 1 +filename = being_of_light/being_of_light.fbx +texdir = being_of_light/textures +joint = jointRoot = Hips +joint = jointLeftHand = LeftHand +joint = jointHead = HeadTop_End +joint = jointLean = Spine +joint = jointEyeLeft = LeftEye +joint = jointRightHand = RightHand +joint = jointNeck = Head +joint = jointEyeRight = RightEye +freeJoint = LeftArm +freeJoint = LeftForeArm +freeJoint = RightArm +freeJoint = RightForeArm +bs = MouthFrown_L = Frown_Left = 1 +bs = MouthLeft = Midmouth_Left = 1 +bs = BrowsU_R = BrowsUp_Right = 1 +bs = ChinUpperRaise = UpperLipUp_Right = 0.5 +bs = ChinUpperRaise = UpperLipUp_Left = 0.5 +bs = MouthSmile_R = Smile_Right = 1 +bs = MouthDimple_L = Smile_Left = 0.25 +bs = EyeBlink_L = Blink_Left = 1 +bs = BrowsD_L = BrowsDown_Left = 1 +bs = MouthFrown_R = Frown_Right = 1 +bs = MouthDimple_R = Smile_Right = 0.25 +bs = Sneer = Squint_Right = 0.5 +bs = Sneer = Squint_Left = 0.5 +bs = Sneer = NoseScrunch_Right = 0.75 +bs = Sneer = NoseScrunch_Left = 0.75 +bs = EyeSquint_L = Squint_Left = 1 +bs = EyeBlink_R = Blink_Right = 1 +bs = JawLeft = JawRotateY_Left = 0.5 +bs = BrowsD_R = BrowsDown_Right = 1 +bs = EyeSquint_R = Squint_Right = 1 +bs = Puff = CheekPuff_Right = 1 +bs = Puff = CheekPuff_Left = 1 +bs = LipsUpperClose = UpperLipIn = 1 +bs = JawOpen = MouthOpen = 0.69999999999999996 +bs = LipsUpperUp = UpperLipUp_Right = 0.69999999999999996 +bs = LipsUpperUp = UpperLipUp_Left = 0.69999999999999996 +bs = LipsLowerDown = LowerLipDown_Right = 0.69999999999999996 +bs = LipsLowerDown = LowerLipDown_Left = 0.69999999999999996 +bs = LipsLowerOpen = LowerLipOut = 1 +bs = EyeOpen_L = EyesWide_Left = 1 +bs = LipsPucker = MouthNarrow_Right = 1 +bs = LipsPucker = MouthNarrow_Left = 1 +bs = EyeOpen_R = EyesWide_Right = 1 +bs = JawRight = Jaw_Right = 1 +bs = MouthRight = Midmouth_Right = 1 +bs = ChinLowerRaise = Jaw_Up = 1 +bs = LipsUpperOpen = UpperLipOut = 1 +bs = BrowsU_C = BrowsUp_Right = 1 +bs = BrowsU_C = BrowsUp_Left = 1 +bs = JawFwd = JawForeward = 1 +bs = BrowsU_L = BrowsUp_Left = 1 +bs = MouthSmile_L = Smile_Left = 1 +bs = LipsLowerClose = LowerLipIn = 1 +bs = LipsFunnel = TongueUp = 1 +bs = LipsFunnel = MouthWhistle_NarrowAdjust_Right = 0.5 +bs = LipsFunnel = MouthWhistle_NarrowAdjust_Left = 0.5 +bs = LipsFunnel = MouthNarrow_Right = 1 +bs = LipsFunnel = MouthNarrow_Left = 1 +bs = LipsFunnel = Jaw_Down = 0.35999999999999999 +bs = LipsFunnel = JawForeward = 0.39000000000000001 +jointIndex = LeftHandIndex1 = 50 +jointIndex = LeftHandIndex2 = 51 +jointIndex = LeftHandIndex3 = 52 +jointIndex = LeftHandIndex4 = 53 +jointIndex = Spine1 = 12 +jointIndex = Spine2 = 13 +jointIndex = RightHandThumb1 = 18 +jointIndex = RightHandThumb2 = 19 +jointIndex = RightHandThumb3 = 20 +jointIndex = RightHandThumb4 = 21 +jointIndex = LeftFoot = 8 +jointIndex = LeftForeArm = 40 +jointIndex = Neck = 62 +jointIndex = Head = 63 +jointIndex = Hips = 0 +jointIndex = RightHandPinky1 = 30 +jointIndex = RightHandPinky2 = 31 +jointIndex = RightHandPinky3 = 32 +jointIndex = RightHandPinky4 = 33 +jointIndex = RightLeg = 2 +jointIndex = RightForeArm = 16 +jointIndex = LeftHandRing1 = 46 +jointIndex = LeftHandRing2 = 47 +jointIndex = LeftHandRing3 = 48 +jointIndex = LeftHandRing4 = 49 +jointIndex = LeftHandThumb1 = 54 +jointIndex = LeftHandThumb2 = 55 +jointIndex = LeftHandThumb3 = 56 +jointIndex = LeftHandThumb4 = 57 +jointIndex = HeadTop_End = 66 +jointIndex = LeftUpLeg = 6 +jointIndex = LeftToeBase = 9 +jointIndex = LeftHandPinky1 = 42 +jointIndex = LeftHandPinky2 = 43 +jointIndex = LeftHandPinky3 = 44 +jointIndex = LeftHandPinky4 = 45 +jointIndex = LeftLeg = 7 +jointIndex = RightEye = 65 +jointIndex = RightHand = 17 +jointIndex = RightToeBase = 4 +jointIndex = RightUpLeg = 1 +jointIndex = RightArm = 15 +jointIndex = RightHandRing1 = 26 +jointIndex = RightHandRing2 = 27 +jointIndex = RightHandRing3 = 28 +jointIndex = RightHandRing4 = 29 +jointIndex = RightHandIndex1 = 22 +jointIndex = RightHandIndex2 = 23 +jointIndex = RightHandIndex3 = 24 +jointIndex = RightHandIndex4 = 25 +jointIndex = LeftToe_End = 10 +jointIndex = LeftHandMiddle1 = 58 +jointIndex = LeftHandMiddle2 = 59 +jointIndex = LeftHandMiddle3 = 60 +jointIndex = LeftShoulder = 38 +jointIndex = LeftHandMiddle4 = 61 +jointIndex = RightFoot = 3 +jointIndex = LeftHand = 41 +jointIndex = RightHandMiddle1 = 34 +jointIndex = RightHandMiddle2 = 35 +jointIndex = RightHandMiddle3 = 36 +jointIndex = RightShoulder = 14 +jointIndex = LeftEye = 64 +jointIndex = RightHandMiddle4 = 37 +jointIndex = Body = 67 +jointIndex = LeftArm = 39 +jointIndex = RightToe_End = 5 +jointIndex = Spine = 11 diff --git a/interface/resources/meshes/being_of_light/being_of_light.fbx b/interface/resources/meshes/being_of_light/being_of_light.fbx new file mode 100644 index 0000000000..20e71abd6d Binary files /dev/null and b/interface/resources/meshes/being_of_light/being_of_light.fbx differ diff --git a/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_DiffuseMap.png b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_DiffuseMap.png new file mode 100644 index 0000000000..8fcf588643 Binary files /dev/null and b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_DiffuseMap.png differ diff --git a/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_EmissiveMap.png b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_EmissiveMap.png new file mode 100644 index 0000000000..d836aebde8 Binary files /dev/null and b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_EmissiveMap.png differ diff --git a/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_NormalMap.png b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_NormalMap.png new file mode 100644 index 0000000000..f5000b86e9 Binary files /dev/null and b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_NormalMap.png differ diff --git a/interface/resources/qml/+android/Stats.qml b/interface/resources/qml/+android/Stats.qml new file mode 100644 index 0000000000..d961285a46 --- /dev/null +++ b/interface/resources/qml/+android/Stats.qml @@ -0,0 +1,394 @@ +import Hifi 1.0 as Hifi +import QtQuick 2.3 +import '.' + +Item { + id: stats + + anchors.leftMargin: 300 + objectName: "StatsItem" + property int modality: Qt.NonModal + implicitHeight: row.height + implicitWidth: row.width + + Component.onCompleted: { + stats.parentChanged.connect(fill); + fill(); + } + Component.onDestruction: { + stats.parentChanged.disconnect(fill); + } + + function fill() { + // This will cause a warning at shutdown, need to find another way to remove + // the warning other than filling the anchors to the parent + anchors.horizontalCenter = parent.horizontalCenter + } + + Hifi.Stats { + id: root + objectName: "Stats" + implicitHeight: row.height + implicitWidth: row.width + + anchors.horizontalCenter: parent.horizontalCenter + readonly property string bgColor: "#AA111111" + + Row { + id: row + spacing: 8 + Rectangle { + width: generalCol.width + 8; + height: generalCol.height + 8; + color: root.bgColor; + + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true + } + + Column { + id: generalCol + spacing: 4; x: 4; y: 4; + StatText { + text: "Servers: " + root.serverCount + } + StatText { + text: "Avatars: " + root.avatarCount + } + StatText { + text: "Game Rate: " + root.gameLoopRate + } + StatText { + visible: root.expanded + text: root.gameUpdateStats + } + StatText { + text: "Render Rate: " + root.renderrate.toFixed(2); + } + StatText { + text: "Present Rate: " + root.presentrate.toFixed(2); + } + StatText { + visible: root.expanded + text: " Present New Rate: " + root.presentnewrate.toFixed(2); + } + StatText { + visible: root.expanded + text: " Present Drop Rate: " + root.presentdroprate.toFixed(2); + } + StatText { + text: "Stutter Rate: " + root.stutterrate.toFixed(3); + visible: root.stutterrate != -1; + } + StatText { + text: "Missed Frame Count: " + root.appdropped; + visible: root.appdropped > 0; + } + StatText { + text: "Long Render Count: " + root.longrenders; + visible: root.longrenders > 0; + } + StatText { + text: "Long Submit Count: " + root.longsubmits; + visible: root.longsubmits > 0; + } + StatText { + text: "Long Frame Count: " + root.longframes; + visible: root.longframes > 0; + } + StatText { + text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount + } + StatText { + text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2) + } + StatText { + visible: root.expanded + text: "Asset Mbps In/Out: " + root.assetMbpsIn.toFixed(2) + "/" + root.assetMbpsOut.toFixed(2) + } + StatText { + visible: root.expanded + text: "Avatars Updated: " + root.updatedAvatarCount + } + StatText { + visible: root.expanded + text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount + } + } + } + + Rectangle { + width: pingCol.width + 8 + height: pingCol.height + 8 + color: root.bgColor; + visible: root.audioPing != -2 + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true + } + Column { + id: pingCol + spacing: 4; x: 4; y: 4; + StatText { + text: "Audio ping/loss: " + root.audioPing + "/" + root.audioPacketLoss + "%" + } + StatText { + text: "Avatar ping: " + root.avatarPing + } + StatText { + text: "Entities avg ping: " + root.entitiesPing + } + StatText { + text: "Asset ping: " + root.assetPing + } + StatText { + visible: root.expanded; + text: "Messages max ping: " + root.messagePing + } + } + } + + Rectangle { + width: geoCol.width + 8 + height: geoCol.height + 8 + color: root.bgColor; + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true + } + Column { + id: geoCol + spacing: 4; x: 4; y: 4; + StatText { + text: "Position: " + root.position.x.toFixed(1) + ", " + + root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1) + } + StatText { + text: "Speed: " + root.speed.toFixed(1) + } + StatText { + text: "Yaw: " + root.yaw.toFixed(1) + } + StatText { + visible: root.expanded; + text: "Avatar Mixer In: " + root.avatarMixerInKbps + " kbps, " + + root.avatarMixerInPps + "pps"; + } + StatText { + visible: root.expanded; + text: "Avatar Mixer Out: " + root.avatarMixerOutKbps + " kbps, " + + root.avatarMixerOutPps + "pps, " + + root.myAvatarSendRate.toFixed(2) + "hz"; + } + StatText { + visible: root.expanded; + text: "Audio Mixer In: " + root.audioMixerInKbps + " kbps, " + + root.audioMixerInPps + "pps"; + } + StatText { + visible: root.expanded; + text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " + + "Silent: " + root.audioSilentInboundPPS + " pps"; + } + StatText { + visible: root.expanded; + text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " + + root.audioMixerOutPps + "pps"; + } + StatText { + visible: root.expanded; + text: "Audio Out Mic: " + root.audioOutboundPPS + " pps, " + + "Silent: " + root.audioSilentOutboundPPS + " pps"; + } + StatText { + visible: root.expanded; + text: "Audio Codec: " + root.audioCodec + " Noise Gate: " + + root.audioNoiseGate; + } + StatText { + visible: root.expanded; + text: "Entity Servers In: " + root.entityPacketsInKbps + " kbps"; + } + StatText { + visible: root.expanded; + text: "Downloads: " + root.downloads + "/" + root.downloadLimit + + ", Pending: " + root.downloadsPending; + } + StatText { + visible: root.expanded; + text: "Processing: " + root.processing + + ", Pending: " + root.processingPending; + } + StatText { + visible: root.expanded && root.downloadUrls.length > 0; + text: "Download URLs:" + } + ListView { + width: geoCol.width + height: root.downloadUrls.length * 15 + + visible: root.expanded && root.downloadUrls.length > 0; + + model: root.downloadUrls + delegate: StatText { + visible: root.expanded; + text: modelData.length > 30 + ? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22) + : modelData + } + } + } + } + Rectangle { + width: octreeCol.width + 8 + height: octreeCol.height + 8 + color: root.bgColor; + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true + } + Column { + id: octreeCol + spacing: 4; x: 4; y: 4; + StatText { + text: "Engine: " + root.engineFrameTime.toFixed(1) + " ms" + } + StatText { + text: "Batch: " + root.batchFrameTime.toFixed(1) + " ms" + } + StatText { + text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms" + } + StatText { + text: "Triangles: " + root.triangles + + " / Material Switches: " + root.materialSwitches + } + StatText { + text: "GPU Free Memory: " + root.gpuFreeMemory + " MB"; + } + StatText { + text: "GPU Textures: "; + } + StatText { + text: " Count: " + root.gpuTextures; + } + StatText { + text: " Pressure State: " + root.gpuTextureMemoryPressureState; + } + StatText { + text: " Resource Allocated / Populated / Pending: "; + } + StatText { + text: " " + root.gpuTextureResourceMemory + " / " + root.gpuTextureResourcePopulatedMemory + " / " + root.texturePendingTransfers + " MB"; + } + StatText { + text: " Resident Memory: " + root.gpuTextureResidentMemory + " MB"; + } + StatText { + text: " Framebuffer Memory: " + root.gpuTextureFramebufferMemory + " MB"; + } + StatText { + text: " External Memory: " + root.gpuTextureExternalMemory + " MB"; + } + StatText { + text: "GPU Buffers: " + } + StatText { + text: " Count: " + root.gpuBuffers; + } + StatText { + text: " Memory: " + root.gpuBufferMemory + " MB"; + } + StatText { + text: "GL Swapchain Memory: " + root.glContextSwapchainMemory + " MB"; + } + StatText { + text: "QML Texture Memory: " + root.qmlTextureMemory + " MB"; + } + StatText { + visible: root.expanded; + text: "Items rendered / considered: " + + root.itemRendered + " / " + root.itemConsidered; + } + StatText { + visible: root.expanded; + text: " out of view: " + root.itemOutOfView + + " too small: " + root.itemTooSmall; + } + StatText { + visible: root.expanded; + text: "Shadows rendered / considered: " + + root.shadowRendered + " / " + root.shadowConsidered; + } + StatText { + visible: root.expanded; + text: " out of view: " + root.shadowOutOfView + + " too small: " + root.shadowTooSmall; + } + StatText { + visible: !root.expanded + text: "Octree Elements Server: " + root.serverElements + + " Local: " + root.localElements; + } + StatText { + visible: root.expanded + text: "Octree Sending Mode: " + root.sendingMode; + } + StatText { + visible: root.expanded + text: "Octree Packets to Process: " + root.packetStats; + } + StatText { + visible: root.expanded + text: "Octree Elements - "; + } + StatText { + visible: root.expanded + text: "\tServer: " + root.serverElements + + " Internal: " + root.serverInternal + + " Leaves: " + root.serverLeaves; + } + StatText { + visible: root.expanded + text: "\tLocal: " + root.localElements + + " Internal: " + root.localInternal + + " Leaves: " + root.localLeaves; + } + StatText { + visible: root.expanded + text: "LOD: " + root.lodStatus; + } + } + } + } + + Rectangle { + y: 250 + visible: root.timingExpanded + width: perfText.width + 8 + height: perfText.height + 8 + color: root.bgColor; + StatText { + x: 4; y: 4 + id: perfText + font.family: root.monospaceFont + text: "------------------------------------------ Function " + + "--------------------------------------- --msecs- -calls--\n" + + root.timingStats; + } + } + + Connections { + target: root.parent + onWidthChanged: { + root.x = root.parent.width - root.width; + } + } + } + +} diff --git a/interface/resources/qml/CurrentAPI.qml b/interface/resources/qml/CurrentAPI.qml index 45cf09925a..37984965d9 100644 --- a/interface/resources/qml/CurrentAPI.qml +++ b/interface/resources/qml/CurrentAPI.qml @@ -16,10 +16,9 @@ import "controls-uit" as HifiControls Item { id: root width: parent.width - height: parent.height + height: parent.height property var hideQtMethods: true - property var maxUpdateValues: 20 property var maxReloadValues: 200 property var apiMembers: [] @@ -30,7 +29,7 @@ Item { property Component keyboard Rectangle { - color: "white" + color: hifi.colors.baseGray width: parent.width height: parent.height } @@ -51,32 +50,22 @@ Item { Row { id: topBar anchors.left: parent.left - anchors.leftMargin: 8 + anchors.leftMargin: 30 + anchors.top: parent.top + anchors.topMargin: 30 width: parent.width - height: 50 - HifiControls.GlyphButton { - id: search - enabled: true - glyph: hifi.glyphs.search - color: hifi.colors.text - size: 48 - width: 50 - height: 50 - onClicked: { - addListElements(searchBar.text); - focus = true; - } - } + height: 40 HifiControls.GlyphButton { id: back; enabled: true; + color: hifi.buttons.black glyph: hifi.glyphs.backward - color: hifi.colors.text - size: 48 - width: 30 - height: 50 - anchors.margins: 2 + size: 40 + width: 40 + height: 40 + anchors.left: search.right + anchors.leftMargin: 12 onClicked: { var text = searchBar.text; var chain = text.split("."); @@ -99,17 +88,20 @@ Item { } } - TextField { - id: searchBar + HifiControls.TextField { + id: searchBar focus: true - font.pixelSize: 16 - width: 2*(parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3 - height: parent.height - font.family: ralewayRegular.name + isSearchField: true + width: parent.width - 112 + height: 40 + colorScheme: hifi.colorSchemes.dark + anchors.left: back.right + anchors.leftMargin: 10 + font.family: firaSansSemiBold.name placeholderText: "Search" onAccepted: { console.log("Enter Pressed"); - search.clicked(); + addListElements(searchBar.text); } onActiveFocusChanged: { if (activeFocus && HMD.mounted) { @@ -119,15 +111,27 @@ Item { } } - } + } + } - HifiControls.Button { + Row { + id: topBar2 + anchors.left: parent.left + anchors.leftMargin: 30 + anchors.top: topBar.bottom + anchors.topMargin: 30 + width: parent.width -60 + height: 40 + + HifiControls.GlyphButton { id: addMember; enabled: true; - text: "+" - width: 50 - height: 50 - anchors.margins: 2 + color: hifi.buttons.black + glyph: hifi.glyphs.maximize + width: 40 + height: 40 + anchors.top: parent.top + anchors.left: parent.left onClicked: { addNewMember(); updateList.start(); @@ -138,36 +142,48 @@ Item { HifiControls.Button { id: evaluate; enabled: true; + color: hifi.buttons.black text: "Eval" - width: 50 - height: 50 - anchors.margins: 2 + width: 40 + height: 40 + anchors.left: addMember.right + anchors.leftMargin: 12 onClicked: { evaluateMember(); focus = true; } } - TextField { - id: valueBar - focus: true + + HifiControls.TextField { + id: valueBar + isSearchField: false font.pixelSize: 16 - width: (parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3 - height: parent.height - font.family: ralewayRegular.name + width: parent.width - 208 + height: 40 + colorScheme: hifi.colorSchemes.dark + font.family: firaSansSemiBold.name placeholderText: "Value" - textColor: "#4466DD" - anchors.margins: 2 + anchors.left: evaluate.right + anchors.leftMargin: 12 + onActiveFocusChanged: { + if (activeFocus && HMD.mounted) { + keyboard.raised = true; + } else { + keyboard.raised = false; + } + } } HifiControls.GlyphButton { id: reload; enabled: false; + color: hifi.buttons.black glyph: hifi.glyphs.reload - color: hifi.colors.text - size: 48 - width: 50 - height: 50 - anchors.margins: 2 + size: 40 + width: 40 + height: 40 + anchors.right: update.left + anchors.rightMargin: 12 onClicked: { reloadListValues(); focus = true; @@ -177,11 +193,12 @@ Item { HifiControls.GlyphButton { id: update; enabled: false; + color: hifi.buttons.black glyph: hifi.glyphs.playback_play - size: 48 - width: 50 - height: 50 - anchors.margins: 2 + size: 40 + width: 40 + height: 40 + anchors.right: parent.right onClicked: { if (isReloading) { update.glyph = hifi.glyphs.playback_play @@ -196,71 +213,104 @@ Item { } } } - - ListModel { - id: memberModel - } - Component { - id: memberDelegate - - Row { - id: memberRow - property var isMainKey: apiType === "class"; - spacing: 10 - Rectangle { - width: isMainKey ? 20 : 40; - height: parent.height - } - - RalewayRegular { - text: apiMember - size: !isMainKey ? 16 : 22 - MouseArea { - width: list.width - height: parent.height - onClicked: { - searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember; - valueBar.text = !apiValue ? "" : apiValue; - list.currentIndex = index; - evaluatingIdx = index; - } - onDoubleClicked: { - if (apiType === "class") { - addListElements(apiMember+"."); - } else { - isolateElement(evaluatingIdx); - } - - } - } - } - - - RalewayRegular { - text: apiType - size: 14 - color: hifi.colors.baseGrayHighlight - } - - RalewayRegular { - text: !apiValue ? "" : apiValue; - size: 16 - color: "#4466DD" - } - } - } - - Rectangle { id: membersBackground anchors { - left: parent.left; right: parent.right; top: topBar.bottom; bottom: parent.bottom; - margins: hifi.dimensions.contentMargin.x - bottomMargin: hifi.dimensions.contentSpacing.y + 40 + left: parent.left; right: parent.right; top: topBar2.bottom; bottom: bottomBar.top; + margins: 30 + } + color: hifi.colors.tableBackgroundDark + border.color: hifi.colors.lightGray + border.width: 2 + radius: 5 + + ListModel { + id: memberModel + } + + Component { + id: memberDelegate + Item { + id: item + width: parent.width + anchors.left: parent.left + height: 26 + clip: true + + Rectangle { + width: parent.width + height: parent.height + color: index % 2 == 0 ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd + anchors.verticalCenter: parent.verticalCenter + + Row { + id: memberRow + anchors.bottom: parent.bottom + anchors.verticalCenter: parent.verticalCenter + spacing: 10 + + FiraSansSemiBold { + property var isMainKey: apiType === "class"; + text: apiMember + size: isMainKey ? 17 : 15 + font.bold: true + anchors.verticalCenter: parent.verticalCenter + color: isMainKey ? hifi.colors.faintGray : hifi.colors.lightGrayText + MouseArea { + width: list.width + height: parent.height + onClicked: { + searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember; + valueBar.text = !apiValue ? "" : apiValue; + list.currentIndex = index; + evaluatingIdx = index; + } + onDoubleClicked: { + if (apiType === "class") { + addListElements(apiMember+"."); + } else { + isolateElement(evaluatingIdx); + } + } + } + } + + FiraSansRegular { + text: apiType + anchors.left: apiMember.right + anchors.verticalCenter: parent.verticalCenter + size: 13 + color: hifi.colors.lightGrayText + } + + FiraSansRegular { + text: !apiValue ? "" : apiValue; + anchors.left: apiType.right + anchors.verticalCenter: parent.verticalCenter + size: 14 + color: hifi.colors.primaryHighlight + } + } + } + } + } + + Component { + id: highlight + Rectangle { + anchors { + left: list.left + right: scrollBar.left + leftMargin: 2 + rightMargin: 2 + } + color: hifi.colors.primaryHighlight + radius: 4 + z: 10 + opacity: 0.5 + } } - color: "white" - radius: 4 ListView { id: list @@ -269,23 +319,16 @@ Item { left: parent.left right: scrollBar.left bottom: parent.bottom - margins: 4 + topMargin: 2 + leftMargin: 2 + bottomMargin: 2 } clip: true cacheBuffer: 4000 model: memberModel delegate: memberDelegate highlightMoveDuration: 0 - - highlight: Rectangle { - anchors { - left: parent ? parent.left : undefined - right: parent ? parent.right : undefined - leftMargin: hifi.dimensions.borderWidth - rightMargin: hifi.dimensions.borderWidth - } - color: "#BBDDFF" - } + highlight: highlight onMovementStarted: { scrollSlider.manual = true; } @@ -310,12 +353,11 @@ Item { top: parent.top right: parent.right bottom: parent.bottom - topMargin: 4 - bottomMargin: 4 + margins: 2 } - width: scrolling ? 18 : 0 - radius: 4 - color: hifi.colors.baseGrayShadow + width: 22 + height: parent.height - 4 + color: hifi.colors.tableScrollBackgroundDark MouseArea { anchors.fill: parent @@ -344,14 +386,12 @@ Item { y = index*(scrollBar.height - scrollSlider.height)/(list.count - 1); } - anchors { - right: parent.right - rightMargin: 3 - } - width: 12 - height: (list.height / list.contentHeight) * list.height - radius: width / 4 - color: "white" + anchors.right: parent.right + anchors.margins: 2 + width: 18 + height: ((list.height / list.contentHeight) * list.height) < 15 ? 15 : (list.height / list.contentHeight) * list.height + radius: 5 + color: hifi.colors.tableScrollHandleDark visible: scrollBar.scrolling; @@ -373,66 +413,75 @@ Item { } } } - - HifiControls.GlyphButton { - id: clipboard; - enabled: true; - glyph: hifi.glyphs.scriptNew - size: 38 - width: 50 - height: 50 + + Row { + id: bottomBar anchors.left: parent.left + anchors.leftMargin: 30 anchors.bottom: parent.bottom - anchors.margins: 2 - anchors.leftMargin: 8 - onClicked: { - var buffer = ""; - for (var i = 0; i < memberModel.count; i++) { - var datarow = memberModel.get(i); - buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue; - } - Window.copyToClipboard(buffer); - focus = true; - } - } - - HifiControls.Button { - id: debug; - enabled: true; - text: "Debug Script" - width: 120 - height: 50 - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: 2 - anchors.rightMargin: 8 - onClicked: { - sendToScript({type: "selectScript"}); - } - } + anchors.bottomMargin: 30 + width: parent.width + height: 40 - HifiControls.CheckBox { - id: hideQt - boxSize: 25 - boxRadius: 3 - checked: true - anchors.left: clipboard.right - anchors.leftMargin: 8 - anchors.verticalCenter: clipboard.verticalCenter - anchors.margins: 2 - onClicked: { - hideQtMethods = checked; - addListElements(); + HifiControls.GlyphButton { + id: clipboard; + enabled: true; + color: hifi.buttons.black + glyph: hifi.glyphs.scriptNew + size: 25 + width: 40 + height: 40 + anchors.left: parent.left + onClicked: { + var buffer = ""; + for (var i = 0; i < memberModel.count; i++) { + var datarow = memberModel.get(i); + buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue; + } + Window.copyToClipboard(buffer); + focus = true; + } } - } - HifiControls.Label { - id: hideLabel - anchors.left: hideQt.right - anchors.verticalCenter: clipboard.verticalCenter - anchors.margins: 2 - font.pixelSize: 15 - text: "Hide Qt Methods" + HifiControls.CheckBox { + id: hideQt + colorScheme: hifi.checkbox.dark + boxSize: 25 + boxRadius: 3 + checked: true + anchors.left: clipboard.right + anchors.leftMargin: 10 + anchors.verticalCenter: clipboard.verticalCenter + onClicked: { + hideQtMethods = checked; + addListElements(); + } + } + + HifiControls.Label { + id: hideLabel + anchors.left: hideQt.right + anchors.verticalCenter: clipboard.verticalCenter + anchors.margins: 2 + font.pixelSize: 15 + text: "Hide Qt Methods" + } + + HifiControls.Button { + id: debug; + enabled: true; + color: hifi.buttons.black + text: "Debug Script" + width: 120 + height: 40 + anchors.right: parent.right + anchors.rightMargin: 60 + anchors.bottom: parent.bottom + + onClicked: { + sendToScript({type: "selectScript"}); + } + } } HifiControls.Keyboard { @@ -639,4 +688,4 @@ Item { } signal sendToScript(var message); -} \ No newline at end of file +} diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index b942c8b4ab..a21d1f8efd 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -24,10 +24,13 @@ TextField { property bool isSearchField: false property string label: "" property real controlHeight: height + (textFieldLabel.visible ? textFieldLabel.height + 1 : 0) + property bool hasDefocusedBorder: true; property bool hasRoundedBorder: false + property int roundedBorderRadius: 4 property bool error: false; property bool hasClearButton: false; - property string leftPlaceholderGlyph: ""; + property string leftPermanentGlyph: ""; + property string centerPlaceholderGlyph: ""; placeholderText: textField.placeholderText @@ -101,12 +104,12 @@ TextField { } } border.color: textField.error ? hifi.colors.redHighlight : - (textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray)) + (textField.activeFocus ? hifi.colors.primaryHighlight : (hasDefocusedBorder ? (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray) : color)) border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0 - radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0) + radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? roundedBorderRadius : 0) HiFiGlyphs { - text: textField.leftPlaceholderGlyph; + text: textField.leftPermanentGlyph; color: textColor; size: hifi.fontSizes.textFieldSearchIcon; anchors.left: parent.left; @@ -115,6 +118,15 @@ TextField { visible: text; } + HiFiGlyphs { + text: textField.centerPlaceholderGlyph; + color: textColor; + size: parent.height; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.verticalCenter: parent.verticalCenter; + visible: text && !textField.focus && textField.text === ""; + } + HiFiGlyphs { text: hifi.glyphs.search color: textColor @@ -145,7 +157,7 @@ TextField { placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray selectedTextColor: hifi.colors.black selectionColor: hifi.colors.primaryHighlight - padding.left: ((isSearchField || textField.leftPlaceholderGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding + padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding } diff --git a/interface/resources/qml/hifi/+android/Desktop.qml b/interface/resources/qml/hifi/+android/Desktop.qml new file mode 100644 index 0000000000..99d792b664 --- /dev/null +++ b/interface/resources/qml/hifi/+android/Desktop.qml @@ -0,0 +1,63 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +import Qt.labs.settings 1.0 + +import "../desktop" as OriginalDesktop +import ".." +import "." +import "./toolbars" + +OriginalDesktop.Desktop { + id: desktop + + MouseArea { + id: hoverWatch + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + scrollGestureEnabled: false // we don't need/want these + onEntered: ApplicationCompositor.reticleOverDesktop = true + onExited: ApplicationCompositor.reticleOverDesktop = false + acceptedButtons: Qt.NoButton + + + } + + + Component { id: toolbarBuilder; Toolbar { } } + // This used to create sysToolbar dynamically with a call to getToolbar() within onCompleted. + // Beginning with QT 5.6, this stopped working, as anything added to toolbars too early got + // wiped during startup. + Toolbar { + id: sysToolbar; + objectName: "com.highfidelity.interface.toolbar.system"; + // Magic: sysToolbar.x and y come from settings, and are bound before the properties specified here are applied. + x: sysToolbar.x; + y: sysToolbar.y; + } + property var toolbars: (function (map) { // answer dictionary preloaded with sysToolbar + map[sysToolbar.objectName] = sysToolbar; + return map; })({}); + + Component.onCompleted: { + } + + // Accept a download through the webview + property bool webViewProfileSetup: false + property string currentUrl: "" + property string adaptedPath: "" + property string tempDir: "" + + // Create or fetch a toolbar with the given name + function getToolbar(name) { + var result = toolbars[name]; + if (!result) { + result = toolbars[name] = toolbarBuilder.createObject(desktop, {}); + result.objectName = name; + } + return result; + } +} + + diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index 8576e26fcd..a9629c0e4e 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -102,7 +102,7 @@ Column { 'include_actions=' + actions, 'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'), 'require_online=true', - 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()), + 'protocol=' + encodeURIComponent(Window.protocolSignature()), 'page=' + pageNumber ]; var url = metaverseBase + 'user_stories?' + options.join('&'); diff --git a/interface/resources/qml/hifi/commerce/common/SortableListModel.qml b/interface/resources/qml/hifi/commerce/common/SortableListModel.qml index 2d82e42ddb..0951d25950 100644 --- a/interface/resources/qml/hifi/commerce/common/SortableListModel.qml +++ b/interface/resources/qml/hifi/commerce/common/SortableListModel.qml @@ -17,6 +17,7 @@ ListModel { id: root; property string sortColumnName: ""; property bool isSortingDescending: true; + property bool valuesAreNumerical: false; function swap(a, b) { if (a < b) { @@ -29,26 +30,51 @@ ListModel { } function partition(begin, end, pivot) { - var piv = get(pivot)[sortColumnName]; - swap(pivot, end - 1); - var store = begin; + if (valuesAreNumerical) { + var piv = get(pivot)[sortColumnName]; + swap(pivot, end - 1); + var store = begin; - for (var i = begin; i < end - 1; ++i) { - if (isSortingDescending) { - if (get(i)[sortColumnName] < piv) { - swap(store, i); - ++store; - } - } else { - if (get(i)[sortColumnName] > piv) { - swap(store, i); - ++store; + for (var i = begin; i < end - 1; ++i) { + var currentElement = get(i)[sortColumnName]; + if (isSortingDescending) { + if (currentElement < piv) { + swap(store, i); + ++store; + } + } else { + if (currentElement > piv) { + swap(store, i); + ++store; + } } } - } - swap(end - 1, store); + swap(end - 1, store); - return store; + return store; + } else { + var piv = get(pivot)[sortColumnName].toLowerCase(); + swap(pivot, end - 1); + var store = begin; + + for (var i = begin; i < end - 1; ++i) { + var currentElement = get(i)[sortColumnName].toLowerCase(); + if (isSortingDescending) { + if (currentElement < piv) { + swap(store, i); + ++store; + } + } else { + if (currentElement > piv) { + swap(store, i); + ++store; + } + } + } + swap(end - 1, store); + + return store; + } } function qsort(begin, end) { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 87b784bc4e..a9961dc17e 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -677,7 +677,7 @@ Rectangle { } } - if (sameItemCount !== tempPurchasesModel.count) { + if (sameItemCount !== tempPurchasesModel.count || filterBar.text !== "") { filteredPurchasesModel.clear(); for (var i = 0; i < tempPurchasesModel.count; i++) { filteredPurchasesModel.append(tempPurchasesModel.get(i)); diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml index d825196655..634a68d117 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Security.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Security.qml @@ -193,7 +193,7 @@ Item { color: hifi.colors.white; } - // "Change Passphrase" button + // "Change Security Pic" button HifiControlsUit.Button { id: changeSecurityImageButton; color: hifi.buttons.blue; diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml index 2ad2b75553..86a4220b74 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml +++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml @@ -34,13 +34,11 @@ Item { securityImageChangePageSecurityImage.source = "image://security/securityImage"; if (exists) { // Success submitting new security image if (root.justSubmitted) { - root.resetSubmitButton(); sendSignalToWallet({method: "walletSecurity_changeSecurityImageSuccess"}); root.justSubmitted = false; } } else if (root.justSubmitted) { // Error submitting new security image. - root.resetSubmitButton(); root.justSubmitted = false; } } @@ -180,7 +178,8 @@ Item { // "Submit" button HifiControlsUit.Button { id: securityImageSubmitButton; - enabled: securityImageSelection.currentIndex !== -1; + text: root.justSubmitted ? "Submitting..." : "Submit"; + enabled: securityImageSelection.currentIndex !== -1 && !root.justSubmitted; color: hifi.buttons.blue; colorScheme: hifi.colorSchemes.dark; anchors.top: parent.top; @@ -188,11 +187,8 @@ Item { anchors.right: parent.right; anchors.rightMargin: 20; width: 150; - text: "Submit"; onClicked: { root.justSubmitted = true; - securityImageSubmitButton.text = "Submitting..."; - securityImageSubmitButton.enabled = false; var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex()) Commerce.chooseSecurityImage(securityImagePath); } @@ -205,11 +201,6 @@ Item { signal sendSignalToWallet(var msg); - function resetSubmitButton() { - securityImageSubmitButton.enabled = true; - securityImageSubmitButton.text = "Submit"; - } - function initModel() { securityImageSelection.initModel(); } diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml index 1f5e67eaa5..56b78c5865 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml @@ -24,7 +24,7 @@ Item { HifiConstants { id: hifi; } id: root; - property int currentIndex: securityImageGrid.currentIndex; + property alias currentIndex: securityImageGrid.currentIndex; // This will cause a bug -- if you bring up security image selection in HUD mode while // in HMD while having HMD preview enabled, then move, then finish passphrase selection, @@ -98,6 +98,11 @@ Item { function initModel() { gridModel.initModel(); + securityImageGrid.currentIndex = -1; + } + + function resetSelection() { + securityImageGrid.currentIndex = -1; } // // FUNCTION DEFINITIONS END diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index da67569bc3..ae42b8e3e1 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -389,7 +389,7 @@ Rectangle { // Item { id: tabButtonsContainer; - visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange"; + visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendMoneyStep"; property int numTabs: 5; // Size width: root.width; diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 81d38bc0dd..b980c13e3c 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -392,7 +392,6 @@ Item { width: 118; height: paintedHeight; wrapMode: Text.WordWrap; - font.bold: true; // Alignment horizontalAlignment: Text.AlignRight; } diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index 21da38def0..fd74b07465 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -348,6 +348,7 @@ Item { width: 200; text: "Back" onClicked: { + securityImageSelection.resetSelection(); root.activeView = "step_1"; } } @@ -516,6 +517,7 @@ Item { width: 200; text: "Back" onClicked: { + securityImageSelection.resetSelection(); root.lastPage = "step_3"; root.activeView = "step_2"; } diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml index c2d9ef3b19..33cd43bb05 100644 --- a/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml @@ -29,13 +29,13 @@ Item { property string userName; property string profilePicUrl; - height: 65; + height: 75; width: parent.width; Rectangle { id: mainContainer; // Style - color: root.isSelected ? hifi.colors.faintGray : hifi.colors.white; + color: root.isSelected ? hifi.colors.faintGray80 : hifi.colors.white; // Size anchors.left: parent.left; anchors.right: parent.right; @@ -49,7 +49,7 @@ Item { anchors.verticalCenter: parent.verticalCenter; anchors.left: parent.left; anchors.leftMargin: 36; - height: root.height - 15; + height: 50; width: visible ? height : 0; clip: true; Image { @@ -83,15 +83,15 @@ Item { RalewaySemiBold { id: userName; anchors.left: avatarImage.right; - anchors.leftMargin: 16; + anchors.leftMargin: 12; anchors.top: parent.top; anchors.bottom: parent.bottom; anchors.right: chooseButton.visible ? chooseButton.left : parent.right; anchors.rightMargin: chooseButton.visible ? 10 : 0; // Text size - size: 20; + size: 18; // Style - color: hifi.colors.baseGray; + color: hifi.colors.blueAccent; text: root.userName; elide: Text.ElideRight; // Alignment @@ -107,9 +107,9 @@ Item { colorScheme: hifi.colorSchemes.dark; anchors.verticalCenter: parent.verticalCenter; anchors.right: parent.right; - anchors.rightMargin: 24; - height: root.height - 20; - width: 110; + anchors.rightMargin: 28; + height: 35; + width: 100; text: "CHOOSE"; onClicked: { var msg = { method: 'chooseConnection', userName: root.userName, profilePicUrl: root.profilePicUrl }; diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml index 267cf0ed51..43636d47ca 100644 --- a/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml @@ -29,6 +29,7 @@ Item { property string displayName; property string userName; property string profilePic; + property string textColor: hifi.colors.white; Item { visible: root.isDisplayingNearby; @@ -46,7 +47,7 @@ Item { // Text size size: 18; // Style - color: hifi.colors.baseGray; + color: root.textColor; verticalAlignment: Text.AlignBottom; elide: Text.ElideRight; } @@ -63,7 +64,7 @@ Item { // Text size size: 16; // Style - color: hifi.colors.baseGray; + color: root.textColor; verticalAlignment: Text.AlignTop; elide: Text.ElideRight; } @@ -108,7 +109,7 @@ Item { // Text size size: 16; // Style - color: hifi.colors.baseGray; + color: root.textColor; verticalAlignment: Text.AlignVCenter; elide: Text.ElideRight; } diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml index 9164ba7a8c..f66781c919 100644 --- a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml @@ -29,8 +29,9 @@ Item { property int parentAppNavBarHeight; property string currentActiveView: "sendMoneyHome"; property string nextActiveView: ""; - property bool isCurrentlyFullScreen: chooseRecipientConnection.visible || - chooseRecipientNearby.visible || sendMoneyStep.visible || paymentSuccess.visible || paymentFailure.visible; + property bool shouldShowTopAndBottomOfWallet: chooseRecipientConnection.visible || + chooseRecipientNearby.visible || paymentSuccess.visible || paymentFailure.visible; + property bool shouldShowTopOfWallet: sendMoneyStep.visible; property bool isCurrentlySendingMoney: false; // This object is always used in a popup or full-screen Wallet section. @@ -38,9 +39,9 @@ Item { // able to click on a button/mouseArea underneath the popup/section. MouseArea { x: 0; - y: root.isCurrentlyFullScreen ? 0 : root.parentAppTitleBarHeight; + y: (root.shouldShowTopAndBottomOfWallet && !root.shouldShowTopOfWallet) ? 0 : root.parentAppTitleBarHeight; width: parent.width; - height: root.isCurrentlyFullScreen ? parent.height : parent.height - root.parentAppTitleBarHeight - root.parentAppNavBarHeight; + height: (root.shouldShowTopAndBottomOfWallet || root.shouldShowTopOfWallet) ? parent.height : parent.height - root.parentAppTitleBarHeight - root.parentAppNavBarHeight; propagateComposedEvents: false; } @@ -105,7 +106,8 @@ Item { // Send Money Home BEGIN Item { id: sendMoneyHome; - visible: root.currentActiveView === "sendMoneyHome"; + z: 996; + visible: root.currentActiveView === "sendMoneyHome" || root.currentActiveView === "chooseRecipientConnection" || root.currentActiveView === "chooseRecipientNearby"; anchors.fill: parent; anchors.topMargin: root.parentAppTitleBarHeight; anchors.bottomMargin: root.parentAppNavBarHeight; @@ -194,6 +196,17 @@ Item { anchors.right: parent.right; anchors.bottom: parent.bottom; height: 440; + + + LinearGradient { + anchors.fill: parent; + start: Qt.point(0, 0); + end: Qt.point(0, height); + gradient: Gradient { + GradientStop { position: 0.0; color: hifi.colors.white } + GradientStop { position: 1.0; color: hifi.colors.faintGray } + } + } RalewaySemiBold { id: sendMoneyText; @@ -223,12 +236,13 @@ Item { Image { anchors.top: parent.top; - source: "../images/wallet-bg.jpg"; - height: 60; + source: "./images/connection.svg"; + height: 70; width: parent.width; fillMode: Image.PreserveAspectFit; horizontalAlignment: Image.AlignHCenter; verticalAlignment: Image.AlignTop; + mipmap: true; } RalewaySemiBold { @@ -248,6 +262,7 @@ Item { anchors.fill: parent; onClicked: { root.nextActiveView = "chooseRecipientConnection"; + filterBar.text = ""; } } } @@ -264,12 +279,13 @@ Item { Image { anchors.top: parent.top; - source: "../images/wallet-bg.jpg"; - height: 60; + source: "./images/nearby.svg"; + height: 70; width: parent.width; fillMode: Image.PreserveAspectFit; horizontalAlignment: Image.AlignHCenter; verticalAlignment: Image.AlignTop; + mipmap: true; } RalewaySemiBold { @@ -299,9 +315,18 @@ Item { // Choose Recipient Connection BEGIN Rectangle { id: chooseRecipientConnection; + z: 997; visible: root.currentActiveView === "chooseRecipientConnection"; anchors.fill: parent; - color: "#AAAAAA"; + color: "#80000000"; + + // This object is always used in a popup or full-screen Wallet section. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + anchors.fill: parent; + propagateComposedEvents: false; + } ListModel { id: connectionsModel; @@ -314,6 +339,8 @@ Item { anchors.centerIn: parent; width: parent.width - 30; height: parent.height - 30; + color: "#FFFFFF"; + radius: 8; RalewaySemiBold { id: chooseRecipientText_connections; @@ -322,11 +349,11 @@ Item { anchors.top: parent.top; anchors.topMargin: 26; anchors.left: parent.left; - anchors.leftMargin: 20; + anchors.leftMargin: 36; width: paintedWidth; height: 30; // Text size - size: 22; + size: 18; // Style color: hifi.colors.baseGray; } @@ -334,6 +361,7 @@ Item { HiFiGlyphs { id: closeGlyphButton_connections; text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; size: 26; anchors.top: parent.top; anchors.topMargin: 10; @@ -363,7 +391,7 @@ Item { height: 40; // Anchors anchors.left: parent.left; - anchors.leftMargin: 16; + anchors.leftMargin: 36; anchors.right: parent.right; anchors.rightMargin: 16; anchors.top: chooseRecipientText_connections.bottom; @@ -374,8 +402,10 @@ Item { colorScheme: hifi.colorSchemes.faintGray; hasClearButton: true; hasRoundedBorder: true; + hasDefocusedBorder: false; + roundedBorderRadius: filterBar.height/2; anchors.fill: parent; - placeholderText: "filter recipients"; + centerPlaceholderGlyph: hifi.glyphs.search; onTextChanged: { buildFilteredConnectionsModel(); @@ -461,17 +491,28 @@ Item { // Choose Recipient Nearby BEGIN Rectangle { id: chooseRecipientNearby; + z: 997; + color: "#80000000"; property string selectedRecipient; visible: root.currentActiveView === "chooseRecipientNearby"; anchors.fill: parent; - color: "#AAAAAA"; + + // This object is always used in a popup or full-screen Wallet section. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + anchors.fill: parent; + propagateComposedEvents: false; + } Rectangle { anchors.centerIn: parent; width: parent.width - 30; height: parent.height - 30; + color: "#FFFFFF"; + radius: 8; RalewaySemiBold { text: "Choose Recipient:"; @@ -483,7 +524,7 @@ Item { width: paintedWidth; height: 30; // Text size - size: 22; + size: 18; // Style color: hifi.colors.baseGray; } @@ -491,6 +532,7 @@ Item { HiFiGlyphs { id: closeGlyphButton_nearby; text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; size: 26; anchors.top: parent.top; anchors.topMargin: 10; @@ -512,163 +554,142 @@ Item { } } - Item { - id: selectionInstructionsContainer; - visible: chooseRecipientNearby.selectedRecipient === ""; - anchors.fill: parent; - - RalewaySemiBold { - id: selectionInstructions_deselected; - text: "Click/trigger on an avatar nearby to select them..."; - // Anchors - anchors.bottom: parent.bottom; - anchors.bottomMargin: 200; - anchors.left: parent.left; - anchors.leftMargin: 58; - anchors.right: parent.right; - anchors.rightMargin: anchors.leftMargin; - height: paintedHeight; - // Text size - size: 20; - // Style - color: hifi.colors.baseGray; - horizontalAlignment: Text.AlignHCenter; - wrapMode: Text.Wrap; - } + RalewaySemiBold { + id: selectionInstructions; + text: chooseRecipientNearby.selectedRecipient === "" ? "Trigger or click on\nsomeone nearby to select them" : + "Trigger or click on\nsomeone else to select again"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 100; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: anchors.leftMargin; + height: paintedHeight; + // Text size + size: 20; + // Style + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignHCenter; + wrapMode: Text.Wrap; } - Item { + Image { + anchors.top: selectionInstructions.bottom; + anchors.topMargin: 20; + anchors.bottom: selectionMadeContainer.top; + anchors.bottomMargin: 30; + source: "./images/p2p-nearby-unselected.svg"; + width: parent.width; + fillMode: Image.PreserveAspectFit; + horizontalAlignment: Image.AlignHCenter; + verticalAlignment: Image.AlignVCenter; + mipmap: true; + } + + Rectangle { id: selectionMadeContainer; - visible: !selectionInstructionsContainer.visible; - anchors.fill: parent; + visible: chooseRecipientNearby.selectedRecipient !== ""; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 190; + color: "#F3F3F3"; + radius: 8; + + // Used to square off the top left and top right edges of this container + Rectangle { + color: "#F3F3F3"; + height: selectionMadeContainer.radius; + anchors.top: selectionMadeContainer.top; + anchors.left: selectionMadeContainer.left; + anchors.right: selectionMadeContainer.right; + } RalewaySemiBold { id: sendToText; - text: "Send To:"; + text: "Send to:"; // Anchors anchors.top: parent.top; - anchors.topMargin: 120; + anchors.topMargin: 36; anchors.left: parent.left; - anchors.leftMargin: 12; + anchors.leftMargin: 36; width: paintedWidth; height: paintedHeight; // Text size - size: 20; + size: 18; // Style color: hifi.colors.baseGray; } + Image { + id: selectedImage; + anchors.top: parent.top; + anchors.topMargin: 24; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 32; + anchors.left: sendToText.right; + anchors.leftMargin: 4; + source: "./images/p2p-nearby-selected.svg"; + width: 50; + fillMode: Image.PreserveAspectFit; + horizontalAlignment: Image.AlignHCenter; + verticalAlignment: Image.AlignVCenter; + mipmap: true; + } + RalewaySemiBold { id: avatarDisplayName; text: '"' + AvatarList.getAvatar(chooseRecipientNearby.selectedRecipient).sessionDisplayName + '"'; // Anchors - anchors.top: sendToText.bottom; - anchors.topMargin: 60; - anchors.left: parent.left; - anchors.leftMargin: 30; + anchors.top: parent.top; + anchors.topMargin: 34; + anchors.left: selectedImage.right; + anchors.leftMargin: 10; anchors.right: parent.right; - anchors.rightMargin: 30; + anchors.rightMargin: 10; height: paintedHeight; // Text size - size: 22; + size: 20; // Style - horizontalAlignment: Text.AlignHCenter; - color: hifi.colors.baseGray; - } - - RalewaySemiBold { - id: avatarNodeID; - text: chooseRecipientNearby.selectedRecipient; - // Anchors - anchors.top: avatarDisplayName.bottom; - anchors.topMargin: 6; - anchors.left: parent.left; - anchors.leftMargin: 30; - anchors.right: parent.right; - anchors.rightMargin: 30; - height: paintedHeight; - // Text size - size: 14; - // Style - horizontalAlignment: Text.AlignHCenter; - color: hifi.colors.lightGrayText; + color: hifi.colors.blueAccent; } RalewaySemiBold { id: avatarUserName; text: sendMoneyStep.selectedRecipientUserName; // Anchors - anchors.top: avatarNodeID.bottom; - anchors.topMargin: 12; - anchors.left: parent.left; - anchors.leftMargin: 30; + anchors.top: avatarDisplayName.bottom; + anchors.topMargin: 16; + anchors.left: selectedImage.right; + anchors.leftMargin: 10; anchors.right: parent.right; - anchors.rightMargin: 30; + anchors.rightMargin: 10; height: paintedHeight; // Text size - size: 22; + size: 18; // Style - horizontalAlignment: Text.AlignHCenter; color: hifi.colors.baseGray; } - RalewaySemiBold { - id: selectionInstructions_selected; - text: "Click/trigger on another avatar nearby to select them...\n\nor press 'Next' to continue."; - // Anchors + // "CHOOSE" button + HifiControlsUit.Button { + id: chooseButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.horizontalCenter: parent.horizontalCenter; anchors.bottom: parent.bottom; - anchors.bottomMargin: 200; - anchors.left: parent.left; - anchors.leftMargin: 58; - anchors.right: parent.right; - anchors.rightMargin: anchors.leftMargin; - height: paintedHeight; - // Text size - size: 20; - // Style - color: hifi.colors.baseGray; - horizontalAlignment: Text.AlignHCenter; - wrapMode: Text.Wrap; - } - } + anchors.bottomMargin: 20; + height: 40; + width: 110; + text: "CHOOSE"; + onClicked: { + sendMoneyStep.referrer = "nearby"; + sendMoneyStep.selectedRecipientNodeID = chooseRecipientNearby.selectedRecipient; + chooseRecipientNearby.selectedRecipient = ""; - // "Cancel" button - HifiControlsUit.Button { - id: cancelButton; - color: hifi.buttons.noneBorderless; - colorScheme: hifi.colorSchemes.dark; - anchors.left: parent.left; - anchors.leftMargin: 60; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 80; - height: 50; - width: 120; - text: "Cancel"; - onClicked: { - root.nextActiveView = "sendMoneyHome"; - resetSendMoneyData(); - } - } - - // "Next" button - HifiControlsUit.Button { - id: nextButton; - enabled: chooseRecipientNearby.selectedRecipient !== ""; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; - anchors.rightMargin: 60; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 80; - height: 50; - width: 120; - text: "Next"; - onClicked: { - sendMoneyStep.referrer = "nearby"; - sendMoneyStep.selectedRecipientNodeID = chooseRecipientNearby.selectedRecipient; - chooseRecipientNearby.selectedRecipient = ""; - - root.nextActiveView = "sendMoneyStep"; + root.nextActiveView = "sendMoneyStep"; + } } } } @@ -676,9 +697,9 @@ Item { // Choose Recipient Nearby END // Send Money Screen BEGIN - Rectangle { + Item { id: sendMoneyStep; - z: 997; + z: 996; property string referrer; // either "connections" or "nearby" property string selectedRecipientNodeID; @@ -688,284 +709,333 @@ Item { visible: root.currentActiveView === "sendMoneyStep"; anchors.fill: parent; - color: "#AAAAAA"; + anchors.topMargin: root.parentAppTitleBarHeight; - Rectangle { - anchors.centerIn: parent; - width: parent.width - 30; - height: parent.height - 30; + RalewaySemiBold { + id: sendMoneyText_sendMoneyStep; + text: "Send Money"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 26; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: paintedWidth; + height: 30; + // Text size + size: 22; + // Style + color: hifi.colors.white; + } + + Item { + id: sendToContainer; + anchors.top: sendMoneyText_sendMoneyStep.bottom; + anchors.topMargin: 20; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; RalewaySemiBold { - id: sendMoneyText_sendMoneyStep; - text: "Send Money To:"; + id: sendToText_sendMoneyStep; + text: "Send to:"; // Anchors anchors.top: parent.top; - anchors.topMargin: 26; anchors.left: parent.left; - anchors.leftMargin: 20; - width: paintedWidth; - height: 30; + anchors.bottom: parent.bottom; + width: 90; // Text size - size: 22; + size: 18; // Style - color: hifi.colors.baseGray; + color: hifi.colors.white; + verticalAlignment: Text.AlignVCenter; } - Item { - id: sendToContainer; - anchors.top: sendMoneyText_sendMoneyStep.bottom; - anchors.topMargin: 20; - anchors.left: parent.left; - anchors.leftMargin: 20; + RecipientDisplay { + anchors.top: parent.top; + anchors.left: sendToText_sendMoneyStep.right; + anchors.right: changeButton.left; + anchors.rightMargin: 12; + height: parent.height; + + displayName: sendMoneyStep.selectedRecipientDisplayName; + userName: sendMoneyStep.selectedRecipientUserName; + profilePic: sendMoneyStep.selectedRecipientProfilePic !== "" ? ((0 === sendMoneyStep.selectedRecipientProfilePic.indexOf("http")) ? + sendMoneyStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendMoneyStep.selectedRecipientProfilePic)) : ""; + isDisplayingNearby: sendMoneyStep.referrer === "nearby"; + } + + // "CHANGE" button + HifiControlsUit.Button { + id: changeButton; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; anchors.right: parent.right; - anchors.rightMargin: 20; - height: 80; - - RalewaySemiBold { - id: sendToText_sendMoneyStep; - text: "Send To:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.bottom: parent.bottom; - width: 90; - // Text size - size: 18; - // Style - color: hifi.colors.baseGray; - verticalAlignment: Text.AlignVCenter; - } - - RecipientDisplay { - anchors.top: parent.top; - anchors.left: sendToText_sendMoneyStep.right; - anchors.right: changeButton.left; - anchors.rightMargin: 12; - height: parent.height; - - displayName: sendMoneyStep.selectedRecipientDisplayName; - userName: sendMoneyStep.selectedRecipientUserName; - profilePic: sendMoneyStep.selectedRecipientProfilePic !== "" ? ((0 === sendMoneyStep.selectedRecipientProfilePic.indexOf("http")) ? - sendMoneyStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendMoneyStep.selectedRecipientProfilePic)) : ""; - isDisplayingNearby: sendMoneyStep.referrer === "nearby"; - } - - // "CHANGE" button - HifiControlsUit.Button { - id: changeButton; - color: hifi.buttons.black; - colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; - anchors.verticalCenter: parent.verticalCenter; - height: 35; - width: 120; - text: "CHANGE"; - onClicked: { - if (sendMoneyStep.referrer === "connections") { - root.nextActiveView = "chooseRecipientConnection"; - } else if (sendMoneyStep.referrer === "nearby") { - root.nextActiveView = "chooseRecipientNearby"; - } - resetSendMoneyData(); + anchors.verticalCenter: parent.verticalCenter; + height: 35; + width: 100; + text: "CHANGE"; + onClicked: { + if (sendMoneyStep.referrer === "connections") { + root.nextActiveView = "chooseRecipientConnection"; + } else if (sendMoneyStep.referrer === "nearby") { + root.nextActiveView = "chooseRecipientNearby"; } + resetSendMoneyData(); + } + } + } + + Item { + id: amountContainer; + anchors.top: sendToContainer.bottom; + anchors.topMargin: 2; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; + + RalewaySemiBold { + id: amountText; + text: "Amount:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: 90; + // Text size + size: 18; + // Style + color: hifi.colors.white; + verticalAlignment: Text.AlignVCenter; + } + + HifiControlsUit.TextField { + id: amountTextField; + colorScheme: hifi.colorSchemes.dark; + inputMethodHints: Qt.ImhDigitsOnly; + // Anchors + anchors.verticalCenter: parent.verticalCenter; + anchors.left: amountText.right; + anchors.right: parent.right; + height: 50; + // Style + leftPermanentGlyph: hifi.glyphs.hfc; + activeFocusOnPress: true; + activeFocusOnTab: true; + + validator: IntValidator { bottom: 0; } + + onAccepted: { + optionalMessage.focus = true; } } - Item { - id: amountContainer; - anchors.top: sendToContainer.bottom; + FiraSansSemiBold { + visible: amountTextFieldError.text === ""; + text: "Balance: "; + // Anchors + anchors.top: amountTextField.bottom; anchors.topMargin: 2; - anchors.left: parent.left; - anchors.leftMargin: 20; - anchors.right: parent.right; - anchors.rightMargin: 20; - height: 80; - - RalewaySemiBold { - id: amountText; - text: "Amount:"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.bottom: parent.bottom; - width: 90; - // Text size - size: 18; - // Style - color: hifi.colors.baseGray; - verticalAlignment: Text.AlignVCenter; - } - - HifiControlsUit.TextField { - id: amountTextField; - colorScheme: hifi.colorSchemes.light; - inputMethodHints: Qt.ImhDigitsOnly; - // Anchors - anchors.verticalCenter: parent.verticalCenter; - anchors.left: amountText.right; - anchors.right: parent.right; - height: 50; - // Style - leftPlaceholderGlyph: hifi.glyphs.hfc; - activeFocusOnPress: true; - activeFocusOnTab: true; - - validator: IntValidator { bottom: 0; } - - onAccepted: { - optionalMessage.focus = true; - } - } - - RalewaySemiBold { - id: amountTextFieldError; - // Anchors - anchors.top: amountTextField.bottom; - anchors.topMargin: 2; - anchors.left: amountTextField.left; - anchors.right: amountTextField.right; - height: 40; - // Text size - size: 16; - // Style - color: hifi.colors.baseGray; - verticalAlignment: Text.AlignTop; - horizontalAlignment: Text.AlignRight; - } + anchors.left: amountTextField.left; + anchors.right: sendMoneyBalanceText_HFC.left; + width: paintedWidth; + height: 40; + // Text size + size: 16; + // Style + color: hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + HiFiGlyphs { + id: sendMoneyBalanceText_HFC; + visible: amountTextFieldError.text === ""; + text: hifi.glyphs.hfc; + // Size + size: 16; + // Anchors + anchors.top: amountTextField.bottom; + anchors.topMargin: 2; + anchors.right: sendMoneyBalanceText.left; + width: paintedWidth; + height: 40; + // Style + color: hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + FiraSansSemiBold { + id: sendMoneyBalanceText; + visible: amountTextFieldError.text === ""; + text: balanceText.text; + // Anchors + anchors.top: amountTextField.bottom; + anchors.topMargin: 2; + anchors.right: amountTextField.right; + width: paintedWidth; + height: 40; + // Text size + size: 16; + // Style + color: hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; } - Item { - id: messageContainer; - anchors.top: amountContainer.bottom; - anchors.topMargin: 16; - anchors.left: parent.left; - anchors.leftMargin: 20; - anchors.right: parent.right; - anchors.rightMargin: 20; - height: 140; + RalewaySemiBold { + id: amountTextFieldError; + // Anchors + anchors.top: amountTextField.bottom; + anchors.topMargin: 2; + anchors.left: amountTextField.left; + anchors.right: amountTextField.right; + height: 40; + // Text size + size: 16; + // Style + color: hifi.colors.white; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + } + + Item { + id: messageContainer; + anchors.top: amountContainer.bottom; + anchors.topMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 140; - FontLoader { id: firaSansSemiBold; source: "../../../../../fonts/FiraSans-SemiBold.ttf"; } - TextArea { - id: optionalMessage; - property int maximumLength: 70; - property string previousText: text; - placeholderText: "Optional Message"; - font.family: firaSansSemiBold.name; - font.pixelSize: 20; - // Anchors + FontLoader { id: firaSansSemiBold; source: "../../../../../fonts/FiraSans-SemiBold.ttf"; } + TextArea { + id: optionalMessage; + property int maximumLength: 72; + property string previousText: text; + placeholderText: "Optional Message (" + maximumLength + " character limit)"; + font.family: firaSansSemiBold.name; + font.pixelSize: 20; + // Anchors + anchors.fill: parent; + // Style + background: Rectangle { anchors.fill: parent; - // Style - background: Rectangle { - anchors.fill: parent; - color: optionalMessage.activeFocus ? hifi.colors.white : hifi.colors.textFieldLightBackground; - border.width: optionalMessage.activeFocus ? 1 : 0; - border.color: optionalMessage.activeFocus ? hifi.colors.primaryHighlight : hifi.colors.textFieldLightBackground; - } - color: hifi.colors.black; - textFormat: TextEdit.PlainText; - wrapMode: TextEdit.Wrap; - activeFocusOnPress: true; - activeFocusOnTab: true; - // Workaround for no max length on TextAreas - onTextChanged: { - if (text.length > maximumLength) { - var cursor = cursorPosition; - text = previousText; - if (cursor > text.length) { - cursorPosition = text.length; - } else { - cursorPosition = cursor-1; - } - } - previousText = text; - } + color: optionalMessage.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow; + border.width: optionalMessage.activeFocus ? 1 : 0; + border.color: optionalMessage.activeFocus ? hifi.colors.primaryHighlight : hifi.colors.textFieldLightBackground; } - RalewaySemiBold { - id: optionalMessageCharacterCount; - text: optionalMessage.text.length + "/" + optionalMessage.maximumLength; - // Anchors - anchors.top: optionalMessage.bottom; - anchors.topMargin: 2; - anchors.right: optionalMessage.right; - height: paintedHeight; - // Text size - size: 16; - // Style - color: hifi.colors.baseGray; - verticalAlignment: Text.AlignTop; - horizontalAlignment: Text.AlignRight; + color: hifi.colors.white; + textFormat: TextEdit.PlainText; + wrapMode: TextEdit.Wrap; + activeFocusOnPress: true; + activeFocusOnTab: true; + // Workaround for no max length on TextAreas + onTextChanged: { + if (text.length > maximumLength) { + var cursor = cursorPosition; + text = previousText; + if (cursor > text.length) { + cursorPosition = text.length; + } else { + cursorPosition = cursor-1; + } + } + previousText = text; + } + } + FiraSansSemiBold { + id: optionalMessageCharacterCount; + text: optionalMessage.text.length + "/" + optionalMessage.maximumLength; + // Anchors + anchors.top: optionalMessage.bottom; + anchors.topMargin: 4; + anchors.right: optionalMessage.right; + height: paintedHeight; + // Text size + size: 16; + // Style + color: optionalMessage.text.length === optionalMessage.maximumLength ? "#ea89a5" : hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + } + + HifiControlsUit.CheckBox { + id: sendPubliclyCheckbox; + visible: false; // FIXME ONCE PARTICLE EFFECTS ARE IN + text: "Send Publicly" + // Anchors + anchors.top: messageContainer.bottom; + anchors.topMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 16; + boxSize: 24; + } + + Item { + id: bottomBarContainer; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 20; + height: 60; + + // "CANCEL" button + HifiControlsUit.Button { + id: cancelButton_sendMoneyStep; + color: hifi.buttons.noneBorderlessWhite; + colorScheme: hifi.colorSchemes.dark; + anchors.left: parent.left; + anchors.leftMargin: 24; + anchors.verticalCenter: parent.verticalCenter; + height: 40; + width: 150; + text: "CANCEL"; + onClicked: { + resetSendMoneyData(); + root.nextActiveView = "sendMoneyHome"; } } - Item { - id: bottomBarContainer; - anchors.top: messageContainer.bottom; - anchors.topMargin: 30; - anchors.left: parent.left; - anchors.leftMargin: 20; + // "SEND" button + HifiControlsUit.Button { + id: sendButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; anchors.right: parent.right; - anchors.rightMargin: 20; - height: 80; - - HifiControlsUit.CheckBox { - id: sendPubliclyCheckbox; - visible: false; // FIXME ONCE PARTICLE EFFECTS ARE IN - text: "Send Publicly" - // Anchors - anchors.verticalCenter: parent.verticalCenter; - anchors.left: parent.left; - anchors.right: cancelButton_sendMoneyStep.left; - anchors.rightMargin: 16; - boxSize: 24; - } - - // "CANCEL" button - HifiControlsUit.Button { - id: cancelButton_sendMoneyStep; - color: hifi.buttons.noneBorderless; - colorScheme: hifi.colorSchemes.dark; - anchors.right: sendButton.left; - anchors.rightMargin: 16; - anchors.verticalCenter: parent.verticalCenter; - height: 35; - width: 100; - text: "CANCEL"; - onClicked: { - resetSendMoneyData(); - root.nextActiveView = "sendMoneyHome"; - } - } - - // "SEND" button - HifiControlsUit.Button { - id: sendButton; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; - anchors.verticalCenter: parent.verticalCenter; - height: 35; - width: 100; - text: "SEND"; - onClicked: { - if (parseInt(amountTextField.text) > parseInt(balanceText.text)) { - amountTextField.focus = true; - amountTextField.error = true; - amountTextFieldError.text = "amount exceeds available funds"; - } else if (amountTextField.text === "" || parseInt(amountTextField.text) < 1) { - amountTextField.focus = true; - amountTextField.error = true; - amountTextFieldError.text = "invalid amount"; - } else { - amountTextFieldError.text = ""; - amountTextField.error = false; - root.isCurrentlySendingMoney = true; - amountTextField.focus = false; - optionalMessage.focus = false; - if (sendMoneyStep.referrer === "connections") { - Commerce.transferHfcToUsername(sendMoneyStep.selectedRecipientUserName, parseInt(amountTextField.text), optionalMessage.text); - } else if (sendMoneyStep.referrer === "nearby") { - Commerce.transferHfcToNode(sendMoneyStep.selectedRecipientNodeID, parseInt(amountTextField.text), optionalMessage.text); - } + anchors.rightMargin: 24; + anchors.verticalCenter: parent.verticalCenter; + height: 40; + width: 150; + text: "SUBMIT"; + onClicked: { + if (parseInt(amountTextField.text) > parseInt(balanceText.text)) { + amountTextField.focus = true; + amountTextField.error = true; + amountTextFieldError.text = "amount exceeds available funds"; + } else if (amountTextField.text === "" || parseInt(amountTextField.text) < 1) { + amountTextField.focus = true; + amountTextField.error = true; + amountTextFieldError.text = "invalid amount"; + } else { + amountTextFieldError.text = ""; + amountTextField.error = false; + root.isCurrentlySendingMoney = true; + amountTextField.focus = false; + optionalMessage.focus = false; + if (sendMoneyStep.referrer === "connections") { + Commerce.transferHfcToUsername(sendMoneyStep.selectedRecipientUserName, parseInt(amountTextField.text), optionalMessage.text); + } else if (sendMoneyStep.referrer === "nearby") { + Commerce.transferHfcToNode(sendMoneyStep.selectedRecipientNodeID, parseInt(amountTextField.text), optionalMessage.text); } } } @@ -981,7 +1051,7 @@ Item { visible: root.isCurrentlySendingMoney; anchors.fill: parent; - color: Qt.rgba(0.0, 0.0, 0.0, 0.5); + color: Qt.rgba(0.0, 0.0, 0.0, 0.8); // This object is always used in a popup or full-screen Wallet section. // This MouseArea is used to prevent a user from being @@ -993,11 +1063,10 @@ Item { AnimatedImage { id: sendingMoneyImage; - source: "../../../../../icons/profilePicLoading.gif" - width: 160; + source: "./images/loader.gif" + width: 96; height: width; - anchors.top: parent.top; - anchors.topMargin: 185; + anchors.verticalCenter: parent.verticalCenter; anchors.horizontalCenter: parent.horizontalCenter; } @@ -1005,11 +1074,11 @@ Item { text: "Sending"; // Anchors anchors.top: sendingMoneyImage.bottom; - anchors.topMargin: 22; + anchors.topMargin: 4; anchors.horizontalCenter: parent.horizontalCenter; width: paintedWidth; // Text size - size: 24; + size: 26; // Style color: hifi.colors.white; verticalAlignment: Text.AlignVCenter; @@ -1018,17 +1087,17 @@ Item { // Sending Money Overlay END // Payment Success BEGIN - Rectangle { + Item { id: paymentSuccess; visible: root.currentActiveView === "paymentSuccess"; anchors.fill: parent; - color: "#AAAAAA"; Rectangle { anchors.centerIn: parent; width: parent.width - 30; height: parent.height - 30; + color: "#FFFFFF"; RalewaySemiBold { id: paymentSentText; @@ -1049,6 +1118,7 @@ Item { HiFiGlyphs { id: closeGlyphButton_paymentSuccess; text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; size: 26; anchors.top: parent.top; anchors.topMargin: 10; @@ -1100,6 +1170,7 @@ Item { anchors.left: sendToText_paymentSuccess.right; anchors.right: parent.right; height: parent.height; + textColor: hifi.colors.blueAccent; displayName: sendMoneyStep.selectedRecipientDisplayName; userName: sendMoneyStep.selectedRecipientUserName; @@ -1145,10 +1216,10 @@ Item { anchors.verticalCenter: parent.verticalCenter; height: 50; // Style - color: hifi.colors.baseGray; + color: hifi.colors.blueAccent; } - RalewaySemiBold { + FiraSansSemiBold { id: amountSentText; text: amountTextField.text; // Anchors @@ -1159,7 +1230,7 @@ Item { height: 50; // Style size: 22; - color: hifi.colors.baseGray; + color: hifi.colors.blueAccent; } } @@ -1177,7 +1248,7 @@ Item { // Text size size: 22; // Style - color: hifi.colors.baseGray; + color: hifi.colors.blueAccent; wrapMode: Text.Wrap; verticalAlignment: Text.AlignTop; } @@ -1203,17 +1274,17 @@ Item { // Payment Success END // Payment Failure BEGIN - Rectangle { + Item { id: paymentFailure; visible: root.currentActiveView === "paymentFailure"; anchors.fill: parent; - color: "#AAAAAA"; Rectangle { anchors.centerIn: parent; width: parent.width - 30; height: parent.height - 30; + color: "#FFFFFF"; RalewaySemiBold { id: paymentFailureText; @@ -1234,6 +1305,7 @@ Item { HiFiGlyphs { id: closeGlyphButton_paymentFailure; text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; size: 26; anchors.top: parent.top; anchors.topMargin: 10; @@ -1303,6 +1375,7 @@ Item { anchors.left: sentToText_paymentFailure.right; anchors.right: parent.right; height: parent.height; + textColor: hifi.colors.baseGray; displayName: sendMoneyStep.selectedRecipientDisplayName; userName: sendMoneyStep.selectedRecipientUserName; @@ -1351,7 +1424,7 @@ Item { color: hifi.colors.baseGray; } - RalewaySemiBold { + FiraSansSemiBold { id: amountSentText_paymentFailure; text: amountTextField.text; // Anchors diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/connection.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/connection.svg new file mode 100644 index 0000000000..7c5403fda3 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/connection.svg @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif new file mode 100644 index 0000000000..0536bd1884 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif differ diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/nearby.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/nearby.svg new file mode 100644 index 0000000000..dec87e658d --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/nearby.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-selected.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-selected.svg new file mode 100644 index 0000000000..59635a99b1 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-selected.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-unselected.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-unselected.svg new file mode 100644 index 0000000000..bba07b9567 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-unselected.svg @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/interface/resources/qml/hifi/dialogs/AdvancedPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/AdvancedPreferencesDialog.qml new file mode 100644 index 0000000000..2d80a1c330 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/AdvancedPreferencesDialog.qml @@ -0,0 +1,29 @@ +// +// AdvancedPreferencesDialog.qml +// +// Created by Brad Hefta-Gaub on 20 Jan 2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import Qt.labs.settings 1.0 + +import "../../dialogs" + +PreferencesDialog { + id: root + objectName: "AdvancedPreferencesDialog" + title: "Advanced Settings" + showCategories: ["Advanced UI" ] + property var settings: Settings { + category: root.objectName + property alias x: root.x + property alias y: root.y + property alias width: root.width + property alias height: root.height + } +} + diff --git a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml index 94665794d6..6f5798e2b2 100644 --- a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml @@ -17,7 +17,7 @@ PreferencesDialog { id: root objectName: "GeneralPreferencesDialog" title: "General Settings" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Game Controller", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"] + showCategories: ["UI", "Snapshots", "Privacy", "HMD", "Game Controller", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"] property var settings: Settings { category: root.objectName property alias x: root.x diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e556ee2f03..c2b22b3772 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -14,17 +14,20 @@ #include #include -#include #include #include #include #include #include +#include + +#include #include #include #include #include +#include #include #include @@ -49,6 +52,7 @@ #include +#include #include #include #include @@ -209,9 +213,8 @@ #include "commerce/QmlCommerce.h" #include "webbrowser/WebBrowserSuggestionsEngine.h" +#include -// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU -// FIXME seems to be broken. #if defined(Q_OS_WIN) #include @@ -226,11 +229,17 @@ #undef QT_BOOTSTRAPPED #endif +// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU +// FIXME seems to be broken. extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } #endif +#if defined(Q_OS_ANDROID) +#include +#endif + enum ApplicationEvent { // Execute a lambda function Lambda = QEvent::User + 1, @@ -240,7 +249,6 @@ enum ApplicationEvent { Idle, }; - class RenderEventHandler : public QObject { using Parent = QObject; Q_OBJECT @@ -304,9 +312,19 @@ static QTimer locationUpdateTimer; static QTimer identityPacketTimer; static QTimer pingTimer; -static const QString DISABLE_WATCHDOG_FLAG("HIFI_DISABLE_WATCHDOG"); +#if defined(Q_OS_ANDROID) +static bool DISABLE_WATCHDOG = true; +#else +static const QString DISABLE_WATCHDOG_FLAG{ "HIFI_DISABLE_WATCHDOG" }; static bool DISABLE_WATCHDOG = QProcessEnvironment::systemEnvironment().contains(DISABLE_WATCHDOG_FLAG); +#endif +#if defined(USE_GLES) +static bool DISABLE_DEFERRED = true; +#else +static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" }; +static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD); +#endif static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16; @@ -333,7 +351,7 @@ static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; -static const QString INFO_HELP_PATH = "../../../html/tabletHelp.html"; +static const QString INFO_HELP_PATH = "html/tabletHelp.html"; static const unsigned int THROTTLED_SIM_FRAMERATE = 15; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; @@ -503,7 +521,13 @@ public: } if (message->message == WM_DEVICECHANGE) { - Midi::USBchanged(); // re-scan the MIDI bus + const float MIN_DELTA_SECONDS = 2.0f; // de-bounce signal + static float lastTriggerTime = 0.0f; + const float deltaSeconds = secTimestampNow() - lastTriggerTime; + lastTriggerTime = secTimestampNow(); + if (deltaSeconds > MIN_DELTA_SECONDS) { + Midi::USBchanged(); // re-scan the MIDI bus + } } } return false; @@ -530,6 +554,26 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt #ifdef Q_OS_WIN OutputDebugStringA(logMessage.toLocal8Bit().constData()); OutputDebugStringA("\n"); +#elif defined Q_OS_ANDROID + const char * local=logMessage.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(); + } #endif qApp->getLogger()->addMessage(qPrintable(logMessage + "\n")); } @@ -573,8 +617,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { } }; reportAndQuit("--protocolVersion", [&](FILE* fp) { - DependencyManager::set(); - auto version = DependencyManager::get()->protocolVersion(); + auto version = protocolVersionsSignatureBase64(); fputs(version.toLatin1().data(), fp); }); reportAndQuit("--version", [&](FILE* fp) { @@ -594,6 +637,24 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { qApp->setProperty(hifi::properties::APP_LOCAL_DATA_PATH, cacheDir); } + // FIXME fix the OSX installer to install the resources.rcc binary instead of resource files and remove + // this conditional exclusion +#if !defined(Q_OS_OSX) + { +#if defined(Q_OS_ANDROID) + const QString resourcesBinaryFile = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources.rcc"; +#else + const QString resourcesBinaryFile = QCoreApplication::applicationDirPath() + "/resources.rcc"; +#endif + if (!QFile::exists(resourcesBinaryFile)) { + throw std::runtime_error("Unable to find primary resources"); + } + if (!QResource::registerResource(resourcesBinaryFile)) { + throw std::runtime_error("Unable to load primary resources"); + } + } +#endif + Setting::init(); // Tell the plugin manager about our statically linked plugins @@ -632,15 +693,16 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(std::bind(&Application::getUserAgent, qApp)); DependencyManager::set(); DependencyManager::set(ScriptEngine::CLIENT_SCRIPT); DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(NodeType::Agent, listenPort); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -729,8 +791,10 @@ OverlayID _keyboardFocusHighlightID{ UNKNOWN_OVERLAY_ID }; // // So instead we create a new offscreen context to share with the QGLWidget, // and manually set THAT to be the shared context for the Chromium helper +#if !defined(DISABLE_QML) OffscreenGLCanvas* _chromiumShareContext { nullptr }; Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); +#endif Setting::Handle sessionRunTime{ "sessionRunTime", 0 }; @@ -776,6 +840,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _sampleSound(nullptr) { + auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); setProperty(hifi::properties::CRASHED, _previousSessionCrashed); @@ -996,8 +1061,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get().data(), &AddressManager::storeCurrentAddress); // Inititalize sample before registering - QFileInfo infSample = QFileInfo(PathUtils::resourcesPath() + "sounds/sample.wav"); - _sampleSound = DependencyManager::get()->getSound(QUrl::fromLocalFile(infSample.absoluteFilePath())); + _sampleSound = DependencyManager::get()->getSound(PathUtils::resourcesUrl("sounds/sample.wav")); auto scriptEngines = DependencyManager::get().data(); scriptEngines->registerScriptInitializer([this](ScriptEnginePointer engine){ @@ -1082,9 +1146,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Make sure we don't time out during slow operations at startup updateHeartbeat(); - // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. - DependencyManager::get()->initializeShapePipelines(); - // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. // The value will be 0 if the user blew away settings this session, which is both a feature and a bug. static const QString TESTER = "HIFI_TESTER"; @@ -1375,8 +1436,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo userInputMapper->registerDevice(_touchscreenDevice->getInputDevice()); } - // force the model the look at the correct directory (weird order of operations issue) - scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation()); + // this will force the model the look at the correct directory (weird order of operations issue) + scriptEngines->reloadLocalFiles(); + // do this as late as possible so that all required subsystems are initialized // If we've overridden the default scripts location, just load default scripts // otherwise, load 'em all @@ -1730,7 +1792,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo static int lastCountOfNearbyAvatars = -1; QTimer* checkNearbyAvatarsTimer = new QTimer(this); checkNearbyAvatarsTimer->setInterval(CHECK_NEARBY_AVATARS_INTERVAL_MS); // 10 seconds, Qt::CoarseTimer ok - connect(checkNearbyAvatarsTimer, &QTimer::timeout, this, [this]() { + connect(checkNearbyAvatarsTimer, &QTimer::timeout, this, []() { auto avatarManager = DependencyManager::get(); int nearbyAvatars = avatarManager->numberOfAvatarsInRange(avatarManager->getMyAvatar()->getWorldPosition(), NEARBY_AVATAR_RADIUS_METERS) - 1; @@ -1769,9 +1831,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo PROFILE_RANGE(render, "Process Default Skybox"); auto textureCache = DependencyManager::get(); - auto skyboxUrl = PathUtils::resourcesPath().toStdString() + "images/Default-Sky-9-cubemap.ktx"; + QFileSelector fileSelector; + fileSelector.setExtraSelectors(FileUtils::getFileSelectors()); + auto skyboxUrl = fileSelector.select(PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.ktx"); - _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl); + _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl.toStdString()); _defaultSkyboxAmbientTexture = _defaultSkyboxTexture; _defaultSkybox->setCubemap(_defaultSkyboxTexture); @@ -1782,8 +1846,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return entityServerNode && !isPhysicsEnabled(); }); - QFileInfo infSnap = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav"); - _snapshotSound = DependencyManager::get()->getSound(QUrl::fromLocalFile(infSnap.absoluteFilePath())); + _snapshotSound = DependencyManager::get()->getSound(PathUtils::resourcesUrl("sounds/snap.wav")); QVariant testProperty = property(hifi::properties::TEST); qDebug() << testProperty; @@ -2181,11 +2244,16 @@ void Application::initializeGL() { } _glWidget->makeCurrent(); - _chromiumShareContext = new OffscreenGLCanvas(); - _chromiumShareContext->setObjectName("ChromiumShareContext"); - _chromiumShareContext->create(_glWidget->qglContext()); - _chromiumShareContext->makeCurrent(); - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + +#if !defined(DISABLE_QML) + if (!nsightActive()) { + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); + _chromiumShareContext->create(_glWidget->qglContext()); + _chromiumShareContext->makeCurrent(); + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + } +#endif _glWidget->makeCurrent(); gpu::Context::init(); @@ -2202,13 +2270,17 @@ void Application::initializeGL() { // Set up the render engine render::CullFunctor cullFunctor = LODManager::shouldRender; static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; - bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD); _renderEngine->addJob("UpdateScene"); - _renderEngine->addJob("SecondaryCameraJob", cullFunctor); - _renderEngine->addJob("RenderMainView", cullFunctor, isDeferred); +#ifndef Q_OS_ANDROID + _renderEngine->addJob("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED); +#endif + _renderEngine->addJob("RenderMainView", cullFunctor, !DISABLE_DEFERRED); _renderEngine->load(); _renderEngine->registerScene(_main3DScene); + // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. + DependencyManager::get()->initializeShapePipelines(); + _offscreenContext = new OffscreenGLCanvas(); _offscreenContext->setObjectName("MainThreadContext"); _offscreenContext->create(_glWidget->qglContext()); @@ -2313,8 +2385,7 @@ void Application::initializeUi() { offscreenUi->setProxyWindow(_window->windowHandle()); // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to // support the window management and scripting proxies for VR use - offscreenUi->createDesktop(QString("hifi/Desktop.qml")); - + offscreenUi->createDesktop(PathUtils::qmlUrl("hifi/Desktop.qml")); // FIXME either expose so that dialogs can set this themselves or // do better detection in the offscreen UI of what has focus offscreenUi->setNavigationFocused(false); @@ -2653,7 +2724,8 @@ void Application::showHelp() { queryString.addQueryItem("defaultTab", defaultTab); auto tabletScriptingInterface = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); - tablet->gotoWebScreen(INFO_HELP_PATH + "?" + queryString.toString()); + tablet->gotoWebScreen(PathUtils::resourcesPath() + INFO_HELP_PATH + "?" + queryString.toString()); + DependencyManager::get()->openTablet(); //InfoView::show(INFO_HELP_PATH, false, queryString.toString()); } @@ -3909,12 +3981,18 @@ void Application::idle() { PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get()->getStat("PendingProcessing").toInt()); auto renderConfig = _renderEngine->getConfiguration(); PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_gpuContext->getFrameTimerGPUAverage()); + auto opaqueRangeTimer = renderConfig->getConfig("OpaqueRangeTimer"); + auto linearDepth = renderConfig->getConfig("LinearDepth"); + auto surfaceGeometry = renderConfig->getConfig("SurfaceGeometry"); + auto renderDeferred = renderConfig->getConfig("RenderDeferred"); + auto toneAndPostRangeTimer = renderConfig->getConfig("ToneAndPostRangeTimer"); + PROFILE_COUNTER(render_detail, "gpuTimes", { - { "OpaqueRangeTimer", renderConfig->getConfig("OpaqueRangeTimer")->property("gpuRunTime") }, - { "LinearDepth", renderConfig->getConfig("LinearDepth")->property("gpuRunTime") }, - { "SurfaceGeometry", renderConfig->getConfig("SurfaceGeometry")->property("gpuRunTime") }, - { "RenderDeferred", renderConfig->getConfig("RenderDeferred")->property("gpuRunTime") }, - { "ToneAndPostRangeTimer", renderConfig->getConfig("ToneAndPostRangeTimer")->property("gpuRunTime") } + { "OpaqueRangeTimer", opaqueRangeTimer ? opaqueRangeTimer->property("gpuRunTime") : 0 }, + { "LinearDepth", linearDepth ? linearDepth->property("gpuRunTime") : 0 }, + { "SurfaceGeometry", surfaceGeometry ? surfaceGeometry->property("gpuRunTime") : 0 }, + { "RenderDeferred", renderDeferred ? renderDeferred->property("gpuRunTime") : 0 }, + { "ToneAndPostRangeTimer", toneAndPostRangeTimer ? toneAndPostRangeTimer->property("gpuRunTime") : 0 } }); PROFILE_RANGE(app, __FUNCTION__); @@ -4279,9 +4357,9 @@ void Application::init() { // Make sure Login state is up to date DependencyManager::get()->toggleLoginDialog(); - - DependencyManager::get()->init(); - + if (!DISABLE_DEFERRED) { + DependencyManager::get()->init(); + } DependencyManager::get()->init(); _timerStart.start(); @@ -5760,10 +5838,13 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, LocationScriptingInterface::locationSetter); +#if !defined(Q_OS_ANDROID) scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor); +#endif scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor); scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); + scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Stats", Stats::getInstance()); scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get().data()); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2a33ee6f7b..b129d44c04 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -756,6 +756,13 @@ Menu::Menu() { // Developer > Stats addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats); + // Developer > Advanced Settings... + action = addActionToQMenuAndActionHash(developerMenu, "Advanced Preferences..."); + connect(action, &QAction::triggered, [] { + qApp->showDialog(QString("hifi/dialogs/AdvancedPreferencesDialog.qml"), + QString("hifi/tablet/AdvancedPreferencesDialog.qml"), "AdvancedPreferencesDialog"); + }); + // Developer > API Debugger action = addActionToQMenuAndActionHash(developerMenu, "API Debugger"); connect(action, &QAction::triggered, [] { diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 4cfa4d6156..5db34c9441 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -203,10 +203,14 @@ public: } }; -void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor) { +void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { const auto cachedArg = task.addJob("SecondaryCamera"); const auto items = task.addJob("FetchCullSort", cullFunctor); assert(items.canCast()); - task.addJob("RenderDeferredTask", items); + if (!isDeferred) { + task.addJob("Forward", items); + } else { + task.addJob("RenderDeferredTask", items); + } task.addJob("EndSecondaryCamera", cachedArg); } \ No newline at end of file diff --git a/interface/src/SecondaryCamera.h b/interface/src/SecondaryCamera.h index 38d75c391e..026b72d865 100644 --- a/interface/src/SecondaryCamera.h +++ b/interface/src/SecondaryCamera.h @@ -71,7 +71,7 @@ public: using JobModel = render::Task::Model; SecondaryCameraRenderTask() {} void configure(const Config& config) {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true); }; #endif diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 11496f727e..2e633986a0 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1797,7 +1797,7 @@ void MyAvatar::initAnimGraph() { } else if (!_fstAnimGraphOverrideUrl.isEmpty()) { graphUrl = _fstAnimGraphOverrideUrl; } else { - graphUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json"); + graphUrl = PathUtils::resourcesUrl("avatar/avatar-animation.json"); } _skeletonModel->getRig().initAnimGraph(graphUrl); @@ -2019,8 +2019,7 @@ void MyAvatar::updateOrientation(float deltaTime) { _smoothOrientationTimer = 0.0f; } - getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); - + Head* head = getHead(); auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD); if (headPose.isValid()) { glm::quat localOrientation = headPose.rotation * Quaternions::Y_180; @@ -2032,6 +2031,10 @@ void MyAvatar::updateOrientation(float deltaTime) { head->setBaseYaw(YAW(euler)); head->setBasePitch(PITCH(euler)); head->setBaseRoll(ROLL(euler)); + } else { + head->setBaseYaw(0.0f); + head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); + head->setBaseRoll(0.0f); } } diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 88bdb1f405..10ddd4c110 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -17,6 +17,7 @@ #include "Ledger.h" #include "CommerceLogging.h" #include +#include // inventory answers {status: 'success', data: {assets: [{id: "guid", title: "name", preview: "url"}....]}} // balance answers {status: 'success', data: {balance: integer}} @@ -122,25 +123,30 @@ QString hfcString(const QJsonValue& sentValue, const QJsonValue& receivedValue) int sent = sentValue.toInt(); int received = receivedValue.toInt(); if (sent <= 0 && received <= 0) { - return QString("-"); + return QString("0 HFC"); } QString result; if (sent > 0) { - result += QString("-%1 HFC").arg(sent); + result += QString("-%1 HFC").arg(sent); if (received > 0) { result += QString("
"); } } if (received > 0) { - result += QString("%1 HFC").arg(received); + result += QString("%1 HFC").arg(received); } return result; } static const QString USER_PAGE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/users/"; +static const QString PLACE_PAGE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/places/"; static const QStringList KNOWN_USERS(QStringList() << "highfidelity" << "marketplace"); -QString userLink(const QString& username) { +QString userLink(const QString& username, const QString& placename) { if (username.isEmpty()) { - return QString("someone"); + if (placename.isEmpty()) { + return QString("someone"); + } else { + return QString("someone nearby").arg(PLACE_PAGE_BASE_URL, placename); + } } if (KNOWN_USERS.contains(username)) { return username; @@ -157,13 +163,13 @@ QString transactionString(const QJsonObject& valueObject) { QDateTime createdAt(QDateTime::fromSecsSinceEpoch(dateInteger, Qt::UTC)); QString result; - if (sentCerts <= 0 && receivedCerts <= 0) { + if (sentCerts <= 0 && receivedCerts <= 0 && !KNOWN_USERS.contains(valueObject["sender_name"].toString())) { // this is an hfc transfer. if (sent > 0) { - QString recipient = userLink(valueObject["recipient_name"].toString()); + QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString()); result += QString("Money sent to %1").arg(recipient); } else { - QString sender = userLink(valueObject["sender_name"].toString()); + QString sender = userLink(valueObject["sender_name"].toString(), valueObject["place_name"].toString()); result += QString("Money from %1").arg(sender); } if (!message.isEmpty()) { @@ -172,8 +178,8 @@ QString transactionString(const QJsonObject& valueObject) { } else { result += valueObject["message"].toString(); } + // no matter what we append a smaller date to the bottom of this... - result += QString("
%1").arg(createdAt.toLocalTime().toString(Qt::DefaultLocaleShortDate)); return result; } @@ -314,6 +320,7 @@ void Ledger::transferHfcToNode(const QString& hfc_key, const QString& nodeID, co transaction["node_id"] = nodeID; transaction["quantity"] = amount; transaction["message"] = optionalMessage; + transaction["place_name"] = DependencyManager::get()->getPlaceName(); QJsonDocument transactionDoc{ transaction }; auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_node", "transferHfcToNodeSuccess", "transferHfcToNodeFailure"); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 8b73042ada..9599af827f 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -591,8 +591,8 @@ void Wallet::chooseSecurityImage(const QString& filename) { if (_securityImage) { delete _securityImage; } - QString path = qApp->applicationDirPath(); - path.append("/resources/qml/hifi/commerce/wallet/"); + QString path = PathUtils::resourcesPath(); + path.append("/qml/hifi/commerce/wallet/"); path.append(filename); // now create a new security image pixmap diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 5c07bebc23..80ec78f118 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -53,6 +52,14 @@ int main(int argc, const char* argv[]) { QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); #endif +#if defined(USE_GLES) && defined(Q_OS_WIN) + // When using GLES on Windows, we can't create normal GL context in Qt, so + // we force Qt to use angle. This will cause the QML to be unable to be used + // in the output window, so QML should be disabled. + qputenv("QT_ANGLE_PLATFORM", "d3d11"); + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); +#endif + disableQtBearerPoll(); // Fixes wifi ping spikes QElapsedTimer startupTime; diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index fad6fe47ae..8c0fb59106 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -44,7 +44,7 @@ OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { QVariantMap overlayProperties; // TODO: make these configurable per pointer overlayProperties["name"] = "stylus"; - overlayProperties["url"] = PathUtils::resourcesPath() + "/meshes/tablet-stylus-fat.fbx"; + overlayProperties["url"] = PathUtils::resourcesUrl() + "/meshes/tablet-stylus-fat.fbx"; overlayProperties["loadPriority"] = 10.0f; overlayProperties["solid"] = true; overlayProperties["visible"] = false; diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 72b6f401e9..c13b5b2e0d 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -39,8 +39,7 @@ AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() { void AssetMappingsScriptingInterface::setMapping(QString path, QString hash, QJSValue callback) { auto assetClient = DependencyManager::get(); auto request = assetClient->createSetMappingRequest(path, hash); - - connect(request, &SetMappingRequest::finished, this, [this, callback](SetMappingRequest* request) mutable { + connect(request, &SetMappingRequest::finished, this, [callback](SetMappingRequest* request) mutable { if (callback.isCallable()) { QJSValueList args { request->getErrorString(), request->getPath() }; callback.call(args); @@ -48,15 +47,13 @@ void AssetMappingsScriptingInterface::setMapping(QString path, QString hash, QJS request->deleteLater(); }); - request->start(); } void AssetMappingsScriptingInterface::getMapping(QString path, QJSValue callback) { auto assetClient = DependencyManager::get(); auto request = assetClient->createGetMappingRequest(path); - - connect(request, &GetMappingRequest::finished, this, [this, callback](GetMappingRequest* request) mutable { + connect(request, &GetMappingRequest::finished, this, [callback](GetMappingRequest* request) mutable { auto hash = request->getHash(); if (callback.isCallable()) { diff --git a/interface/src/scripting/LimitlessConnection.h b/interface/src/scripting/LimitlessConnection.h index ee049aff8e..9ed39bd653 100644 --- a/interface/src/scripting/LimitlessConnection.h +++ b/interface/src/scripting/LimitlessConnection.h @@ -12,9 +12,11 @@ #ifndef hifi_LimitlessConnection_h #define hifi_LimitlessConnection_h +#include +#include +#include + #include -#include -#include class LimitlessConnection : public QObject { Q_OBJECT diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index e50eb06355..fc45555bf9 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -277,7 +277,8 @@ void WindowScriptingInterface::browseAsync(const QString& title, const QString& if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } - emit openFileChanged(result); + emit browseChanged(result); + emit openFileChanged(result); // Deprecated signal; to be removed in due course. }); } @@ -390,6 +391,10 @@ QString WindowScriptingInterface::checkVersion() { return QCoreApplication::applicationVersion(); } +QString WindowScriptingInterface::protocolSignature() { + return protocolVersionsSignatureBase64(); +} + int WindowScriptingInterface::getInnerWidth() { return qApp->getDeviceSize().x; } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index bfad5644bf..dc71611c5b 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -197,18 +197,19 @@ public slots: /**jsdoc * Prompt the user to choose a file. Displays a non-modal dialog that navigates the directory tree. A - * {@link Window.openFileChanged|openFileChanged} signal is emitted when a file is chosen; no signal is emitted if the user + * {@link Window.browseChanged|browseChanged} signal is emitted when a file is chosen; no signal is emitted if the user * cancels the dialog. + * @deprecated A deprecated {@link Window.openFileChanged|openFileChanged} signal is also emitted when a file is chosen. * @function Window.browseAsync * @param {string} title="" - The title to display at the top of the dialog. * @param {string} directory="" - The initial directory to start browsing at. * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. * @example Ask the user to choose an image file without waiting for the answer. - * function onOpenFileChanged(filename) { + * function onBrowseChanged(filename) { * print("File: " + filename); * } - * Window.openFileChanged.connect(onOpenFileChanged); + * Window.browseChanged.connect(onBrowseChanged); * * Window.browseAsync("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)"); * print("Script continues without waiting"); @@ -305,6 +306,13 @@ public slots: */ QString checkVersion(); + /**jsdoc + * Get the signature for Interface's protocol version. + * @function Window.protocolSignature + * @returns {string} A string uniquely identifying the version of the metaverse protocol that Interface is using. + */ + QString protocolSignature(); + /**jsdoc * Copies text to the operating system's clipboard. * @function Window.copyToClipboard @@ -652,9 +660,18 @@ signals: */ void saveFileChanged(QString filename); + /**jsdoc + * Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog. + * @function Window.browseChanged + * @param {string} filename - The path and name of the file the user chose in the dialog. + * @returns {Signal} + */ + void browseChanged(QString filename); + /**jsdoc * Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog. * @function Window.openFileChanged + * @deprecated This signal is being replaced with {@link Window.browseChanged|browseChanged} and will be removed. * @param {string} filename - The path and name of the file the user chose in the dialog. * @returns {Signal} */ diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 52b53a3298..7cce12c8e0 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "ApplicationOverlay.h" + #include #include @@ -17,12 +19,10 @@ #include #include #include -#include #include "AudioClient.h" #include "audio/AudioScope.h" #include "Application.h" -#include "ApplicationOverlay.h" #include "Util.h" #include "ui/Stats.h" diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 0d30123c61..b36b4f02df 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -13,6 +13,7 @@ #define hifi_ApplicationOverlay_h #include +#include // Handles the drawing of the overlays to the screen diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 1a09af07ab..48b56c7ced 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -82,19 +82,42 @@ void setupPreferences() { preference->setMax(500); preferences->addPreference(preference); } - { - auto getter = []()->float { return qApp->getDesktopTabletScale(); }; - auto setter = [](float value) { qApp->setDesktopTabletScale(value); }; - auto preference = new SpinnerPreference(UI_CATEGORY, "Desktop Tablet Scale %", getter, setter); - preference->setMin(20); - preference->setMax(500); - preferences->addPreference(preference); - } + + { auto getter = []()->bool { return qApp->getPreferStylusOverLaser(); }; auto setter = [](bool value) { qApp->setPreferStylusOverLaser(value); }; preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Stylus Over Laser", getter, setter)); } + + static const QString ADVANCED_UI_CATEGORY { "Advanced UI" }; + { + auto getter = []()->float { return qApp->getDesktopTabletScale(); }; + auto setter = [](float value) { qApp->setDesktopTabletScale(value); }; + auto preference = new SpinnerPreference(ADVANCED_UI_CATEGORY, "Desktop Tablet Scale %", getter, setter); + preference->setMin(20); + preference->setMax(500); + preferences->addPreference(preference); + } + { + auto getter = [=]()->float { return myAvatar->getRealWorldFieldOfView(); }; + auto setter = [=](float value) { myAvatar->setRealWorldFieldOfView(value); }; + auto preference = new SpinnerPreference(ADVANCED_UI_CATEGORY, "Real world vertical field of view (angular size of monitor)", getter, setter); + preference->setMin(1); + preference->setMax(180); + preferences->addPreference(preference); + } + { + auto getter = []()->float { return qApp->getFieldOfView(); }; + auto setter = [](float value) { qApp->setFieldOfView(value); }; + auto preference = new SpinnerPreference(ADVANCED_UI_CATEGORY, "Vertical field of view", getter, setter); + preference->setMin(1); + preference->setMax(180); + preference->setStep(1); + preferences->addPreference(preference); + } + + // FIXME: Remove setting completely or make available through JavaScript API? /* { @@ -128,21 +151,13 @@ void setupPreferences() { preferences->addPreference(preference); } - // Scripts - { - auto getter = []()->QString { return DependencyManager::get()->getScriptsLocation(); }; - auto setter = [](const QString& value) { DependencyManager::get()->setScriptsLocation(value); }; - preferences->addPreference(new BrowsePreference("Scripts", "Load scripts from this directory", getter, setter)); - } - - preferences->addPreference(new ButtonPreference("Scripts", "Load Default Scripts", [] { - DependencyManager::get()->loadDefaultScripts(); - })); - { auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); }; auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); }; - preferences->addPreference(new CheckPreference("Privacy", "Send data", getter, setter)); + preferences->addPreference(new CheckPreference("Privacy", "Send data - High Fidelity uses information provided by your " + "client to improve the product through the logging of errors, tracking of usage patterns, " + "installation and system details, and crash events. By allowing High Fidelity to collect " + "this information you are helping to improve the product. ", getter, setter)); } static const QString LOD_TUNING("Level of Detail Tuning"); @@ -167,23 +182,6 @@ void setupPreferences() { } static const QString AVATAR_TUNING { "Avatar Tuning" }; - { - auto getter = [=]()->float { return myAvatar->getRealWorldFieldOfView(); }; - auto setter = [=](float value) { myAvatar->setRealWorldFieldOfView(value); }; - auto preference = new SpinnerPreference(AVATAR_TUNING, "Real world vertical field of view (angular size of monitor)", getter, setter); - preference->setMin(1); - preference->setMax(180); - preferences->addPreference(preference); - } - { - auto getter = []()->float { return qApp->getFieldOfView(); }; - auto setter = [](float value) { qApp->setFieldOfView(value); }; - auto preference = new SpinnerPreference(AVATAR_TUNING, "Vertical field of view", getter, setter); - preference->setMin(1); - preference->setMax(180); - preference->setStep(1); - preferences->addPreference(preference); - } { auto getter = [=]()->QString { return myAvatar->getDominantHand(); }; auto setter = [=](const QString& value) { myAvatar->setDominantHand(value); }; @@ -297,26 +295,6 @@ void setupPreferences() { } #endif - { - auto getter = []()->float { return qApp->getMaxOctreePacketsPerSecond(); }; - auto setter = [](float value) { qApp->setMaxOctreePacketsPerSecond(value); }; - auto preference = new SpinnerPreference("Octree", "Max packets sent each second", getter, setter); - preference->setMin(60); - preference->setMax(6000); - preference->setStep(10); - preferences->addPreference(preference); - } - - - { - auto getter = []()->float { return qApp->getApplicationCompositor().getHmdUIAngularSize(); }; - auto setter = [](float value) { qApp->getApplicationCompositor().setHmdUIAngularSize(value); }; - auto preference = new SpinnerPreference("HMD", "UI horizontal angular size (degrees)", getter, setter); - preference->setMin(30); - preference->setMax(160); - preference->setStep(1); - preferences->addPreference(preference); - } { static const QString RENDER("Graphics"); @@ -342,7 +320,7 @@ void setupPreferences() { } } { - static const QString RENDER("Networking"); + static const QString NETWORKING("Networking"); auto nodelist = DependencyManager::get(); { @@ -350,10 +328,21 @@ void setupPreferences() { static const int MAX_PORT_NUMBER { 65535 }; auto getter = [nodelist] { return static_cast(nodelist->getSocketLocalPort()); }; auto setter = [nodelist](int preset) { nodelist->setSocketLocalPort(static_cast(preset)); }; - auto preference = new IntSpinnerPreference(RENDER, "Listening Port", getter, setter); + auto preference = new IntSpinnerPreference(NETWORKING, "Listening Port", getter, setter); preference->setMin(MIN_PORT_NUMBER); preference->setMax(MAX_PORT_NUMBER); preferences->addPreference(preference); } + + { + auto getter = []()->float { return qApp->getMaxOctreePacketsPerSecond(); }; + auto setter = [](float value) { qApp->setMaxOctreePacketsPerSecond(value); }; + auto preference = new SpinnerPreference(NETWORKING, "Max entities packets sent each second", getter, setter); + preference->setMin(60); + preference->setMax(6000); + preference->setStep(10); + preferences->addPreference(preference); + } + } } diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 5b7c1896fe..f14f4ca78e 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -10,10 +10,9 @@ #include "ResourceImageItem.h" +#include + #include -#include -#include -#include #include @@ -89,11 +88,9 @@ QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(con } void ResourceImageItemRenderer::render() { - auto f = QOpenGLContext::currentContext()->extraFunctions(); - if (_fenceSync) { - f->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); - f->glDeleteSync(_fenceSync); + glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(_fenceSync); _fenceSync = 0; } if (_ready) { diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index 985ab5a66e..d154588094 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -14,6 +14,8 @@ #include "Application.h" +#include + #include #include #include diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index f991420fe8..c157898e74 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -42,8 +42,9 @@ using namespace std; static Stats* INSTANCE{ nullptr }; +#if !defined (Q_OS_ANDROID) QString getTextureMemoryPressureModeString(); - +#endif Stats* Stats::getInstance() { if (!INSTANCE) { Stats::registerType(); @@ -359,7 +360,9 @@ void Stats::updateStats(bool force) { STAT_UPDATE(gpuTextureResourceMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourceGPUMemSize())); STAT_UPDATE(gpuTextureResourcePopulatedMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourcePopulatedGPUMemSize())); STAT_UPDATE(gpuTextureExternalMemory, (int)BYTES_TO_MB(gpu::Context::getTextureExternalGPUMemSize())); +#if !defined(Q_OS_ANDROID) STAT_UPDATE(gpuTextureMemoryPressureState, getTextureMemoryPressureModeString()); +#endif STAT_UPDATE(gpuFreeMemory, (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemSize())); STAT_UPDATE(rectifiedTextureCount, (int)RECTIFIED_TEXTURE_COUNT.load()); STAT_UPDATE(decimatedTextureCount, (int)DECIMATED_TEXTURE_COUNT.load()); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index d29ee93e62..4804a2db3d 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -564,9 +564,9 @@ void ModelOverlay::animate() { rotationMat * fbxJoints[index].postTransform); auto& jointData = jointsData[j]; jointData.translation = extractTranslation(finalMat); - jointData.translationSet = true; + jointData.translationIsDefaultPose = false; jointData.rotation = glmExtractRotation(finalMat); - jointData.rotationSet = true; + jointData.rotationIsDefaultPose = false; } } // Set the data in the model diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 1c9d9a3447..b1b41775a8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1705,16 +1705,16 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { // rotations are in absolute rig frame. glm::quat defaultAbsRot = geometryToRigPose.rot() * _animSkeleton->getAbsoluteDefaultPose(i).rot(); data.rotation = _internalPoseSet._absolutePoses[i].rot(); - data.rotationSet = !isEqual(data.rotation, defaultAbsRot); + data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot); // translations are in relative frame but scaled so that they are in meters, // instead of geometry units. glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans(); data.translation = _geometryOffset.scale() * _internalPoseSet._relativePoses[i].trans(); - data.translationSet = !isEqual(data.translation, defaultRelTrans); + data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans); } else { - data.translationSet = false; - data.rotationSet = false; + data.translationIsDefaultPose = true; + data.rotationIsDefaultPose = true; } } } @@ -1739,11 +1739,11 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); for (int i = 0; i < numJoints; i++) { const JointData& data = jointDataVec.at(i); - if (data.rotationSet) { + if (data.rotationIsDefaultPose) { + rotations.push_back(absoluteDefaultPoses[i].rot()); + } else { // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame rotations.push_back(rigToGeometryRot * data.rotation); - } else { - rotations.push_back(absoluteDefaultPoses[i].rot()); } } @@ -1759,11 +1759,11 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { const JointData& data = jointDataVec.at(i); _internalPoseSet._relativePoses[i].scale() = Vectors::ONE; _internalPoseSet._relativePoses[i].rot() = rotations[i]; - if (data.translationSet) { + if (data.translationIsDefaultPose) { + _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); + } else { // JointData translations are in scaled relative-frame so we scale back to regular relative-frame _internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation; - } else { - _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); } } } diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 9f32372a8e..d1b919292a 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -427,28 +427,11 @@ AudioInjectorPointer AudioInjector::playSound(SharedSoundPointer sound, const fl options.stereo = sound->isStereo(); options.position = position; options.volume = volume; + options.pitch = 1.0f / stretchFactor; QByteArray samples = sound->getByteArray(); - if (stretchFactor == 1.0f) { - return playSoundAndDelete(samples, options); - } - const int standardRate = AudioConstants::SAMPLE_RATE; - const int resampledRate = standardRate * stretchFactor; - const int channelCount = sound->isStereo() ? 2 : 1; - - AudioSRC resampler(standardRate, resampledRate, channelCount); - - const int nInputFrames = samples.size() / (channelCount * sizeof(int16_t)); - const int maxOutputFrames = resampler.getMaxOutput(nInputFrames); - QByteArray resampled(maxOutputFrames * channelCount * sizeof(int16_t), '\0'); - - int nOutputFrames = resampler.render(reinterpret_cast(samples.data()), - reinterpret_cast(resampled.data()), - nInputFrames); - - Q_UNUSED(nOutputFrames); - return playSoundAndDelete(resampled, options); + return playSoundAndDelete(samples, options); } AudioInjectorPointer AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options) { @@ -461,12 +444,40 @@ AudioInjectorPointer AudioInjector::playSoundAndDelete(const QByteArray& buffer, return sound; } - AudioInjectorPointer AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options) { - AudioInjectorPointer injector = AudioInjectorPointer::create(buffer, options); - if (!injector->inject(&AudioInjectorManager::threadInjector)) { - qWarning() << "AudioInjector::playSound failed to thread injector"; + if (options.pitch == 1.0f) { + + AudioInjectorPointer injector = AudioInjectorPointer::create(buffer, options); + + if (!injector->inject(&AudioInjectorManager::threadInjector)) { + qWarning() << "AudioInjector::playSound failed to thread injector"; + } + return injector; + + } else { + + const int standardRate = AudioConstants::SAMPLE_RATE; + const int resampledRate = AudioConstants::SAMPLE_RATE / glm::clamp(options.pitch, 1/16.0f, 16.0f); // limit to 4 octaves + const int numChannels = options.ambisonic ? AudioConstants::AMBISONIC : + (options.stereo ? AudioConstants::STEREO : AudioConstants::MONO); + + AudioSRC resampler(standardRate, resampledRate, numChannels); + + // create a resampled buffer that is guaranteed to be large enough + const int nInputFrames = buffer.size() / (numChannels * sizeof(int16_t)); + const int maxOutputFrames = resampler.getMaxOutput(nInputFrames); + QByteArray resampledBuffer(maxOutputFrames * numChannels * sizeof(int16_t), '\0'); + + resampler.render(reinterpret_cast(buffer.data()), + reinterpret_cast(resampledBuffer.data()), + nInputFrames); + + AudioInjectorPointer injector = AudioInjectorPointer::create(resampledBuffer, options); + + if (!injector->inject(&AudioInjectorManager::threadInjector)) { + qWarning() << "AudioInjector::playSound failed to thread pitch-shifted injector"; + } + return injector; } - return injector; } diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 0af74a796c..2f82cae137 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -26,7 +26,8 @@ AudioInjectorOptions::AudioInjectorOptions() : ambisonic(false), ignorePenumbra(false), localOnly(false), - secondOffset(0.0f) + secondOffset(0.0f), + pitch(1.0f) { } @@ -40,6 +41,7 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje obj.setProperty("ignorePenumbra", injectorOptions.ignorePenumbra); obj.setProperty("localOnly", injectorOptions.localOnly); obj.setProperty("secondOffset", injectorOptions.secondOffset); + obj.setProperty("pitch", injectorOptions.pitch); return obj; } @@ -87,6 +89,12 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt } else { qCWarning(audio) << "Audio injector options: secondOffset is not a number"; } + } else if (it.name() == "pitch") { + if (it.value().isNumber()) { + injectorOptions.pitch = it.value().toNumber(); + } else { + qCWarning(audio) << "Audio injector options: pitch is not a number"; + } } else { qCWarning(audio) << "Unknown audio injector option:" << it.name(); } diff --git a/libraries/audio/src/AudioInjectorOptions.h b/libraries/audio/src/AudioInjectorOptions.h index 40a3f343bd..4dd38ce915 100644 --- a/libraries/audio/src/AudioInjectorOptions.h +++ b/libraries/audio/src/AudioInjectorOptions.h @@ -29,6 +29,7 @@ public: bool ignorePenumbra; bool localOnly; float secondOffset; + float pitch; // multiplier, where 2.0f shifts up one octave }; Q_DECLARE_METATYPE(AudioInjectorOptions); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 95a8a9f1fd..500a24763d 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -791,10 +791,19 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { // virtual void Avatar::simulateAttachments(float deltaTime) { + assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size()); PerformanceTimer perfTimer("attachments"); for (int i = 0; i < (int)_attachmentModels.size(); i++) { const AttachmentData& attachment = _attachmentData.at(i); auto& model = _attachmentModels.at(i); + bool texturesLoaded = _attachmentModelsTexturesLoaded.at(i); + + // Watch for texture loading + if (!texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) { + _attachmentModelsTexturesLoaded[i] = true; + model->updateRenderItems(); + } + int jointIndex = getJointIndex(attachment.jointName); glm::vec3 jointPosition; glm::quat jointRotation; @@ -1319,6 +1328,7 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { while ((int)_attachmentModels.size() > attachmentData.size()) { auto attachmentModel = _attachmentModels.back(); _attachmentModels.pop_back(); + _attachmentModelsTexturesLoaded.pop_back(); _attachmentsToRemove.push_back(attachmentModel); } @@ -1326,11 +1336,16 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { if (i == (int)_attachmentModels.size()) { // if number of attachments has been increased, we need to allocate a new model _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar())); - } - else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) { + _attachmentModelsTexturesLoaded.push_back(false); + } else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) { // if the attachment has changed type, we need to re-allocate a new one. _attachmentsToRemove.push_back(_attachmentModels[i]); _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar()); + _attachmentModelsTexturesLoaded[i] = false; + } + // If the model URL has changd, we need to wait for the textures to load + if (_attachmentModels[i]->getURL() != attachmentData[i].modelURL) { + _attachmentModelsTexturesLoaded[i] = false; } _attachmentModels[i]->setURL(attachmentData[i].modelURL); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 3fe14f980f..c2b404a925 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -306,6 +306,7 @@ protected: glm::vec3 _skeletonOffset; std::vector> _attachmentModels; + std::vector _attachmentModelsTexturesLoaded; std::vector> _attachmentsToRemove; std::vector> _attachmentsToDelete; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 94df5bf7f4..c2aadb8723 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include "AvatarLogging.h" @@ -77,6 +78,16 @@ size_t AvatarDataPacket::maxJointDataSize(size_t numJoints) { return totalSize; } +size_t AvatarDataPacket::maxJointDefaultPoseFlagsSize(size_t numJoints) { + const size_t bitVectorSize = calcBitVectorSize((int)numJoints); + size_t totalSize = sizeof(uint8_t); // numJoints + + // one set of bits for rotation and one for translation + const size_t NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION = 2; + totalSize += NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION * bitVectorSize; + + return totalSize; +} AvatarData::AvatarData() : SpatiallyNestable(NestableType::Avatar, QUuid()), @@ -103,7 +114,7 @@ AvatarData::~AvatarData() { QUrl AvatarData::_defaultFullAvatarModelUrl = {}; // In C++, if this initialization were in the AvatarInfo, every file would have it's own copy, even for class vars. const QUrl& AvatarData::defaultFullAvatarModelUrl() { if (_defaultFullAvatarModelUrl.isEmpty()) { - _defaultFullAvatarModelUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "/meshes/defaultAvatar_full.fst"); + _defaultFullAvatarModelUrl = PathUtils::resourcesUrl("/meshes/defaultAvatar_full.fst"); } return _defaultFullAvatarModelUrl; } @@ -272,6 +283,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent bool hasFaceTrackerInfo = false; bool hasJointData = false; + bool hasJointDefaultPoseFlags = false; if (sendPALMinimum) { hasAudioLoudness = true; @@ -290,12 +302,14 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent hasFaceTrackerInfo = !dropFaceTracking && hasFaceTracker() && (sendAll || faceTrackerInfoChangedSince(lastSentTime)); hasJointData = sendAll || !sendMinimum; + hasJointDefaultPoseFlags = hasJointData; } const size_t byteArraySize = AvatarDataPacket::MAX_CONSTANT_HEADER_SIZE + (hasFaceTrackerInfo ? AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getNumSummedBlendshapeCoefficients()) : 0) + - (hasJointData ? AvatarDataPacket::maxJointDataSize(_jointData.size()) : 0); + (hasJointData ? AvatarDataPacket::maxJointDataSize(_jointData.size()) : 0) + + (hasJointDefaultPoseFlags ? AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size()) : 0); QByteArray avatarDataByteArray((int)byteArraySize, 0); unsigned char* destinationBuffer = reinterpret_cast(avatarDataByteArray.data()); @@ -314,7 +328,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent | (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0) | (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0) | (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0) - | (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0); + | (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0) + | (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0); memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); destinationBuffer += sizeof(packetStateFlags); @@ -541,14 +556,19 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent for (int i = 0; i < _jointData.size(); i++) { const JointData& data = _jointData[i]; + const JointData& last = lastSentJointData[i]; - // The dot product for smaller rotations is a smaller number. - // So if the dot() is less than the value, then the rotation is a larger angle of rotation - bool largeEnoughRotation = fabsf(glm::dot(data.rotation, lastSentJointData[i].rotation)) < minRotationDOT; + if (!data.rotationIsDefaultPose) { + if (sendAll || last.rotationIsDefaultPose || last.rotation != data.rotation) { - if (sendAll || lastSentJointData[i].rotation != data.rotation) { - if (sendAll || !cullSmallChanges || largeEnoughRotation) { - if (data.rotationSet) { + bool largeEnoughRotation = true; + if (cullSmallChanges) { + // The dot product for smaller rotations is a smaller number. + // So if the dot() is less than the value, then the rotation is a larger angle of rotation + largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT; + } + + if (sendAll || !cullSmallChanges || largeEnoughRotation) { validity |= (1 << validityBit); #ifdef WANT_DEBUG rotationSentCount++; @@ -557,8 +577,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (sentJointDataOut) { localSentJointDataOut[i].rotation = data.rotation; + localSentJointDataOut[i].rotationIsDefaultPose = false; } - } } } @@ -588,11 +608,10 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent float maxTranslationDimension = 0.0; for (int i = 0; i < _jointData.size(); i++) { const JointData& data = _jointData[i]; - if (sendAll || lastSentJointData[i].translation != data.translation) { - if (sendAll || - !cullSmallChanges || - glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) { - if (data.translationSet) { + + if (!data.translationIsDefaultPose) { + if (sendAll || lastSentJointData[i].translation != data.translation) { + if (sendAll || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) { validity |= (1 << validityBit); #ifdef WANT_DEBUG translationSentCount++; @@ -606,8 +625,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (sentJointDataOut) { localSentJointDataOut[i].translation = data.translation; + localSentJointDataOut[i].translationIsDefaultPose = false; } - } } } @@ -655,6 +674,30 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } + if (hasJointDefaultPoseFlags) { + auto startSection = destinationBuffer; + QReadLocker readLock(&_jointDataLock); + + // write numJoints + int numJoints = _jointData.size(); + *destinationBuffer++ = (uint8_t)numJoints; + + // write rotationIsDefaultPose bits + destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) { + return _jointData[i].rotationIsDefaultPose; + }); + + // write translationIsDefaultPose bits + destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) { + return _jointData[i].translationIsDefaultPose; + }); + + if (outboundDataRateOut) { + size_t numBytes = destinationBuffer - startSection; + outboundDataRateOut->jointDefaultPoseFlagsRate.increment(numBytes); + } + } + int avatarDataSize = destinationBuffer - startPosition; if (avatarDataSize > (int)byteArraySize) { @@ -664,6 +707,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent return avatarDataByteArray.left(avatarDataSize); } + // NOTE: This is never used in a "distanceAdjust" mode, so it's ok that it doesn't use a variable minimum rotation/translation void AvatarData::doneEncoding(bool cullSmallChanges) { // The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData. @@ -674,7 +718,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) { if (_lastSentJointData[i].rotation != data.rotation) { if (!cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { - if (data.rotationSet) { + if (!data.rotationIsDefaultPose) { _lastSentJointData[i].rotation = data.rotation; } } @@ -682,7 +726,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) { if (_lastSentJointData[i].translation != data.translation) { if (!cullSmallChanges || glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) { - if (data.translationSet) { + if (!data.translationIsDefaultPose) { _lastSentJointData[i].translation = data.translation; } } @@ -730,6 +774,7 @@ const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSa // read data in packet starting at byte offset and return number of bytes parsed int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { + // lazily allocate memory for HeadData in case we're not an Avatar instance lazyInitHeadData(); @@ -745,18 +790,19 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { #define HAS_FLAG(B,F) ((B & F) == F) - bool hasAvatarGlobalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION); - bool hasAvatarBoundingBox = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX); - bool hasAvatarOrientation = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION); - bool hasAvatarScale = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_SCALE); - bool hasLookAtPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION); - bool hasAudioLoudness = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS); - bool hasSensorToWorldMatrix = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX); - bool hasAdditionalFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS); - bool hasParentInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO); - bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION); - bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO); - bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA); + bool hasAvatarGlobalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION); + bool hasAvatarBoundingBox = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX); + bool hasAvatarOrientation = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION); + bool hasAvatarScale = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_SCALE); + bool hasLookAtPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION); + bool hasAudioLoudness = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS); + bool hasSensorToWorldMatrix = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX); + bool hasAdditionalFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS); + bool hasParentInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO); + bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION); + bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO); + bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA); + bool hasJointDefaultPoseFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS); quint64 now = usecTimestampNow(); @@ -1055,7 +1101,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (validRotations[i]) { sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, data.rotation); _hasNewJointData = true; - data.rotationSet = true; + data.rotationIsDefaultPose = false; } } @@ -1090,7 +1136,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (validTranslations[i]) { sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); _hasNewJointData = true; - data.translationSet = true; + data.translationIsDefaultPose = false; } } @@ -1110,6 +1156,32 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { _jointDataUpdateRate.increment(); } + if (hasJointDefaultPoseFlags) { + auto startSection = sourceBuffer; + + QWriteLocker writeLock(&_jointDataLock); + + PACKET_READ_CHECK(JointDefaultPoseFlagsNumJoints, sizeof(uint8_t)); + int numJoints = (int)*sourceBuffer++; + + _jointData.resize(numJoints); + + size_t bitVectorSize = calcBitVectorSize(numJoints); + PACKET_READ_CHECK(JointDefaultPoseFlagsRotationFlags, bitVectorSize); + sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) { + _jointData[i].rotationIsDefaultPose = value; + }); + + PACKET_READ_CHECK(JointDefaultPoseFlagsTranslationFlags, bitVectorSize); + sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) { + _jointData[i].translationIsDefaultPose = value; + }); + + int numBytesRead = sourceBuffer - startSection; + _jointDefaultPoseFlagsRate.increment(numBytesRead); + _jointDefaultPoseFlagsUpdateRate.increment(); + } + int numBytesRead = sourceBuffer - startPosition; _averageBytesReceived.updateAverage(numBytesRead); @@ -1146,6 +1218,8 @@ float AvatarData::getDataRate(const QString& rateName) const { return _faceTrackerRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "jointData") { return _jointDataRate.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "jointDefaultPoseFlagsRate") { + return _jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "globalPositionOutbound") { return _outboundDataRate.globalPositionRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "localPositionOutbound") { @@ -1170,6 +1244,8 @@ float AvatarData::getDataRate(const QString& rateName) const { return _outboundDataRate.faceTrackerRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "jointDataOutbound") { return _outboundDataRate.jointDataRate.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "jointDefaultPoseFlagsOutbound") { + return _outboundDataRate.jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT; } return 0.0f; } @@ -1236,9 +1312,9 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v } JointData& data = _jointData[index]; data.rotation = rotation; - data.rotationSet = true; + data.rotationIsDefaultPose = false; data.translation = translation; - data.translationSet = true; + data.translationIsDefaultPose = false; } void AvatarData::clearJointData(int index) { @@ -1294,7 +1370,8 @@ void AvatarData::setJointData(const QString& name, const glm::quat& rotation, co auto& jointData = _jointData[index]; jointData.rotation = rotation; jointData.translation = translation; - jointData.rotationSet = jointData.translationSet = true; + jointData.rotationIsDefaultPose = false; + jointData.translationIsDefaultPose = false; }); } @@ -1304,7 +1381,7 @@ void AvatarData::setJointRotation(const QString& name, const glm::quat& rotation writeLockWithNamedJointIndex(name, [&](int index) { auto& data = _jointData[index]; data.rotation = rotation; - data.rotationSet = true; + data.rotationIsDefaultPose = false; }); } @@ -1314,7 +1391,7 @@ void AvatarData::setJointTranslation(const QString& name, const glm::vec3& trans writeLockWithNamedJointIndex(name, [&](int index) { auto& data = _jointData[index]; data.translation = translation; - data.translationSet = true; + data.translationIsDefaultPose = false; }); } @@ -1328,7 +1405,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) { } JointData& data = _jointData[index]; data.rotation = rotation; - data.rotationSet = true; + data.rotationIsDefaultPose = false; } void AvatarData::setJointTranslation(int index, const glm::vec3& translation) { @@ -1341,7 +1418,7 @@ void AvatarData::setJointTranslation(int index, const glm::vec3& translation) { } JointData& data = _jointData[index]; data.translation = translation; - data.translationSet = true; + data.translationIsDefaultPose = false; } void AvatarData::clearJointData(const QString& name) { @@ -1397,7 +1474,7 @@ void AvatarData::setJointRotations(const QVector& jointRotations) { for (int i = 0; i < size; ++i) { auto& data = _jointData[i]; data.rotation = jointRotations[i]; - data.rotationSet = true; + data.rotationIsDefaultPose = false; } } @@ -1419,7 +1496,7 @@ void AvatarData::setJointTranslations(const QVector& jointTranslation for (int i = 0; i < size; ++i) { auto& data = _jointData[i]; data.translation = jointTranslations[i]; - data.translationSet = true; + data.translationIsDefaultPose = false; } } @@ -1996,9 +2073,9 @@ JointData jointDataFromJsonValue(const QJsonValue& json) { if (json.isArray()) { QJsonArray array = json.toArray(); result.rotation = quatFromJsonValue(array[0]); - result.rotationSet = true; + result.rotationIsDefaultPose = false; result.translation = vec3FromJsonValue(array[1]); - result.translationSet = true; + result.translationIsDefaultPose = false; } return result; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 00cc760658..92c505f5c3 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -113,18 +113,19 @@ namespace AvatarDataPacket { // Packet State Flags - we store the details about the existence of other records in this bitset: // AvatarGlobalPosition, Avatar face tracker, eye tracking, and existence of using HasFlags = uint16_t; - const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0; - const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1; - const HasFlags PACKET_HAS_AVATAR_ORIENTATION = 1U << 2; - const HasFlags PACKET_HAS_AVATAR_SCALE = 1U << 3; - const HasFlags PACKET_HAS_LOOK_AT_POSITION = 1U << 4; - const HasFlags PACKET_HAS_AUDIO_LOUDNESS = 1U << 5; - const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX = 1U << 6; - const HasFlags PACKET_HAS_ADDITIONAL_FLAGS = 1U << 7; - const HasFlags PACKET_HAS_PARENT_INFO = 1U << 8; - const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9; - const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10; - const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11; + const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0; + const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1; + const HasFlags PACKET_HAS_AVATAR_ORIENTATION = 1U << 2; + const HasFlags PACKET_HAS_AVATAR_SCALE = 1U << 3; + const HasFlags PACKET_HAS_LOOK_AT_POSITION = 1U << 4; + const HasFlags PACKET_HAS_AUDIO_LOUDNESS = 1U << 5; + const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX = 1U << 6; + const HasFlags PACKET_HAS_ADDITIONAL_FLAGS = 1U << 7; + const HasFlags PACKET_HAS_PARENT_INFO = 1U << 8; + const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9; + const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10; + const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11; + const HasFlags PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS = 1U << 12; const size_t AVATAR_HAS_FLAGS_SIZE = 2; using SixByteQuat = uint8_t[6]; @@ -256,6 +257,15 @@ namespace AvatarDataPacket { }; */ size_t maxJointDataSize(size_t numJoints); + + /* + struct JointDefaultPoseFlags { + uint8_t numJoints; + uint8_t rotationIsDefaultPoseBits[ceil(numJoints / 8)]; + uint8_t translationIsDefaultPoseBits[ceil(numJoints / 8)]; + }; + */ + size_t maxJointDefaultPoseFlagsSize(size_t numJoints); } const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation @@ -321,6 +331,7 @@ public: RateCounter<> parentInfoRate; RateCounter<> faceTrackerRate; RateCounter<> jointDataRate; + RateCounter<> jointDefaultPoseFlagsRate; }; class AvatarPriority { @@ -810,6 +821,7 @@ protected: RateCounter<> _parentInfoRate; RateCounter<> _faceTrackerRate; RateCounter<> _jointDataRate; + RateCounter<> _jointDefaultPoseFlagsRate; // Some rate data for incoming data updates RateCounter<> _parseBufferUpdateRate; @@ -825,6 +837,7 @@ protected: RateCounter<> _parentInfoUpdateRate; RateCounter<> _faceTrackerUpdateRate; RateCounter<> _jointDataUpdateRate; + RateCounter<> _jointDefaultPoseFlagsUpdateRate; // Some rate data for outgoing data AvatarDataRate _outboundDataRate; diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index f448375f0d..6c2acb7e62 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -53,8 +53,6 @@ public: bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; - float getHmdUIAngularSize() const { return _hmdUIAngularSize; } - void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; } bool isHMD() const; bool fakeEventActive() const { return _fakeMouseEvent; } @@ -139,7 +137,6 @@ private: //quint64 _hoverItemEnterUsecs { 0 }; bool _isOverDesktop { true }; - float _hmdUIAngularSize { glm::degrees(VIRTUAL_UI_TARGET_FOV.y) }; float _textureFov { VIRTUAL_UI_TARGET_FOV.y }; float _textureAspectRatio { VIRTUAL_UI_ASPECT_RATIO }; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index e646ba27f5..40dcf1b8c7 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -10,16 +10,14 @@ #include #include +#include + #include #include #include -#include #include -#include -#if defined(Q_OS_MAC) -#include -#endif +#include #include #include @@ -27,7 +25,6 @@ #include #include -#include #include #include @@ -130,6 +127,8 @@ public: OpenGLDisplayPlugin* currentPlugin{ nullptr }; Q_ASSERT(_context); _context->makeCurrent(); + CHECK_GL_ERROR(); + _context->doneCurrent(); while (!_shutdown) { if (_pendingMainThreadOperation) { PROFILE_RANGE(render, "MainThreadOp") @@ -171,20 +170,15 @@ public: QThread::setPriority(newPlugin->getPresentPriority()); bool wantVsync = newPlugin->wantVsync(); _context->makeCurrent(); -#if defined(Q_OS_WIN) - wglSwapIntervalEXT(wantVsync ? 1 : 0); - hasVsync = wglGetSwapIntervalEXT() != 0; -#elif defined(Q_OS_MAC) - GLint interval = wantVsync ? 1 : 0; + CHECK_GL_ERROR(); +#if defined(Q_OS_MAC) newPlugin->swapBuffers(); - CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); - newPlugin->swapBuffers(); - CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); - hasVsync = interval != 0; -#else - // TODO: Fill in for linux - Q_UNUSED(wantVsync); #endif + gl::setSwapInterval(wantVsync ? 1 : 0); +#if defined(Q_OS_MAC) + newPlugin->swapBuffers(); +#endif + hasVsync = gl::getSwapInterval() != 0; newPlugin->setVsyncEnabled(hasVsync); newPlugin->customizeContext(); CHECK_GL_ERROR(); @@ -284,6 +278,12 @@ bool OpenGLDisplayPlugin::activate() { DependencyManager::set(); presentThread = DependencyManager::get(); presentThread->setObjectName("Presentation Thread"); + if (!widget->context()->makeCurrent()) { + throw std::runtime_error("Failed to make context current"); + } + CHECK_GL_ERROR(); + widget->context()->doneCurrent(); + presentThread->setContext(widget->context()); // Start execution presentThread->start(); @@ -884,6 +884,7 @@ void OpenGLDisplayPlugin::updateCompositeFramebuffer() { } void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) { +#if !defined(USE_GLES) auto glBackend = const_cast(*this).getGLBackend(); withMainThreadContext([&] { GLuint sourceTexture = glBackend->getTextureID(networkTexture->getGPUTexture()); @@ -924,11 +925,13 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne } else { newY = (target->height() - newHeight) / 2; } + glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, texWidth, texHeight, newX, newY, newX + newWidth, newY + newHeight, GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT, GL_NEAREST); // don't delete the textures! glDeleteFramebuffers(2, fbo); *fenceSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); }); +#endif } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index d67c0b8663..1176471b40 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -8,7 +8,6 @@ #pragma once #include "DisplayPlugin.h" -#include #include #include diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 1d7fee38eb..90bb83a663 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -33,6 +33,7 @@ #include "../Logging.h" #include "../CompositorHelper.h" +#include "DesktopPreviewProvider.h" #include "render-utils/hmd_ui_vert.h" #include "render-utils/hmd_ui_frag.h" @@ -254,17 +255,9 @@ void HmdDisplayPlugin::internalPresent() { swapBuffers(); } else if (_clearPreviewFlag) { - QImage image; - if (_vsyncEnabled) { - image = QImage(PathUtils::resourcesPath() + "images/preview.png"); - } else { - image = QImage(PathUtils::resourcesPath() + "images/preview-disabled.png"); - } - image = image.mirrored(); - image = image.convertToFormat(QImage::Format_RGBA8888); - if (!_previewTexture) { - _previewTexture = gpu::Texture::createStrict( + QImage image = DesktopPreviewProvider::getInstance()->getPreviewDisabledImage(_vsyncEnabled); + _previewTexture = gpu::Texture::createStrict( gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, @@ -274,7 +267,6 @@ void HmdDisplayPlugin::internalPresent() { _previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); _previewTexture->assignStoredMip(0, image.byteCount(), image.constBits()); _previewTexture->setAutoGenerateMips(true); - } auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 827507c3aa..2e39e2e498 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1050,7 +1050,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - QVector jointsData; + QVector jointsData; const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy int frameCount = frames.size(); @@ -1394,8 +1394,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // That is where _currentFrame and _lastAnimated were updated. if (_animating) { DETAILED_PROFILE_RANGE(simulation_physics, "Animate"); + if (!jointsMapped()) { mapJoints(entity, model->getJointNames()); + //else the joint have been mapped before but we have a new animation to load + } else if (_animation && (_animation->getURL().toString() != entity->getAnimationURL())) { + _animation = DependencyManager::get()->getAnimation(entity->getAnimationURL()); + _jointMappingCompleted = false; + mapJoints(entity, model->getJointNames()); } if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) { animate(entity); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index a3e6cd4341..2059487426 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -26,7 +26,7 @@ static std::weak_ptr _texturedPipeline; // FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 11 to avoid collisions static int32_t PARTICLE_UNIFORM_SLOT { 11 }; -static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key) { +static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch) { auto texturedPipeline = _texturedPipeline.lock(); if (!texturedPipeline) { auto state = std::make_shared(); @@ -40,10 +40,13 @@ static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, co auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag)); auto program = gpu::Shader::createProgram(vertShader, fragShader); - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("particleBuffer"), PARTICLE_UNIFORM_SLOT)); - gpu::Shader::makeProgram(*program, slotBindings); _texturedPipeline = texturedPipeline = gpu::Pipeline::create(program, state); + + batch.runLambda([program] { + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("particleBuffer"), PARTICLE_UNIFORM_SLOT)); + gpu::Shader::makeProgram(*program, slotBindings); + }); } return std::make_shared(texturedPipeline, nullptr, nullptr, nullptr); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index b362721cde..4d223669b4 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -46,7 +46,7 @@ struct PolyLineUniforms { glm::vec3 color; }; -static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) { +static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) { if (!polylinePipeline) { auto VS = gpu::Shader::createVertex(std::string(paintStroke_vert)); auto PS = gpu::Shader::createPixel(std::string(paintStroke_frag)); @@ -56,15 +56,21 @@ static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlum auto fadePS = gpu::Shader::createPixel(std::string(paintStroke_fade_frag)); gpu::ShaderPointer fadeProgram = gpu::Shader::createProgram(fadeVS, fadePS); #endif - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("originalTexture"), PAINTSTROKE_TEXTURE_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("polyLineBuffer"), PAINTSTROKE_UNIFORM_SLOT)); - gpu::Shader::makeProgram(*program, slotBindings); + batch.runLambda([program #ifdef POLYLINE_ENTITY_USE_FADE_EFFECT - slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), PAINTSTROKE_TEXTURE_SLOT + 1)); - slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), PAINTSTROKE_UNIFORM_SLOT + 1)); - gpu::Shader::makeProgram(*fadeProgram, slotBindings); + , fadeProgram #endif + ] { + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("originalTexture"), PAINTSTROKE_TEXTURE_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("polyLineBuffer"), PAINTSTROKE_UNIFORM_SLOT)); + gpu::Shader::makeProgram(*program, slotBindings); +#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT + slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), PAINTSTROKE_TEXTURE_SLOT + 1)); + slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), PAINTSTROKE_UNIFORM_SLOT + 1)); + gpu::Shader::makeProgram(*fadeProgram, slotBindings); +#endif + }); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(true, true, gpu::LESS_EQUAL); PrepareStencil::testMask(*state); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index b4412e441f..aadd49fde8 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1457,7 +1457,7 @@ static gpu::PipelinePointer _pipelines[2]; static gpu::PipelinePointer _wireframePipelines[2]; static gpu::Stream::FormatPointer _vertexFormat; -ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key) { +ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch) { if (!_pipelines[0]) { gpu::ShaderPointer vertexShaders[2] = { gpu::Shader::createVertex(std::string(polyvox_vert)), gpu::Shader::createVertex(std::string(polyvox_fade_vert)) }; gpu::ShaderPointer pixelShaders[2] = { gpu::Shader::createPixel(std::string(polyvox_frag)), gpu::Shader::createPixel(std::string(polyvox_fade_frag)) }; @@ -1485,7 +1485,10 @@ ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const Sha // Two sets of pipelines: normal and fading for (auto i = 0; i < 2; i++) { gpu::ShaderPointer program = gpu::Shader::createProgram(vertexShaders[i], pixelShaders[i]); - gpu::Shader::makeProgram(*program, slotBindings); + + batch.runLambda([program, slotBindings] { + gpu::Shader::makeProgram(*program, slotBindings); + }); _pipelines[i] = gpu::Pipeline::create(program, state); _wireframePipelines[i] = gpu::Pipeline::create(program, wireframeState); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ecfb7b5dcd..f9559a375b 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -368,6 +368,7 @@ public: void* getPhysicsInfo() const { return _physicsInfo; } void setPhysicsInfo(void* data) { _physicsInfo = data; } + EntityTreeElementPointer getElement() const { return _element; } EntityTreePointer getTree() const; virtual SpatialParentTree* getParentTree() const override; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 8996665262..a615beefa9 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -452,7 +452,7 @@ void ModelEntityItem::resizeJointArrays(int newSize) { }); } -void ModelEntityItem::setAnimationJointsData(const QVector& jointsData) { +void ModelEntityItem::setAnimationJointsData(const QVector& jointsData) { resizeJointArrays(jointsData.size()); _jointDataLock.withWriteLock([&] { for (auto index = 0; index < jointsData.size(); ++index) { diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 6169039f52..c2109ba51f 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -124,7 +124,7 @@ public: virtual void setJointTranslations(const QVector& translations); virtual void setJointTranslationsSet(const QVector& translationsSet); - virtual void setAnimationJointsData(const QVector& jointsData); + virtual void setAnimationJointsData(const QVector& jointsData); QVector getJointRotations() const; QVector getJointRotationsSet() const; @@ -150,7 +150,7 @@ protected: bool _jointTranslationsExplicitlySet{ false }; // were the joints set as a property or just side effect of animations struct ModelJointData { - JointData joint; + EntityJointData joint; bool rotationDirty { false }; bool translationDirty { false }; }; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 4bc5eb1b6d..171fc88443 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1733,8 +1733,18 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS qCDebug(modelformat) << "Joint not in model list: " << jointID; fbxCluster.jointIndex = 0; } + fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; + + // slam bottom row to (0, 0, 0, 1), we KNOW this is not a perspective matrix and + // sometimes floating point fuzz can be introduced after the inverse. + fbxCluster.inverseBindMatrix[0][3] = 0.0f; + fbxCluster.inverseBindMatrix[1][3] = 0.0f; + fbxCluster.inverseBindMatrix[2][3] = 0.0f; + fbxCluster.inverseBindMatrix[3][3] = 1.0f; + fbxCluster.inverseBindTransform = Transform(fbxCluster.inverseBindMatrix); + extracted.mesh.clusters.append(fbxCluster); // override the bind rotation with the transform link @@ -1836,13 +1846,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } // now that we've accumulated the most relevant weights for each vertex - // normalize and compress to 8-bits + // normalize and compress to 16-bits extracted.mesh.clusterWeights.fill(0, numClusterIndices); int numVertices = extracted.mesh.vertices.size(); for (int i = 0; i < numVertices; ++i) { int j = i * WEIGHTS_PER_VERTEX; - // normalize weights into uint8_t + // normalize weights into uint16_t float totalWeight = weightAccumulators[j]; for (int k = j + 1; k < j + WEIGHTS_PER_VERTEX; ++k) { totalWeight += weightAccumulators[k]; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index e446861627..ad002b237c 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -14,6 +14,7 @@ #include "FBX.h" +#include #include #include #include @@ -33,7 +34,11 @@ class QIODevice; class FBXNode; +#if defined(Q_OS_ANDROID) +#define FBX_PACK_NORMALS 0 +#else #define FBX_PACK_NORMALS 1 +#endif #if FBX_PACK_NORMALS using NormalType = glm::uint32; diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 7299f0c71c..b0e2faa600 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -611,7 +611,11 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { const int normalsSize = fbxMesh.normals.size() * sizeof(NormalType); const int tangentsSize = fbxMesh.tangents.size() * sizeof(NormalType); // If there are normals then there should be tangents - assert(normalsSize == tangentsSize); + + assert(normalsSize <= tangentsSize); + if (tangentsSize > normalsSize) { + qWarning() << "Unexpected tangents in " << url; + } const auto normalsAndTangentsSize = normalsSize + tangentsSize; const int normalsAndTangentsStride = 2 * sizeof(NormalType); const int colorsSize = fbxMesh.colors.size() * sizeof(ColorType); diff --git a/libraries/gl/src/gl/Config.cpp b/libraries/gl/src/gl/Config.cpp index 1f29fe21b1..0e3a174e91 100644 --- a/libraries/gl/src/gl/Config.cpp +++ b/libraries/gl/src/gl/Config.cpp @@ -13,23 +13,112 @@ #include -#if defined(Q_OS_ANDROID) -PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT = NULL; -PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT = NULL; -PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT = NULL; +#if defined(Q_OS_WIN) +#elif defined(Q_OS_ANDROID) +#elif defined(Q_OS_MAC) +#include +#include +#include +#include +#else +#include +#include #endif + + +#if defined(Q_OS_WIN) + +static void* getGlProcessAddress(const char *namez) { + auto result = wglGetProcAddress(namez); + if (!result) { + static HMODULE glModule = nullptr; + if (!glModule) { + glModule = LoadLibraryW(L"opengl32.dll"); + } + result = GetProcAddress(glModule, namez); + } + if (!result) { + OutputDebugStringA(namez); + OutputDebugStringA("\n"); + } + return (void*)result; +} + +typedef BOOL(APIENTRYP PFNWGLSWAPINTERVALEXTPROC)(int interval); +typedef int (APIENTRYP PFNWGLGETSWAPINTERVALEXTPROC) (void); +typedef BOOL(APIENTRYP PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +typedef HGLRC(APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int *attribList); + +PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; +PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT; +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; +PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; + +#elif defined(Q_OS_ANDROID) + +static void* getGlProcessAddress(const char *namez) { + auto result = eglGetProcAddress(namez); + return (void*)result; +} + +#elif defined(Q_OS_MAC) + +static void* getGlProcessAddress(const char *namez) { + static void* GL_LIB = nullptr; + if (nullptr == GL_LIB) { + GL_LIB = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_NOW | RTLD_GLOBAL); + } + return dlsym(GL_LIB, namez); +} + +#else + +static void* getGlProcessAddress(const char *namez) { + return (void*)glXGetProcAddressARB((const GLubyte*)namez); +} + +#endif + + + void gl::initModuleGl() { static std::once_flag once; std::call_once(once, [] { -#if defined(Q_OS_ANDROID) - glQueryCounterEXT = (PFNGLQUERYCOUNTEREXTPROC)eglGetProcAddress("glQueryCounterEXT"); - glGetQueryObjectui64vEXT = (PFNGLGETQUERYOBJECTUI64VEXTPROC)eglGetProcAddress("glGetQueryObjectui64vEXT"); - glFramebufferTextureEXT = (PFNGLFRAMEBUFFERTEXTUREEXTPROC)eglGetProcAddress("glFramebufferTextureEXT"); +#if defined(Q_OS_WIN) + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)getGlProcessAddress("wglSwapIntervalEXT"); + wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)getGlProcessAddress("wglGetSwapIntervalEXT"); + wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)getGlProcessAddress("wglChoosePixelFormatARB"); + wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)getGlProcessAddress("wglCreateContextAttribsARB"); +#endif + +#if defined(USE_GLES) + gladLoadGLES2Loader(getGlProcessAddress); #else - glewExperimental = true; - glewInit(); + gladLoadGLLoader(getGlProcessAddress); #endif }); } +int gl::getSwapInterval() { +#if defined(Q_OS_WIN) + return wglGetSwapIntervalEXT(); +#elif defined(Q_OS_MAC) + GLint interval; + CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); + return interval; +#else + // TODO: Fill in for linux + return 1; +#endif +} + +void gl::setSwapInterval(int interval) { +#if defined(Q_OS_WIN) + wglSwapIntervalEXT(interval); +#elif defined(Q_OS_MAC) + CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); +#else + Q_UNUSED(interval); +#endif +} diff --git a/libraries/gl/src/gl/Config.h b/libraries/gl/src/gl/Config.h index cdf86675c6..aad000a242 100644 --- a/libraries/gl/src/gl/Config.h +++ b/libraries/gl/src/gl/Config.h @@ -14,12 +14,7 @@ #include -#if defined(Q_OS_ANDROID) -#define HIFI_GLES -#define HIFI_EGL -#endif - -#if defined(HIFI_GLES) +#if defined(USE_GLES) // Minimum GL ES version required is 3.2 #define GL_MIN_VERSION_MAJOR 0x03 #define GL_MIN_VERSION_MINOR 0x02 @@ -35,51 +30,28 @@ #define MINIMUM_GL_VERSION ((GL_MIN_VERSION_MAJOR << 8) | GL_MIN_VERSION_MINOR) -#if defined(HIFI_GLES) +#include + +#if defined(Q_OS_ANDROID) #include +#else + +#ifndef GL_SLUMINANCE8_EXT +#define GL_SLUMINANCE8_EXT 0x8C47 #endif -#if defined(HIFI_GLES) -#include +// Prevent inclusion of System GL headers +#define __glext_h_ +#define __gl_h_ +#define __gl3_h_ -#define GL_DEPTH_COMPONENT32_OES 0x81A7 -#define GL_TIME_ELAPSED_EXT 0x88BF -#define GL_TIMESTAMP_EXT 0x8E28 -#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 -#define GL_TEXTURE_BORDER_COLOR_EXT 0x1004 -#define GL_CLAMP_TO_BORDER_EXT 0x812D -#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE -#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF - - -// Add some additional extensions missing from GLES 3.1 -extern "C" { - typedef void (GL_APIENTRYP PFNGLQUERYCOUNTEREXTPROC) (GLuint id, GLenum target); - typedef void (GL_APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64 *params); - typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); - extern PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT; - extern PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT; - extern PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT; -} - -#else // !defined(HIFI_GLES) - -#define GL_GLEXT_PROTOTYPES 1 -#include - -#if defined(Q_OS_DARWIN) -#include -#include -#include -#elif defined(Q_OS_WIN64) -#include #endif -#endif // !defined(Q_OS_ANDROID) - // Platform specific code to load the GL functions namespace gl { void initModuleGl(); + int getSwapInterval(); + void setSwapInterval(int swapInterval); } #endif // hifi_gpu_GPUConfig_h diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index f7e08e607b..8acbada5aa 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -23,10 +23,11 @@ #include #include #include "GLLogging.h" +#include "Config.h" #ifdef Q_OS_WIN -#ifdef DEBUG +#if defined(DEBUG) || defined(USE_GLES) static bool enableDebugLogger = true; #else static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL"); @@ -69,15 +70,9 @@ Context::Context(QWindow* window) { setWindow(window); } -#ifdef Q_OS_WIN -void Context::destroyWin32Context(HGLRC hglrc) { - wglDeleteContext(hglrc); -} -#endif - void Context::release() { doneCurrent(); -#ifdef Q_OS_WIN +#ifdef GL_CUSTOM_CONTEXT if (_wrappedContext) { destroyContext(_wrappedContext); _wrappedContext = nullptr; @@ -123,14 +118,68 @@ void Context::setWindow(QWindow* window) { release(); _window = window; -#ifdef Q_OS_WIN +#ifdef GL_CUSTOM_CONTEXT _hwnd = (HWND)window->winId(); #endif updateSwapchainMemoryCounter(); } -#ifdef Q_OS_WIN +void Context::clear() { + glClearColor(0, 0, 0, 1); + QSize windowSize = _window->size() * _window->devicePixelRatio(); + glViewport(0, 0, windowSize.width(), windowSize.height()); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + swapBuffers(); +} + +#if defined(GL_CUSTOM_CONTEXT) + +static void debugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { + if (GL_DEBUG_SEVERITY_NOTIFICATION == severity) { + return; + } + // FIXME For high severity errors, force a sync to the log, since we might crash + // before the log file was flushed otherwise. Performance hit here + qCDebug(glLogging) << "OpenGL: " << message; +} + +static void setupPixelFormatSimple(HDC hdc) { + // FIXME build the PFD based on the + static const PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be + { + sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor + 1, // Version Number + PFD_DRAW_TO_WINDOW | // Format Must Support Window + PFD_SUPPORT_OPENGL | // Format Must Support OpenGL + PFD_DOUBLEBUFFER, // Must Support Double Buffering + PFD_TYPE_RGBA, // Request An RGBA Format + 24, // Select Our Color Depth + 0, 0, 0, 0, 0, 0, // Color Bits Ignored + 1, // Alpha Buffer + 0, // Shift Bit Ignored + 0, // No Accumulation Buffer + 0, 0, 0, 0, // Accumulation Bits Ignored + 24, // 24 Bit Z-Buffer (Depth Buffer) + 8, // 8 Bit Stencil Buffer + 0, // No Auxiliary Buffer + PFD_MAIN_PLANE, // Main Drawing Layer + 0, // Reserved + 0, 0, 0 // Layer Masks Ignored + }; + auto pixelFormat = ChoosePixelFormat(hdc, &pfd); + if (pixelFormat == 0) { + throw std::runtime_error("Unable to create initial context"); + } + + if (SetPixelFormat(hdc, pixelFormat, &pfd) == FALSE) { + throw std::runtime_error("Unable to create initial context"); + } +} + +void Context::destroyWin32Context(HGLRC hglrc) { + wglDeleteContext(hglrc); +} bool Context::makeCurrent() { BOOL result = wglMakeCurrent(_hdc, _hglrc); @@ -148,56 +197,38 @@ void Context::doneCurrent() { wglMakeCurrent(0, 0); } -void GLAPIENTRY debugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { - if (GL_DEBUG_SEVERITY_NOTIFICATION == severity) { - return; - } - qCDebug(glLogging) << "OpenGL: " << message; - - // For high severity errors, force a sync to the log, since we might crash - // before the log file was flushed otherwise. Performance hit here - if (GL_DEBUG_SEVERITY_HIGH == severity) { - AbstractLoggerInterface* logger = AbstractLoggerInterface::get(); - if (logger) { - // FIXME find a way to force the log file to sync that doesn't lead to a deadlock - // logger->sync(); - } - } -} +// Pixel format arguments +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 -// FIXME build the PFD based on the -static const PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be -{ - sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor - 1, // Version Number - PFD_DRAW_TO_WINDOW | // Format Must Support Window - PFD_SUPPORT_OPENGL | // Format Must Support OpenGL - PFD_DOUBLEBUFFER, // Must Support Double Buffering - PFD_TYPE_RGBA, // Request An RGBA Format - 24, // Select Our Color Depth - 0, 0, 0, 0, 0, 0, // Color Bits Ignored - 1, // Alpha Buffer - 0, // Shift Bit Ignored - 0, // No Accumulation Buffer - 0, 0, 0, 0, // Accumulation Bits Ignored - 24, // 24 Bit Z-Buffer (Depth Buffer) - 8, // 8 Bit Stencil Buffer - 0, // No Auxiliary Buffer - PFD_MAIN_PLANE, // Main Drawing Layer - 0, // Reserved - 0, 0, 0 // Layer Masks Ignored -}; +// Context create arguments +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 -void setupPixelFormatSimple(HDC hdc) { - auto pixelFormat = ChoosePixelFormat(hdc, &pfd); - if (pixelFormat == 0) { - throw std::runtime_error("Unable to create initial context"); - } +// Context create flag bits +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 + +#if !defined(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#endif + +typedef BOOL(APIENTRYP PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +typedef HGLRC(APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int *attribList); + +GLAPI PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; +GLAPI PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; - if (SetPixelFormat(hdc, pixelFormat, &pfd) == FALSE) { - throw std::runtime_error("Unable to create initial context"); - } -} void Context::create() { if (!PRIMARY) { @@ -218,7 +249,6 @@ void Context::create() { if (qApp->property(hifi::properties::CRASHED).toBool()) { enableDebugLogger = true; } - auto hdc = GetDC(hwnd); setupPixelFormatSimple(hdc); auto glrc = wglCreateContext(hdc); @@ -227,20 +257,25 @@ void Context::create() { if (!makeCurrentResult) { throw std::runtime_error("Unable to create initial context"); } - glewExperimental = true; - glewInit(); - if (glewIsSupported("GL_VERSION_4_5")) { - _version = 0x0405; - } else if (glewIsSupported("GL_VERSION_4_3")) { - _version = 0x0403; - } - glGetError(); + gl::initModuleGl(); wglMakeCurrent(0, 0); wglDeleteContext(glrc); ReleaseDC(hwnd, hdc); }); _hdc = GetDC(_hwnd); +#if defined(USE_GLES) + _version = 0x0200; +#else + if (GLAD_GL_VERSION_4_5) { + _version = 0x0405; + } else if (GLAD_GL_VERSION_4_3) { + _version = 0x0403; + } else { + _version = 0x0401; + } +#endif + static int pixelFormat = 0; static PIXELFORMATDESCRIPTOR pfd; if (!pixelFormat) { @@ -261,6 +296,7 @@ void Context::create() { formatAttribs.push_back(24); formatAttribs.push_back(WGL_STENCIL_BITS_ARB); formatAttribs.push_back(8); + #ifdef NATIVE_SRGB_FRAMEBUFFER // formatAttribs.push_back(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); // formatAttribs.push_back(GL_TRUE); @@ -286,7 +322,11 @@ void Context::create() { contextAttribs.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); contextAttribs.push_back(minorVersion); contextAttribs.push_back(WGL_CONTEXT_PROFILE_MASK_ARB); +#if defined(USE_GLES) + contextAttribs.push_back(WGL_CONTEXT_ES2_PROFILE_BIT_EXT); +#else contextAttribs.push_back(WGL_CONTEXT_CORE_PROFILE_BIT_ARB); +#endif contextAttribs.push_back(WGL_CONTEXT_FLAGS_ARB); if (enableDebugLogger) { contextAttribs.push_back(WGL_CONTEXT_DEBUG_BIT_ARB); @@ -307,22 +347,17 @@ void Context::create() { qApp->setProperty(hifi::properties::gl::PRIMARY_CONTEXT, QVariant::fromValue((void*)PRIMARY)); } + if (!makeCurrent()) { + throw std::runtime_error("Could not make context current"); + } if (enableDebugLogger) { - makeCurrent(); glDebugMessageCallback(debugMessageCallback, NULL); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - doneCurrent(); } + doneCurrent(); } -#endif -void Context::clear() { - glClearColor(0, 0, 0, 1); - QSize windowSize = _window->size() * _window->devicePixelRatio(); - glViewport(0, 0, windowSize.width(), windowSize.height()); - glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - swapBuffers(); -} +#endif OffscreenContext::~OffscreenContext() { diff --git a/libraries/gl/src/gl/Context.h b/libraries/gl/src/gl/Context.h index d1b0d53631..4f0747a86f 100644 --- a/libraries/gl/src/gl/Context.h +++ b/libraries/gl/src/gl/Context.h @@ -22,6 +22,9 @@ class QWindow; class QOpenGLContext; class QThread; +#if defined(Q_OS_WIN) +#define GL_CUSTOM_CONTEXT +#endif namespace gl { class Context { @@ -29,7 +32,7 @@ namespace gl { QWindow* _window { nullptr }; static Context* PRIMARY; static void destroyContext(QOpenGLContext* context); -#if defined(Q_OS_WIN) +#if defined(GL_CUSTOM_CONTEXT) uint32_t _version { 0x0401 }; HWND _hwnd { 0 }; HDC _hdc { 0 }; diff --git a/libraries/gl/src/gl/ContextQt.cpp b/libraries/gl/src/gl/ContextQt.cpp index 4d5d2c4e9f..dd65c3076c 100644 --- a/libraries/gl/src/gl/ContextQt.cpp +++ b/libraries/gl/src/gl/ContextQt.cpp @@ -8,6 +8,8 @@ #include "Context.h" +#include "Config.h" + #include #include @@ -28,7 +30,7 @@ void Context::makeCurrent(QOpenGLContext* context, QSurface* surface) { } QOpenGLContext* Context::qglContext() { -#ifdef Q_OS_WIN +#ifdef GL_CUSTOM_CONTEXT if (!_wrappedContext) { _wrappedContext = new QOpenGLContext(); _wrappedContext->setNativeHandle(QVariant::fromValue(QWGLNativeContext(_hglrc, _hwnd))); @@ -45,10 +47,12 @@ void Context::moveToThread(QThread* thread) { qglContext()->moveToThread(thread); } -#ifndef Q_OS_WIN +#ifndef GL_CUSTOM_CONTEXT bool Context::makeCurrent() { updateSwapchainMemoryCounter(); - return _context->makeCurrent(_window); + bool result = _context->makeCurrent(_window); + gl::initModuleGl(); + return result; } void Context::swapBuffers() { diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 722253c26f..d5e3bb5a67 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -2,6 +2,8 @@ #include +#include "Config.h" + #include #include #include @@ -11,8 +13,6 @@ #include #include -#include - size_t evalGLFormatSwapchainPixelSize(const QSurfaceFormat& format) { size_t pixelSize = format.redBufferSize() + format.greenBufferSize() + format.blueBufferSize() + format.alphaBufferSize(); // We don't apply the length of the swap chain into this pixelSize since it is not vsible for the Process (on windows). @@ -28,18 +28,19 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { static QSurfaceFormat format; static std::once_flag once; std::call_once(once, [] { -#if defined(HIFI_GLES) +#if defined(USE_GLES) format.setRenderableType(QSurfaceFormat::OpenGLES); format.setRedBufferSize(8); format.setGreenBufferSize(8); format.setBlueBufferSize(8); format.setAlphaBufferSize(8); +#else + format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); #endif // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); setGLFormatVersion(format); - format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); QSurfaceFormat::setDefaultFormat(format); }); return format; diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index 73319e2e6d..581bb09360 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -26,7 +26,7 @@ class QGLFormat; template // https://bugreports.qt.io/browse/QTBUG-64703 prevents us from using "defined(QT_OPENGL_ES_3_1)" -#if defined(Q_OS_ANDROID) +#if defined(USE_GLES) void setGLFormatVersion(F& format, int major = 3, int minor = 2) #else void setGLFormatVersion(F& format, int major = 4, int minor = 5) diff --git a/libraries/gl/src/gl/GLWidget.cpp b/libraries/gl/src/gl/GLWidget.cpp index 23bdcff640..c1d049f7f3 100644 --- a/libraries/gl/src/gl/GLWidget.cpp +++ b/libraries/gl/src/gl/GLWidget.cpp @@ -69,6 +69,7 @@ void GLWidget::createContext() { _context->create(); _context->makeCurrent(); _context->clear(); + _context->doneCurrent(); } bool GLWidget::makeCurrent() { diff --git a/libraries/gl/src/gl/GLWindow.cpp b/libraries/gl/src/gl/GLWindow.cpp index c40301deb0..e1e6279b1c 100644 --- a/libraries/gl/src/gl/GLWindow.cpp +++ b/libraries/gl/src/gl/GLWindow.cpp @@ -8,6 +8,7 @@ #include "GLWindow.h" +#include "Config.h" #include #include @@ -19,26 +20,18 @@ void GLWindow::createContext(QOpenGLContext* shareContext) { } void GLWindow::createContext(const QSurfaceFormat& format, QOpenGLContext* shareContext) { - setSurfaceType(QSurface::OpenGLSurface); - setFormat(format); - _context = new QOpenGLContext; - _context->setFormat(format); - if (shareContext) { - _context->setShareContext(shareContext); - } + _context = new gl::Context(); + _context->setWindow(this); _context->create(); + _context->makeCurrent(); + _context->clear(); } GLWindow::~GLWindow() { - if (_context) { - _context->doneCurrent(); - _context->deleteLater(); - _context = nullptr; - } } bool GLWindow::makeCurrent() { - bool makeCurrentResult = _context->makeCurrent(this); + bool makeCurrentResult = _context->makeCurrent(); Q_ASSERT(makeCurrentResult); std::call_once(_reportOnce, []{ @@ -48,8 +41,6 @@ bool GLWindow::makeCurrent() { qCDebug(glLogging) << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); }); - Q_ASSERT(_context == QOpenGLContext::currentContext()); - return makeCurrentResult; } @@ -58,11 +49,11 @@ void GLWindow::doneCurrent() { } void GLWindow::swapBuffers() { - _context->swapBuffers(this); + _context->swapBuffers(); } QOpenGLContext* GLWindow::context() const { - return _context; + return _context->qglContext(); } diff --git a/libraries/gl/src/gl/GLWindow.h b/libraries/gl/src/gl/GLWindow.h index bccfcd7320..e3df21cb49 100644 --- a/libraries/gl/src/gl/GLWindow.h +++ b/libraries/gl/src/gl/GLWindow.h @@ -12,9 +12,9 @@ #include #include +#include "Context.h" class QOpenGLContext; -class QOpenGLDebugLogger; class GLWindow : public QWindow { public: @@ -26,8 +26,8 @@ public: void swapBuffers(); QOpenGLContext* context() const; private: + gl::Context* _context{ nullptr }; std::once_flag _reportOnce; - QOpenGLContext* _context{ nullptr }; }; #endif diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 67eb3520c9..8a476c45ad 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -12,6 +12,8 @@ #include "OffscreenGLCanvas.h" +#include "Config.h" + #include #include #include diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp deleted file mode 100644 index 771a8b9a75..0000000000 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// -// OpenGLVersionChecker.cpp -// libraries/gl/src/gl -// -// Created by David Rowe on 28 Jan 2016. -// 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 "OpenGLVersionChecker.h" -#include "Config.h" - -#include - -#include -#include -#include -#include - -#include "GLHelpers.h" - -OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) : - QApplication(argc, argv) -{ -} - -const QGLFormat& getDefaultGLFormat() { - // Specify an OpenGL 3.3 format using the Core profile. - // That is, no old-school fixed pipeline functionality - static QGLFormat glFormat; - static std::once_flag once; - std::call_once(once, [] { - setGLFormatVersion(glFormat); - glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0 - glFormat.setSampleBuffers(false); - glFormat.setDepth(false); - glFormat.setStencil(false); - QGLFormat::setDefaultFormat(glFormat); - }); - return glFormat; -} - - -QJsonObject OpenGLVersionChecker::checkVersion(bool& valid, bool& override) { - valid = true; - override = false; - - QGLWidget* glWidget = new QGLWidget(getDefaultGLFormat()); - - valid = glWidget->isValid(); - // Inform user if no OpenGL support - if (!valid) { - QMessageBox messageBox; - messageBox.setWindowTitle("Missing OpenGL Support"); - messageBox.setIcon(QMessageBox::Warning); - messageBox.setText(QString().sprintf("Your system does not support OpenGL, Interface cannot run.")); - messageBox.setInformativeText("Press OK to exit."); - messageBox.setStandardButtons(QMessageBox::Ok); - messageBox.setDefaultButton(QMessageBox::Ok); - messageBox.exec(); - return QJsonObject(); - } - - // Retrieve OpenGL version - // glWidget->initializeGL(); - glWidget->makeCurrent(); - QJsonObject glData = getGLContextData(); - delete glWidget; - - // Compare against minimum - // The GL_VERSION string begins with a version number in one of these forms: - // - major_number.minor_number - // - major_number.minor_number.release_number - // Reference: https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGetString.xml - - int minimumMajorNumber = (MINIMUM_GL_VERSION >> 8) & 0xFF; - int minimumMinorNumber = (MINIMUM_GL_VERSION & 0xFF); - int majorNumber = 0; - int minorNumber = 0; - const QString version { "version" }; - if (glData.contains(version)) { - QString glVersion = glData[version].toString(); - QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]")); - if (versionParts.size() >= 2) { - majorNumber = versionParts[0].toInt(); - minorNumber = versionParts[1].toInt(); - valid = (majorNumber > minimumMajorNumber - || (majorNumber == minimumMajorNumber && minorNumber >= minimumMinorNumber)); - } else { - valid = false; - } - } else { - valid = false; - } - - // Prompt user if below minimum - if (!valid) { - QMessageBox messageBox; - messageBox.setWindowTitle("OpenGL Version Too Low"); - messageBox.setIcon(QMessageBox::Warning); - messageBox.setText(QString().sprintf("Your OpenGL version of %i.%i is lower than the minimum of %i.%i.", - majorNumber, minorNumber, minimumMajorNumber, minimumMinorNumber)); - messageBox.setInformativeText("Press OK to exit; Ignore to continue."); - messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Ignore); - messageBox.setDefaultButton(QMessageBox::Ok); - override = messageBox.exec() == QMessageBox::Ignore; - } - - return glData; -} diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.h b/libraries/gl/src/gl/OpenGLVersionChecker.h deleted file mode 100644 index 913675af24..0000000000 --- a/libraries/gl/src/gl/OpenGLVersionChecker.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// OpenGLVersionChecker.h -// libraries/gl/src/gl -// -// Created by David Rowe on 28 Jan 2016. -// 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_OpenGLVersionChecker_h -#define hifi_OpenGLVersionChecker_h - -#include - -class OpenGLVersionChecker : public QApplication { - -public: - OpenGLVersionChecker(int& argc, char** argv); - - static QJsonObject checkVersion(bool& valid, bool& override); -}; - -#endif // hifi_OpenGLVersionChecker_h diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 2192f5f45b..eb6de5df13 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -153,30 +153,7 @@ void GLBackend::init() { qCDebug(gpugllogging) << "\tcard:" << gpu->getName(); qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver(); qCDebug(gpugllogging) << "\tdedicated memory:" << gpu->getMemory() << "MB"; - - glewExperimental = true; - GLenum err = glewInit(); - glGetError(); // clear the potential error from glewExperimental - if (GLEW_OK != err) { - // glewInit failed, something is seriously wrong. - qCDebug(gpugllogging, "Error: %s\n", glewGetErrorString(err)); - } - qCDebug(gpugllogging, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); - -#if defined(Q_OS_WIN) - if (wglewGetExtension("WGL_EXT_swap_control")) { - int swapInterval = wglGetSwapIntervalEXT(); - qCDebug(gpugllogging, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } -#endif - -#if defined(Q_OS_LINUX) - // TODO: Write the correct code for Linux... - /* if (wglewGetExtension("WGL_EXT_swap_control")) { - int swapInterval = wglGetSwapIntervalEXT(); - qCDebug(gpugllogging, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - }*/ -#endif + qCDebug(gpugllogging, "V-Sync is %s\n", (::gl::getSwapInterval() > 0 ? "ON" : "OFF")); #if THREADED_TEXTURE_BUFFERING // This has to happen on the main thread in order to give the thread // pool a reasonable parent object diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index 7b3db4e4fe..1873034ac5 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -75,7 +75,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { break; case gpu::NUINT8: if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { - result = GL_SLUMINANCE8; + result = GL_SLUMINANCE8_EXT; } else { result = GL_R8; } @@ -491,7 +491,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E } case gpu::NUINT8: { if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { - texel.internalFormat = GL_SLUMINANCE8; + texel.internalFormat = GL_SLUMINANCE8_EXT; } else { texel.internalFormat = GL_R8; } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp index 32bfb777a8..88cf89ad99 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp @@ -102,7 +102,7 @@ void GL41Backend::do_drawInstanced(const Batch& batch, size_t paramOffset) { _stats._DSNumTriangles += (trueNumInstances * numVertices) / 3; _stats._DSNumDrawcalls += trueNumInstances; } else { - glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances); + glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); _stats._DSNumTriangles += (numInstances * numVertices) / 3; _stats._DSNumDrawcalls += numInstances; } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index cde4ff5f75..925e83bb77 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -156,7 +156,7 @@ void GL41Texture::syncSampler() const { glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, fm.magFilter); if (sampler.doComparison()) { - glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE_ARB); glTexParameteri(_target, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); } else { glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_NONE); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 795a630ccd..1d415c7ca3 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -197,7 +197,7 @@ void GL45Texture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_MAG_FILTER, fm.magFilter); if (sampler.doComparison()) { - glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE_ARB); glTextureParameteri(_id, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); } else { glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_NONE); diff --git a/libraries/gpu-gles/CMakeLists.txt b/libraries/gpu-gles/CMakeLists.txt index 55ec53b184..16575a7018 100644 --- a/libraries/gpu-gles/CMakeLists.txt +++ b/libraries/gpu-gles/CMakeLists.txt @@ -1,11 +1,5 @@ set(TARGET_NAME gpu-gles) -setup_hifi_library(OpenGL) +setup_hifi_library(Concurrent) link_hifi_libraries(shared gl gpu) GroupSources("src") - target_opengl() -target_nsight() - -if (NOT ANDROID) - target_glew() -endif () diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp index cb00d00b3e..6fd5df6f81 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp @@ -22,17 +22,18 @@ #include "nvToolsExt.h" #endif +#include #include #include #include #include "GLTexture.h" #include "GLShader.h" + using namespace gpu; using namespace gpu::gl; static GLBackend* INSTANCE{ nullptr }; -static const char* GL_BACKEND_PROPERTY_NAME = "com.highfidelity.gl.backend"; BackendPointer GLBackend::createBackend() { // FIXME provide a mechanism to override the backend for testing @@ -45,18 +46,17 @@ BackendPointer GLBackend::createBackend() { result->initInput(); result->initTransform(); + result->initTextureManagementStage(); INSTANCE = result.get(); void* voidInstance = &(*result); - qApp->setProperty(GL_BACKEND_PROPERTY_NAME, QVariant::fromValue(voidInstance)); - - gl::GLTexture::initTextureTransferHelper(); + qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); return result; } GLBackend& getBackend() { if (!INSTANCE) { - INSTANCE = static_cast(qApp->property(GL_BACKEND_PROPERTY_NAME).value()); + INSTANCE = static_cast(qApp->property(hifi::properties::gl::BACKEND).value()); } return *INSTANCE; } @@ -65,9 +65,6 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin return GLShader::makeProgram(getBackend(), shader, slotBindings); } -std::array commandNames = { - {QString("draw"),QString("drawIndexed"),QString("drawInstanced"),QString("drawIndexedInstanced"),QString("multiDrawIndirect"),QString("multiDrawIndexedIndirect"),QString("setInputFormat"),QString("setInputBuffer"),QString("setIndexBuffer"),QString("setIndirectBuffer"),QString("setModelTransform"),QString("setViewTransform"),QString("setProjectionTransform"),QString("setViewportTransform"),QString("setDepthRangeTransform"),QString("setPipeline"),QString("setStateBlendFactor"),QString("setStateScissorRect"),QString("setUniformBuffer"),QString("setResourceTexture"),QString("setFramebuffer"),QString("clearFramebuffer"),QString("blit"),QString("generateTextureMips"),QString("beginQuery"),QString("endQuery"),QString("getQuery"),QString("resetStages"),QString("runLambda"),QString("startNamedCall"),QString("stopNamedCall"),QString("glUniform1i"),QString("glUniform1f"),QString("glUniform2f"),QString("glUniform3f"),QString("glUniform4f"),QString("glUniform3fv"),QString("glUniform4fv"),QString("glUniform4iv"),QString("glUniformMatrix3fv"),QString("glUniformMatrix4fv"),QString("glColor4f"),QString("pushProfileRange"),QString("popProfileRange"),QString("NUM_COMMANDS")} -}; GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = { @@ -94,6 +91,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_setStateScissorRect), (&::gpu::gl::GLBackend::do_setUniformBuffer), + (&::gpu::gl::GLBackend::do_setResourceBuffer), (&::gpu::gl::GLBackend::do_setResourceTexture), (&::gpu::gl::GLBackend::do_setFramebuffer), @@ -107,6 +105,11 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_resetStages), + (&::gpu::gl::GLBackend::do_disableContextViewCorrection), + (&::gpu::gl::GLBackend::do_restoreContextViewCorrection), + (&::gpu::gl::GLBackend::do_disableContextStereo), + (&::gpu::gl::GLBackend::do_restoreContextStereo), + (&::gpu::gl::GLBackend::do_runLambda), (&::gpu::gl::GLBackend::do_startNamedCall), @@ -144,16 +147,11 @@ void GLBackend::init() { qCDebug(gpugllogging) << "\tcard:" << gpu->getName(); qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver(); qCDebug(gpugllogging) << "\tdedicated memory:" << gpu->getMemory() << "MB"; - - /*glewExperimental = true; - GLenum err = glewInit(); - glGetError(); // clear the potential error from glewExperimental - if (GLEW_OK != err) { - // glewInit failed, something is seriously wrong. - qCDebug(gpugllogging, "Error: %s\n", glewGetErrorString(err)); - } - qCDebug(gpugllogging, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); - */ +#if THREADED_TEXTURE_BUFFERING + // This has to happen on the main thread in order to give the thread + // pool a reasonable parent object + GLVariableAllocationSupport::TransferJob::startBufferingThread(); +#endif }); } @@ -165,8 +163,6 @@ GLBackend::GLBackend() { GLBackend::~GLBackend() { - resetStages(); - killInput(); killTransform(); } @@ -178,7 +174,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { _inRenderTransferPass = true; { // Sync all the buffers - ANDROID_PROFILE(render, "syncGPUBuffer", 0xffaaffaa, 1) + PROFILE_RANGE(render_gpu_gl_detail, "syncGPUBuffer"); for (auto& cached : batch._buffers._items) { if (cached._data) { @@ -187,8 +183,8 @@ void GLBackend::renderPassTransfer(const Batch& batch) { } } - { // Sync all the buffers - ANDROID_PROFILE(render, "syncCPUTransform", 0xffaaaaff, 1) + { // Sync all the transform states + PROFILE_RANGE(render_gpu_gl_detail, "syncCPUTransform"); _transform._cameras.clear(); _transform._cameraOffsets.clear(); @@ -203,10 +199,17 @@ void GLBackend::renderPassTransfer(const Batch& batch) { _transform.preUpdate(_commandIndex, _stereo); break; + case Batch::COMMAND_disableContextStereo: + _stereo._contextDisable = true; + break; + + case Batch::COMMAND_restoreContextStereo: + _stereo._contextDisable = false; + break; + case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: { - ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xffeeaaff, 1) CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); break; @@ -221,8 +224,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { } { // Sync the transform buffers - //PROFILE_RANGE(render_gpu_gl, "transferTransformState"); - ANDROID_PROFILE(render, "transferTransformState", 0xff0000ff, 1) + PROFILE_RANGE(render_gpu_gl_detail, "transferGPUTransform"); transferTransformState(batch); } @@ -257,14 +259,12 @@ void GLBackend::renderPassDraw(const Batch& batch) { updateInput(); updateTransform(batch); updatePipeline(); - {ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xff0000ff, 1) + CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); - } break; } default: { - ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xffff00ff, 1) CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); break; @@ -277,7 +277,6 @@ void GLBackend::renderPassDraw(const Batch& batch) { } void GLBackend::render(const Batch& batch) { - ANDROID_PROFILE(render, "GLBackendRender", 0xffff00ff, 1) _transform._skybox = _stereo._skybox = batch.isSkyboxEnabled(); // Allow the batch to override the rendering stereo settings // for things like full framebuffer copy operations (deferred lighting passes) @@ -287,16 +286,24 @@ void GLBackend::render(const Batch& batch) { } { - //PROFILE_RANGE(render_gpu_gl, "Transfer"); - ANDROID_PROFILE(render, "Transfer", 0xff0000ff, 1) + PROFILE_RANGE(render_gpu_gl_detail, "Transfer"); renderPassTransfer(batch); } +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + if (_stereo.isStereo()) { + glEnable(GL_CLIP_DISTANCE0_EXT); + } +#endif { - //PROFILE_RANGE(render_gpu_gl, _stereo._enable ? "Render Stereo" : "Render"); - ANDROID_PROFILE(render, "RenderPassDraw", 0xff00ddff, 1) + PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render"); renderPassDraw(batch); } +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + if (_stereo.isStereo()) { + glDisable(GL_CLIP_DISTANCE0_EXT); + } +#endif // Restore the saved stereo state for the next batch _stereo._enable = savedStereo; @@ -304,15 +311,15 @@ void GLBackend::render(const Batch& batch) { void GLBackend::syncCache() { + PROFILE_RANGE(render_gpu_gl_detail, __FUNCTION__); + syncTransformStateCache(); syncPipelineStateCache(); syncInputStateCache(); syncOutputStateCache(); - - //glEnable(GL_LINE_SMOOTH); - qDebug() << "TODO: GLBackend.cpp:syncCache GL_LINE_SMOOTH"; } +#ifdef GPU_STEREO_DRAWCALL_DOUBLED void GLBackend::setupStereoSide(int side) { ivec4 vp = _transform._viewport; vp.z /= 2; @@ -320,19 +327,35 @@ void GLBackend::setupStereoSide(int side) { #ifdef GPU_STEREO_CAMERA_BUFFER #ifdef GPU_STEREO_DRAWCALL_DOUBLED - //glVertexAttribI1i(14, side); - glVertexAttribI4i(14, side, 0, 0, 0); - + glVertexAttribI1i(14, side); #endif #else _transform.bindCurrentCamera(side); #endif } +#else +#endif void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { resetStages(); } +void GLBackend::do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) { + _transform._viewCorrectionEnabled = false; +} + +void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) { + _transform._viewCorrectionEnabled = true; +} + +void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) { + +} + +void GLBackend::do_restoreContextStereo(const Batch& batch, size_t paramOffset) { + +} + void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) { std::function f = batch._lambdas.get(batch._params[paramOffset]._uint); f(); @@ -361,18 +384,22 @@ void GLBackend::resetStages() { void GLBackend::do_pushProfileRange(const Batch& batch, size_t paramOffset) { - auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); - profileRanges.push_back(name); + if (trace_render_gpu_gl_detail().isDebugEnabled()) { + auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); + profileRanges.push_back(name); #if defined(NSIGHT_FOUND) - nvtxRangePush(name.c_str()); + nvtxRangePush(name.c_str()); #endif + } } void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) { - profileRanges.pop_back(); + if (trace_render_gpu_gl_detail().isDebugEnabled()) { + profileRanges.pop_back(); #if defined(NSIGHT_FOUND) - nvtxRangePop(); + nvtxRangePop(); #endif + } } // TODO: As long as we have gl calls explicitely issued from interface @@ -380,8 +407,11 @@ void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) { // term strategy is to get rid of any GL calls in favor of the HIFI GPU API // As long as we don;t use several versions of shaders we can avoid this more complex code path -// #define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, isStereo()); +#ifdef GPU_STEREO_CAMERA_BUFFER +#define GET_UNIFORM_LOCATION(shaderUniformLoc) ((_pipeline._programShader) ? _pipeline._programShader->getUniformLocation(shaderUniformLoc, (GLShader::Version) isStereo()) : -1) +#else #define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc +#endif void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { @@ -391,7 +421,7 @@ void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) { } updatePipeline(); - glUniform1f( + glUniform1i( GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int), batch._params[paramOffset + 0]._int); (void)CHECK_GL_ERROR(); @@ -545,6 +575,10 @@ void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { if (_input._colorAttribute != newColor) { _input._colorAttribute = newColor; glVertexAttrib4fv(gpu::Stream::COLOR, &_input._colorAttribute.r); + // Color has been changed and is not white. To prevent colors from bleeding + // between different objects, we need to set the _hadColorAttribute flag + // as if a previous render call had potential colors + _input._hadColorAttribute = (newColor != glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); } (void)CHECK_GL_ERROR(); } @@ -590,6 +624,7 @@ void GLBackend::queueLambda(const std::function lambda) const { } void GLBackend::recycle() const { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__) { std::list> lamdbasTrash; { @@ -709,9 +744,9 @@ void GLBackend::recycle() const { } } -#ifndef THREADED_TEXTURE_TRANSFER - gl::GLTexture::_textureTransferHelper->process(); -#endif + GLVariableAllocationSupport::manageMemory(); + GLVariableAllocationSupport::_frameTexturesCreated = 0; + Texture::KtxStorage::releaseOpenKtxFiles(); } void GLBackend::setCameraCorrection(const Mat4& correction) { diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.h b/libraries/gpu-gles/src/gpu/gl/GLBackend.h index f8f307bc17..ea06b6b672 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.h @@ -29,9 +29,16 @@ // Different versions for the stereo drawcall // Current preferred is "instanced" which draw the shape twice but instanced and rely on clipping plane to draw left/right side only +#define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE //#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER //#define GPU_STEREO_TECHNIQUE_INSTANCED + +// Let these be configured by the one define picked above +#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE +#define GPU_STEREO_DRAWCALL_DOUBLED +#endif + #ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER #define GPU_STEREO_DRAWCALL_DOUBLED #define GPU_STEREO_CAMERA_BUFFER @@ -42,259 +49,265 @@ #define GPU_STEREO_CAMERA_BUFFER #endif -//#define ANDROID_INTENSIVE_INSTRUMENTATION 1 - -#ifdef ANDROID_INTENSIVE_INSTRUMENTATION -#define ANDROID_PROFILE_COMMAND(category, commandIndex, argbColor, payload, ...) PROFILE_RANGE_EX(category, commandNames[commandIndex], argbColor, payload, ##__VA_ARGS__); -#define ANDROID_PROFILE(category, name, argbColor, payload, ...) PROFILE_RANGE_EX(category, name, argbColor, payload, ##__VA_ARGS__); -#else -#define ANDROID_PROFILE_COMMAND(category, commandIndex, argbColor, payload, ...) -#define ANDROID_PROFILE(category, name, argbColor, payload, ...) -#endif namespace gpu { namespace gl { - class GLBackend : public Backend, public std::enable_shared_from_this { - // Context Backend static interface required - friend class gpu::Context; - static void init(); - static BackendPointer createBackend(); +class GLBackend : public Backend, public std::enable_shared_from_this { + // Context Backend static interface required + friend class gpu::Context; + static void init(); + static BackendPointer createBackend(); - protected: - explicit GLBackend(bool syncCache); - GLBackend(); - public: - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); +protected: + explicit GLBackend(bool syncCache); + GLBackend(); +public: + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); - ~GLBackend(); + virtual ~GLBackend(); - void setCameraCorrection(const Mat4& correction); - void render(const Batch& batch) final override; + void setCameraCorrection(const Mat4& correction); + void render(const Batch& batch) final override; - // This call synchronize the Full Backend cache with the current GLState - // THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync - // the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls - // Let's try to avoid to do that as much as possible! - void syncCache() final override; + // This call synchronize the Full Backend cache with the current GLState + // THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync + // the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls + // Let's try to avoid to do that as much as possible! + void syncCache() final override; - // This is the ugly "download the pixels to sysmem for taking a snapshot" - // Just avoid using it, it's ugly and will break performances - virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, - const Vec4i& region, QImage& destImage) final override; + // This is the ugly "download the pixels to sysmem for taking a snapshot" + // Just avoid using it, it's ugly and will break performances + virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, + const Vec4i& region, QImage& destImage) final override; - // this is the maximum numeber of available input buffers - size_t getNumInputBuffers() const { return _input._invalidBuffers.size(); } + // this is the maximum numeber of available input buffers + size_t getNumInputBuffers() const { return _input._invalidBuffers.size(); } - // this is the maximum per shader stage on the low end apple - // TODO make it platform dependant at init time - static const int MAX_NUM_UNIFORM_BUFFERS = 12; - size_t getMaxNumUniformBuffers() const { return MAX_NUM_UNIFORM_BUFFERS; } + // this is the maximum per shader stage on the low end apple + // TODO make it platform dependant at init time + static const int MAX_NUM_UNIFORM_BUFFERS = 12; + size_t getMaxNumUniformBuffers() const { return MAX_NUM_UNIFORM_BUFFERS; } - // this is the maximum per shader stage on the low end apple - // TODO make it platform dependant at init time - static const int MAX_NUM_RESOURCE_TEXTURES = 16; - size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; } + // this is the maximum per shader stage on the low end apple + // TODO make it platform dependant at init time + static const int MAX_NUM_RESOURCE_BUFFERS = 16; + size_t getMaxNumResourceBuffers() const { return MAX_NUM_RESOURCE_BUFFERS; } + static const int MAX_NUM_RESOURCE_TEXTURES = 16; + size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; } - // Draw Stage - virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0; - virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0; - virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0; + // Draw Stage + virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0; + virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0; + virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0; - // Input Stage - virtual void do_setInputFormat(const Batch& batch, size_t paramOffset) final; - virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; + // Input Stage + virtual void do_setInputFormat(const Batch& batch, size_t paramOffset) final; + virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; - // Transform Stage - virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final; + // Transform Stage + virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final; - // Uniform Stage - virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final; + // Uniform Stage + virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final; - // Resource Stage - virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; + // Resource Stage + virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; - // Pipeline Stage - virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final; + // Pipeline Stage + virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final; - // Output stage - virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_blit(const Batch& batch, size_t paramOffset) = 0; + // Output stage + virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_blit(const Batch& batch, size_t paramOffset) = 0; - // Query section - virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final; - virtual void do_endQuery(const Batch& batch, size_t paramOffset) final; - virtual void do_getQuery(const Batch& batch, size_t paramOffset) final; + // Query section + virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final; + virtual void do_endQuery(const Batch& batch, size_t paramOffset) final; + virtual void do_getQuery(const Batch& batch, size_t paramOffset) final; - // Reset stages - virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; + // Reset stages + virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; - virtual void do_runLambda(const Batch& batch, size_t paramOffset) final; + + virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final; + virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final; - virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final; - virtual void do_stopNamedCall(const Batch& batch, size_t paramOffset) final; + virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final; + virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final; - static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; - // The drawcall Info attribute channel is reserved and is the upper bound for the number of availables Input buffers - static const int MAX_NUM_INPUT_BUFFERS = Stream::DRAW_CALL_INFO; + virtual void do_runLambda(const Batch& batch, size_t paramOffset) final; - virtual void do_pushProfileRange(const Batch& batch, size_t paramOffset) final; - virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final; + virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final; + virtual void do_stopNamedCall(const Batch& batch, size_t paramOffset) final; - // TODO: As long as we have gl calls explicitely issued from interface - // code, we need to be able to record and batch these calls. THe long - // term strategy is to get rid of any GL calls in favor of the HIFI GPU API - virtual void do_glUniform1i(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform1f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform2f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform3f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform3fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4iv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final; + static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; + // The drawcall Info attribute channel is reserved and is the upper bound for the number of availables Input buffers + static const int MAX_NUM_INPUT_BUFFERS = Stream::DRAW_CALL_INFO; - virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final; + virtual void do_pushProfileRange(const Batch& batch, size_t paramOffset) final; + virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final; - // The State setters called by the GLState::Commands when a new state is assigned - virtual void do_setStateFillMode(int32 mode) final; - virtual void do_setStateCullMode(int32 mode) final; - virtual void do_setStateFrontFaceClockwise(bool isClockwise) final; - virtual void do_setStateDepthClampEnable(bool enable) final; - virtual void do_setStateScissorEnable(bool enable) final; - virtual void do_setStateMultisampleEnable(bool enable) final; - virtual void do_setStateAntialiasedLineEnable(bool enable) final; - virtual void do_setStateDepthBias(Vec2 bias) final; - virtual void do_setStateDepthTest(State::DepthTest test) final; - virtual void do_setStateStencil(State::StencilActivation activation, State::StencilTest frontTest, State::StencilTest backTest) final; - virtual void do_setStateAlphaToCoverageEnable(bool enable) final; - virtual void do_setStateSampleMask(uint32 mask) final; - virtual void do_setStateBlend(State::BlendFunction blendFunction) final; - virtual void do_setStateColorWriteMask(uint32 mask) final; - virtual void do_setStateBlendFactor(const Batch& batch, size_t paramOffset) final; - virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final; + // TODO: As long as we have gl calls explicitely issued from interface + // code, we need to be able to record and batch these calls. THe long + // term strategy is to get rid of any GL calls in favor of the HIFI GPU API + virtual void do_glUniform1i(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform1f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform2f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform3f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform3fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4iv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final; - virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; - virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0; - virtual GLuint getBufferID(const Buffer& buffer) = 0; - virtual GLuint getQueryID(const QueryPointer& query) = 0; - virtual bool isTextureReady(const TexturePointer& texture); + virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final; - virtual void releaseBuffer(GLuint id, Size size) const; - virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const; - virtual void releaseTexture(GLuint id, Size size) const; - virtual void releaseFramebuffer(GLuint id) const; - virtual void releaseShader(GLuint id) const; - virtual void releaseProgram(GLuint id) const; - virtual void releaseQuery(GLuint id) const; - virtual void queueLambda(const std::function lambda) const; + // The State setters called by the GLState::Commands when a new state is assigned + virtual void do_setStateFillMode(int32 mode) final; + virtual void do_setStateCullMode(int32 mode) final; + virtual void do_setStateFrontFaceClockwise(bool isClockwise) final; + virtual void do_setStateDepthClampEnable(bool enable) final; + virtual void do_setStateScissorEnable(bool enable) final; + virtual void do_setStateMultisampleEnable(bool enable) final; + virtual void do_setStateAntialiasedLineEnable(bool enable) final; + virtual void do_setStateDepthBias(Vec2 bias) final; + virtual void do_setStateDepthTest(State::DepthTest test) final; + virtual void do_setStateStencil(State::StencilActivation activation, State::StencilTest frontTest, State::StencilTest backTest) final; + virtual void do_setStateAlphaToCoverageEnable(bool enable) final; + virtual void do_setStateSampleMask(uint32 mask) final; + virtual void do_setStateBlend(State::BlendFunction blendFunction) final; + virtual void do_setStateColorWriteMask(uint32 mask) final; + virtual void do_setStateBlendFactor(const Batch& batch, size_t paramOffset) final; + virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final; - bool isTextureManagementSparseEnabled() const override { return (_textureManagement._sparseCapable && Texture::getEnableSparseTextures()); } + virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; + virtual GLuint getTextureID(const TexturePointer& texture) final; + virtual GLuint getBufferID(const Buffer& buffer) = 0; + virtual GLuint getQueryID(const QueryPointer& query) = 0; - protected: + virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; + virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; + virtual GLTexture* syncGPUObject(const TexturePointer& texture); + virtual GLQuery* syncGPUObject(const Query& query) = 0; + //virtual bool isTextureReady(const TexturePointer& texture); - void recycle() const override; - virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; - virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; - virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0; - virtual GLQuery* syncGPUObject(const Query& query) = 0; + virtual void releaseBuffer(GLuint id, Size size) const; + virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const; + virtual void releaseTexture(GLuint id, Size size) const; + virtual void releaseFramebuffer(GLuint id) const; + virtual void releaseShader(GLuint id) const; + virtual void releaseProgram(GLuint id) const; + virtual void releaseQuery(GLuint id) const; + virtual void queueLambda(const std::function lambda) const; - static const size_t INVALID_OFFSET = (size_t)-1; - bool _inRenderTransferPass { false }; - int32_t _uboAlignment { 0 }; - int _currentDraw { -1 }; + bool isTextureManagementSparseEnabled() const override { return (_textureManagement._sparseCapable && Texture::getEnableSparseTextures()); } - std::list profileRanges; - mutable Mutex _trashMutex; - mutable std::list> _buffersTrash; - mutable std::list> _texturesTrash; - mutable std::list> _externalTexturesTrash; - mutable std::list _framebuffersTrash; - mutable std::list _shadersTrash; - mutable std::list _programsTrash; - mutable std::list _queriesTrash; - mutable std::list> _lambdaQueue; +protected: - void renderPassTransfer(const Batch& batch); - void renderPassDraw(const Batch& batch); - void setupStereoSide(int side); + void recycle() const override; - virtual void initInput() final; - virtual void killInput() final; - virtual void syncInputStateCache() final; - virtual void resetInputStage(); - virtual void updateInput(); + static const size_t INVALID_OFFSET = (size_t)-1; + bool _inRenderTransferPass { false }; + int32_t _uboAlignment { 0 }; + int _currentDraw { -1 }; - struct InputStageState { - bool _invalidFormat { true }; - Stream::FormatPointer _format; - std::string _formatKey; + std::list profileRanges; + mutable Mutex _trashMutex; + mutable std::list> _buffersTrash; + mutable std::list> _texturesTrash; + mutable std::list> _externalTexturesTrash; + mutable std::list _framebuffersTrash; + mutable std::list _shadersTrash; + mutable std::list _programsTrash; + mutable std::list _queriesTrash; + mutable std::list> _lambdaQueue; - typedef std::bitset ActivationCache; - ActivationCache _attributeActivation { 0 }; + void renderPassTransfer(const Batch& batch); + void renderPassDraw(const Batch& batch); - typedef std::bitset BuffersState; +#ifdef GPU_STEREO_DRAWCALL_DOUBLED + void setupStereoSide(int side); +#endif - BuffersState _invalidBuffers{ 0 }; - BuffersState _attribBindingBuffers{ 0 }; + virtual void initInput() final; + virtual void killInput() final; + virtual void syncInputStateCache() final; + virtual void resetInputStage(); + virtual void updateInput() = 0; - Buffers _buffers; - Offsets _bufferOffsets; - Offsets _bufferStrides; - std::vector _bufferVBOs; + struct InputStageState { + bool _invalidFormat { true }; + bool _hadColorAttribute{ true }; + Stream::FormatPointer _format; + std::string _formatKey; - glm::vec4 _colorAttribute{ 0.0f }; + typedef std::bitset ActivationCache; + ActivationCache _attributeActivation { 0 }; - BufferPointer _indexBuffer; - Offset _indexBufferOffset { 0 }; - Type _indexBufferType { UINT32 }; + typedef std::bitset BuffersState; - BufferPointer _indirectBuffer; - Offset _indirectBufferOffset{ 0 }; - Offset _indirectBufferStride{ 0 }; + BuffersState _invalidBuffers{ 0 }; + BuffersState _attribBindingBuffers{ 0 }; - GLuint _defaultVAO { 0 }; + Buffers _buffers; + Offsets _bufferOffsets; + Offsets _bufferStrides; + std::vector _bufferVBOs; - InputStageState() : - _invalidFormat(true), - _format(0), - _formatKey(), - _attributeActivation(0), - _buffers(_invalidBuffers.size(), BufferPointer(0)), - _bufferOffsets(_invalidBuffers.size(), 0), - _bufferStrides(_invalidBuffers.size(), 0), - _bufferVBOs(_invalidBuffers.size(), 0) {} - } _input; + glm::vec4 _colorAttribute{ 0.0f }; - virtual void initTransform() = 0; - void killTransform(); - // Synchronize the state cache of this Backend with the actual real state of the GL Context - void syncTransformStateCache(); - void updateTransform(const Batch& batch); - void resetTransformStage(); + BufferPointer _indexBuffer; + Offset _indexBufferOffset { 0 }; + Type _indexBufferType { UINT32 }; + + BufferPointer _indirectBuffer; + Offset _indirectBufferOffset{ 0 }; + Offset _indirectBufferStride{ 0 }; - // Allows for correction of the camera pose to account for changes - // between the time when a was recorded and the time(s) when it is - // executed - struct CameraCorrection { - Mat4 correction; - Mat4 correctionInverse; - }; + GLuint _defaultVAO { 0 }; - struct TransformStageState { + InputStageState() : + _invalidFormat(true), + _format(0), + _formatKey(), + _attributeActivation(0), + _buffers(_invalidBuffers.size(), BufferPointer(0)), + _bufferOffsets(_invalidBuffers.size(), 0), + _bufferStrides(_invalidBuffers.size(), 0), + _bufferVBOs(_invalidBuffers.size(), 0) {} + } _input; + + virtual void initTransform() = 0; + void killTransform(); + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncTransformStateCache(); + virtual void updateTransform(const Batch& batch) = 0; + virtual void resetTransformStage(); + + // Allows for correction of the camera pose to account for changes + // between the time when a was recorded and the time(s) when it is + // executed + struct CameraCorrection { + Mat4 correction; + Mat4 correctionInverse; + }; + + struct TransformStageState { #ifdef GPU_STEREO_CAMERA_BUFFER - struct Cameras { + struct Cameras { TransformCamera _cams[2]; Cameras() {}; @@ -304,124 +317,158 @@ namespace gpu { namespace gl { using CameraBufferElement = Cameras; #else - using CameraBufferElement = TransformCamera; + using CameraBufferElement = TransformCamera; #endif - using TransformCameras = std::vector; + using TransformCameras = std::vector; - TransformCamera _camera; - TransformCameras _cameras; + TransformCamera _camera; + TransformCameras _cameras; - mutable std::map _drawCallInfoOffsets; + mutable std::map _drawCallInfoOffsets; - GLuint _objectBuffer { 0 }; - GLuint _cameraBuffer { 0 }; - GLuint _drawCallInfoBuffer { 0 }; - GLuint _objectBufferTexture { 0 }; - size_t _cameraUboSize { 0 }; - bool _viewIsCamera{ false }; - bool _skybox { false }; - Transform _view; - CameraCorrection _correction; + GLuint _objectBuffer { 0 }; + GLuint _cameraBuffer { 0 }; + GLuint _drawCallInfoBuffer { 0 }; + GLuint _objectBufferTexture { 0 }; + size_t _cameraUboSize { 0 }; + bool _viewIsCamera{ false }; + bool _skybox { false }; + Transform _view; + CameraCorrection _correction; + bool _viewCorrectionEnabled{ true }; - Mat4 _projection; - Vec4i _viewport { 0, 0, 1, 1 }; - Vec2 _depthRange { 0.0f, 1.0f }; - bool _invalidView { false }; - bool _invalidProj { false }; - bool _invalidViewport { false }; - bool _enabledDrawcallInfoBuffer{ false }; + Mat4 _projection; + Vec4i _viewport { 0, 0, 1, 1 }; + Vec2 _depthRange { 0.0f, 1.0f }; + bool _invalidView { false }; + bool _invalidProj { false }; + bool _invalidViewport { false }; - using Pair = std::pair; - using List = std::list; - List _cameraOffsets; - mutable List::const_iterator _camerasItr; - mutable size_t _currentCameraOffset{ INVALID_OFFSET }; + bool _enabledDrawcallInfoBuffer{ false }; - void preUpdate(size_t commandIndex, const StereoState& stereo); - void update(size_t commandIndex, const StereoState& stereo) const; - void bindCurrentCamera(int stereoSide) const; - } _transform; + using Pair = std::pair; + using List = std::list; + List _cameraOffsets; + mutable List::const_iterator _camerasItr; + mutable size_t _currentCameraOffset{ INVALID_OFFSET }; - virtual void transferTransformState(const Batch& batch) const = 0; + void preUpdate(size_t commandIndex, const StereoState& stereo); + void update(size_t commandIndex, const StereoState& stereo) const; + void bindCurrentCamera(int stereoSide) const; + } _transform; - struct UniformStageState { - std::array _buffers; - //Buffers _buffers { }; - } _uniform; + virtual void transferTransformState(const Batch& batch) const = 0; - void releaseUniformBuffer(uint32_t slot); - void resetUniformStage(); + struct UniformStageState { + std::array _buffers; + //Buffers _buffers { }; + } _uniform; - // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s - void releaseResourceTexture(uint32_t slot); + void releaseUniformBuffer(uint32_t slot); + void resetUniformStage(); - void resetResourceStage(); + // update resource cache and do the gl bind/unbind call with the current gpu::Buffer cached at slot s + // This is using different gl object depending on the gl version + virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0; + virtual void releaseResourceBuffer(uint32_t slot) = 0; - struct ResourceStageState { - std::array _textures; - //Textures _textures { { MAX_NUM_RESOURCE_TEXTURES } }; - int findEmptyTextureSlot() const; - } _resource; + // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s + void releaseResourceTexture(uint32_t slot); - size_t _commandIndex{ 0 }; + void resetResourceStage(); - // Standard update pipeline check that the current Program and current State or good to go for a - void updatePipeline(); - // Force to reset all the state fields indicated by the 'toBeReset" signature - void resetPipelineState(State::Signature toBeReset); - // Synchronize the state cache of this Backend with the actual real state of the GL Context - void syncPipelineStateCache(); - void resetPipelineStage(); + struct ResourceStageState { + std::array _buffers; + std::array _textures; + //Textures _textures { { MAX_NUM_RESOURCE_TEXTURES } }; + int findEmptyTextureSlot() const; + } _resource; - struct PipelineStageState { - PipelinePointer _pipeline; + size_t _commandIndex{ 0 }; - GLuint _program { 0 }; - GLint _cameraCorrectionLocation { -1 }; - GLShader* _programShader { nullptr }; - bool _invalidProgram { false }; + // Standard update pipeline check that the current Program and current State or good to go for a + void updatePipeline(); + // Force to reset all the state fields indicated by the 'toBeReset" signature + void resetPipelineState(State::Signature toBeReset); + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncPipelineStateCache(); + void resetPipelineStage(); - BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; + struct PipelineStageState { + PipelinePointer _pipeline; - State::Data _stateCache{ State::DEFAULT }; - State::Signature _stateSignatureCache { 0 }; + GLuint _program { 0 }; + GLint _cameraCorrectionLocation { -1 }; + GLShader* _programShader { nullptr }; + bool _invalidProgram { false }; - GLState* _state { nullptr }; - bool _invalidState { false }; + BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; + BufferView _cameraCorrectionBufferIdentity { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; - PipelineStageState() { - _cameraCorrectionBuffer.edit() = CameraCorrection(); - } - } _pipeline; + State::Data _stateCache{ State::DEFAULT }; + State::Signature _stateSignatureCache { 0 }; - // Synchronize the state cache of this Backend with the actual real state of the GL Context - void syncOutputStateCache(); - void resetOutputStage(); + GLState* _state { nullptr }; + bool _invalidState { false }; - struct OutputStageState { - FramebufferPointer _framebuffer { nullptr }; - GLuint _drawFBO { 0 }; - } _output; + PipelineStageState() { + _cameraCorrectionBuffer.edit() = CameraCorrection(); + _cameraCorrectionBufferIdentity.edit() = CameraCorrection(); + _cameraCorrectionBufferIdentity._buffer->flush(); + } + } _pipeline; - void resetQueryStage(); - struct QueryStageState { - uint32_t _rangeQueryDepth { 0 }; - } _queryStage; + // Backend dependant compilation of the shader + virtual GLShader* compileBackendProgram(const Shader& program); + virtual GLShader* compileBackendShader(const Shader& shader); + virtual std::string getBackendShaderHeader() const; + virtual void makeProgramBindings(ShaderObject& shaderObject); + class ElementResource { + public: + gpu::Element _element; + uint16 _resource; + ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} + }; + ElementResource getFormatFromGLUniform(GLenum gltype); + static const GLint UNUSED_SLOT {-1}; + static bool isUnusedSlot(GLint binding) { return (binding == UNUSED_SLOT); } + virtual int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers); + virtual int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); + virtual int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers) = 0; + virtual int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); + virtual int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); - void resetStages(); - struct TextureManagementStageState { - bool _sparseCapable { false }; - } _textureManagement; - virtual void initTextureManagementStage() {} + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncOutputStateCache(); + void resetOutputStage(); + + struct OutputStageState { + FramebufferPointer _framebuffer { nullptr }; + GLuint _drawFBO { 0 }; + } _output; - typedef void (GLBackend::*CommandCall)(const Batch&, size_t); - static CommandCall _commandCalls[Batch::NUM_COMMANDS]; - friend class GLState; - friend class GLTexture; - }; + void resetQueryStage(); + struct QueryStageState { + uint32_t _rangeQueryDepth { 0 }; + } _queryStage; - } } + void resetStages(); + + struct TextureManagementStageState { + bool _sparseCapable { false }; + } _textureManagement; + virtual void initTextureManagementStage() {} + + typedef void (GLBackend::*CommandCall)(const Batch&, size_t); + static CommandCall _commandCalls[Batch::NUM_COMMANDS]; + friend class GLState; + friend class GLTexture; + friend class GLShader; +}; + +} } #endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp index 057682584d..4145eb6061 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp @@ -73,13 +73,11 @@ void GLBackend::initInput() { if(!_input._defaultVAO) { glGenVertexArrays(1, &_input._defaultVAO); } - qDebug() << "glBindVertexArray(" << _input._defaultVAO << ")"; glBindVertexArray(_input._defaultVAO); (void) CHECK_GL_ERROR(); } void GLBackend::killInput() { - qDebug() << "glBindVertexArray(0)"; glBindVertexArray(0); if(_input._defaultVAO) { glDeleteVertexArrays(1, &_input._defaultVAO); @@ -94,7 +92,6 @@ void GLBackend::syncInputStateCache() { _input._attributeActivation[i] = active; } //_input._defaultVAO - qDebug() << "glBindVertexArray("<<_input._defaultVAO<< ")"; glBindVertexArray(_input._defaultVAO); } @@ -103,7 +100,6 @@ void GLBackend::resetInputStage() { _input._indexBufferType = UINT32; _input._indexBufferOffset = 0; _input._indexBuffer.reset(); - //qDebug() << "GLBackend::resetInputStage glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);"; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); (void) CHECK_GL_ERROR(); @@ -159,49 +155,83 @@ void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } - -// Core 41 doesn't expose the features to really separate the vertex format from the vertex buffers binding -// Core 43 does :) -// FIXME crashing problem with glVertexBindingDivisor / glVertexAttribFormat -// Once resolved, break this up into the GL 4.1 and 4.5 backends -#if 1 || (GPU_INPUT_PROFILE == GPU_CORE_41) -#define NO_SUPPORT_VERTEX_ATTRIB_FORMAT -#else -#define SUPPORT_VERTEX_ATTRIB_FORMAT -#endif - void GLBackend::updateInput() { -#if defined(SUPPORT_VERTEX_ATTRIB_FORMAT) if (_input._invalidFormat) { - InputStageState::ActivationCache newActivation; // Assign the vertex format required if (_input._format) { - for (auto& it : _input._format->getAttributes()) { - const Stream::Attribute& attrib = (it).second; + bool hasColorAttribute{ false }; - GLuint slot = attrib._slot; - GLuint count = attrib._element.getLocationScalarCount(); - uint8_t locationCount = attrib._element.getLocationCount(); - GLenum type = _elementTypeToGL41Type[attrib._element.getType()]; - GLuint offset = attrib._offset;; - GLboolean isNormalized = attrib._element.isNormalized(); + _input._attribBindingBuffers.reset(); - GLenum perLocationSize = attrib._element.getLocationSize(); + const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); + auto& inputChannels = _input._format->getChannels(); + for (auto& channelIt : inputChannels) { + auto bufferChannelNum = (channelIt).first; + const Stream::Format::ChannelMap::value_type::second_type& channel = (channelIt).second; + _input._attribBindingBuffers.set(bufferChannelNum); - for (size_t locNum = 0; locNum < locationCount; ++locNum) { - newActivation.set(slot + locNum); - glVertexAttribFormat(slot + locNum, count, type, isNormalized, offset + locNum * perLocationSize); - glVertexAttribBinding(slot + locNum, attrib._channel); + GLuint frequency = 0; + for (unsigned int i = 0; i < channel._slots.size(); i++) { + const Stream::Attribute& attrib = attributes.at(channel._slots[i]); + + GLuint slot = attrib._slot; + GLuint count = attrib._element.getLocationScalarCount(); + uint8_t locationCount = attrib._element.getLocationCount(); + GLenum type = gl::ELEMENT_TYPE_TO_GL[attrib._element.getType()]; + + GLuint offset = (GLuint)attrib._offset;; + GLboolean isNormalized = attrib._element.isNormalized(); + + GLenum perLocationSize = attrib._element.getLocationSize(); + + hasColorAttribute = hasColorAttribute || (slot == Stream::COLOR); + + for (GLuint locNum = 0; locNum < locationCount; ++locNum) { + GLuint attriNum = (GLuint)(slot + locNum); + newActivation.set(attriNum); + if (!_input._attributeActivation[attriNum]) { + _input._attributeActivation.set(attriNum); + glEnableVertexAttribArray(attriNum); + } + if (attrib._element.isInteger()) { + glVertexAttribIFormat(attriNum, count, type, offset + locNum * perLocationSize); + } else { + glVertexAttribFormat(attriNum, count, type, isNormalized, offset + locNum * perLocationSize); + } + glVertexAttribBinding(attriNum, attrib._channel); + } + + if (i == 0) { + frequency = attrib._frequency; + } else { + assert(frequency == attrib._frequency); + } + + + (void)CHECK_GL_ERROR(); } - glVertexBindingDivisor(attrib._channel, attrib._frequency); +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glVertexBindingDivisor(bufferChannelNum, frequency * (isStereo() ? 2 : 1)); +#else + glVertexBindingDivisor(bufferChannelNum, frequency); +#endif } - (void)CHECK_GL_ERROR(); + + if (_input._hadColorAttribute && !hasColorAttribute) { + // The previous input stage had a color attribute but this one doesn't so reset + // color to pure white. + const auto white = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + glVertexAttrib4fv(Stream::COLOR, &white.r); + _input._colorAttribute = white; + } + _input._hadColorAttribute = hasColorAttribute; } // Manage Activation what was and what is expected now - for (size_t i = 0; i < newActivation.size(); i++) { + // This should only disable VertexAttribs since the one needed by the vertex format (if it exists) have been enabled above + for (GLuint i = 0; i < (GLuint)newActivation.size(); i++) { bool newState = newActivation[i]; if (newState != _input._attributeActivation[i]) { if (newState) { @@ -219,120 +249,18 @@ void GLBackend::updateInput() { } if (_input._invalidBuffers.any()) { - int numBuffers = _input._buffers.size(); - auto buffer = _input._buffers.data(); auto vbo = _input._bufferVBOs.data(); auto offset = _input._bufferOffsets.data(); auto stride = _input._bufferStrides.data(); - for (int bufferNum = 0; bufferNum < numBuffers; bufferNum++) { - if (_input._invalidBuffers.test(bufferNum)) { - glBindVertexBuffer(bufferNum, (*vbo), (*offset), (*stride)); + for (GLuint buffer = 0; buffer < _input._buffers.size(); buffer++, vbo++, offset++, stride++) { + if (_input._invalidBuffers.test(buffer)) { + glBindVertexBuffer(buffer, (*vbo), (*offset), (GLsizei)(*stride)); } - buffer++; - vbo++; - offset++; - stride++; } + _input._invalidBuffers.reset(); (void)CHECK_GL_ERROR(); } -#else - if (_input._invalidFormat || _input._invalidBuffers.any()) { - - if (_input._invalidFormat) { - InputStageState::ActivationCache newActivation; - - _stats._ISNumFormatChanges++; - - // Check expected activation - if (_input._format) { - for (auto& it : _input._format->getAttributes()) { - const Stream::Attribute& attrib = (it).second; - uint8_t locationCount = attrib._element.getLocationCount(); - for (int i = 0; i < locationCount; ++i) { - newActivation.set(attrib._slot + i); - } - } - } - - // Manage Activation what was and what is expected now - for (unsigned int i = 0; i < newActivation.size(); i++) { - bool newState = newActivation[i]; - if (newState != _input._attributeActivation[i]) { - - if (newState) { - glEnableVertexAttribArray(i); - } else { - glDisableVertexAttribArray(i); - } - (void)CHECK_GL_ERROR(); - - _input._attributeActivation.flip(i); - } - } - } - - // now we need to bind the buffers and assign the attrib pointers - if (_input._format) { - const Buffers& buffers = _input._buffers; - const Offsets& offsets = _input._bufferOffsets; - const Offsets& strides = _input._bufferStrides; - - const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); - auto& inputChannels = _input._format->getChannels(); - _stats._ISNumInputBufferChanges++; - - GLuint boundVBO = 0; - for (auto& channelIt : inputChannels) { - const Stream::Format::ChannelMap::value_type::second_type& channel = (channelIt).second; - if ((channelIt).first < buffers.size()) { - int bufferNum = (channelIt).first; - - if (_input._invalidBuffers.test(bufferNum) || _input._invalidFormat) { - // GLuint vbo = gpu::GL41Backend::getBufferID((*buffers[bufferNum])); - GLuint vbo = _input._bufferVBOs[bufferNum]; - if (boundVBO != vbo) { - //qDebug() << "GLBackend::updateInput glBindBuffer(GL_ARRAY_BUFFER, " << vbo <<")"; - glBindBuffer(GL_ARRAY_BUFFER, vbo); - (void)CHECK_GL_ERROR(); - boundVBO = vbo; - } - _input._invalidBuffers[bufferNum] = false; - - for (unsigned int i = 0; i < channel._slots.size(); i++) { - const Stream::Attribute& attrib = attributes.at(channel._slots[i]); - GLuint slot = attrib._slot; - GLuint count = attrib._element.getLocationScalarCount(); - uint8_t locationCount = attrib._element.getLocationCount(); - GLenum type = gl::ELEMENT_TYPE_TO_GL[attrib._element.getType()]; - // GLenum perLocationStride = strides[bufferNum]; - GLenum perLocationStride = attrib._element.getLocationSize(); - GLuint stride = (GLuint)strides[bufferNum]; - GLuint pointer = (GLuint)(attrib._offset + offsets[bufferNum]); - GLboolean isNormalized = attrib._element.isNormalized(); - - for (size_t locNum = 0; locNum < locationCount; ++locNum) { - glVertexAttribPointer(slot + (GLuint)locNum, count, type, isNormalized, stride, - reinterpret_cast(pointer + perLocationStride * (GLuint)locNum)); -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency * (isStereo() ? 2 : 1)); -#else - glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency); -#endif - } - - // TODO: Support properly the IAttrib version - - (void)CHECK_GL_ERROR(); - } - } - } - } - } - // everything format related should be in sync now - _input._invalidFormat = false; - } -#endif } diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp index 6fddb810ee..45c0de8ed7 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp @@ -48,8 +48,8 @@ void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) { } void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { - if (_stereo._enable && !_pipeline._stateCache.scissorEnable) { - //qWarning("Clear without scissor in stereo mode"); + if (_stereo.isStereo() && !_pipeline._stateCache.scissorEnable) { + qWarning("Clear without scissor in stereo mode"); } uint32 masks = batch._params[paramOffset + 7]._uint; @@ -63,17 +63,21 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { int useScissor = batch._params[paramOffset + 0]._int; GLuint glmask = 0; + bool restoreStencilMask = false; + uint8_t cacheStencilMask = 0xFF; if (masks & Framebuffer::BUFFER_STENCIL) { glClearStencil(stencil); glmask |= GL_STENCIL_BUFFER_BIT; - // TODO: we will probably need to also check the write mask of stencil like we do - // for depth buffer, but as would say a famous Fez owner "We'll cross that bridge when we come to it" + cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront(); + if (cacheStencilMask != 0xFF) { + restoreStencilMask = true; + glStencilMask(0xFF); + } } bool restoreDepthMask = false; if (masks & Framebuffer::BUFFER_DEPTH) { glClearDepthf(depth); - glmask |= GL_DEPTH_BUFFER_BIT; bool cacheDepthMask = _pipeline._stateCache.depthTest.getWriteMask(); @@ -122,6 +126,11 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { glDisable(GL_SCISSOR_TEST); } + // Restore Stencil write mask + if (restoreStencilMask) { + glStencilMask(cacheStencilMask); + } + // Restore write mask meaning turn back off if (restoreDepthMask) { glDepthMask(GL_FALSE); @@ -142,22 +151,19 @@ void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, co auto readFBO = getFramebufferID(srcFramebuffer); if (srcFramebuffer && readFBO) { if ((srcFramebuffer->getWidth() < (region.x + region.z)) || (srcFramebuffer->getHeight() < (region.y + region.w))) { - qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : srcFramebuffer is too small to provide the region queried"; + qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : srcFramebuffer is too small to provide the region queried"; return; } } if ((destImage.width() < region.z) || (destImage.height() < region.w)) { - qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : destImage is too small to receive the region of the framebuffer"; + qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : destImage is too small to receive the region of the framebuffer"; return; } GLenum format = GL_RGBA; - //GLenum format = GL_BGRA; - qDebug() << "TODO: GLBackendOutput.cpp:do_clearFramebuffer GL_BGRA"; - if (destImage.format() != QImage::Format_ARGB32) { - qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer"; + qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer"; return; } diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp index c35966d440..b80be8492f 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp @@ -31,7 +31,6 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { // null pipeline == reset if (!pipeline) { - qDebug() << " null pipeline"; _pipeline._pipeline.reset(); _pipeline._program = 0; @@ -78,7 +77,12 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { if (_pipeline._invalidProgram) { glUseProgram(_pipeline._program); if (_pipeline._cameraCorrectionLocation != -1) { - auto cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); + gl::GLBuffer* cameraCorrectionBuffer = nullptr; + if (_transform._viewCorrectionEnabled) { + cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); + } else { + cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBufferIdentity._buffer); + } glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection)); } (void) CHECK_GL_ERROR(); @@ -150,6 +154,10 @@ void GLBackend::resetUniformStage() { void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 3]._uint; + if (slot >(GLuint)MAX_NUM_UNIFORM_BUFFERS) { + qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot << " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers(); + return; + } BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); GLintptr rangeStart = batch._params[paramOffset + 1]._uint; GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; @@ -192,15 +200,48 @@ void GLBackend::releaseResourceTexture(uint32_t slot) { } void GLBackend::resetResourceStage() { + for (uint32_t i = 0; i < _resource._buffers.size(); i++) { + releaseResourceBuffer(i); + } for (uint32_t i = 0; i < _resource._textures.size(); i++) { releaseResourceTexture(i); } } +void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) { + GLuint slot = batch._params[paramOffset + 1]._uint; + if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) { + qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot << " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers(); + return; + } + + auto resourceBuffer = batch._buffers.get(batch._params[paramOffset + 0]._uint); + + if (!resourceBuffer) { + releaseResourceBuffer(slot); + return; + } + // check cache before thinking + if (_resource._buffers[slot] == resourceBuffer) { + return; + } + + // One more True Buffer bound + _stats._RSNumResourceBufferBounded++; + + // If successful bind then cache it + if (bindResourceBuffer(slot, resourceBuffer)) { + _resource._buffers[slot] = resourceBuffer; + } else { // else clear slot and cache + releaseResourceBuffer(slot); + return; + } +} + void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 1]._uint; if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) { - // "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" + slot + " which doesn't exist. MaxNumResourceTextures = " + getMaxNumResourceTextures()); + qCDebug(gpugllogging) << "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures(); return; } @@ -230,7 +271,7 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { _resource._textures[slot] = resourceTexture; - _stats._RSAmountTextureMemoryBounded += object->size(); + _stats._RSAmountTextureMemoryBounded += (int) object->size(); } else { releaseResourceTexture(slot); diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp index e5126bb3df..43c8f8f465 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp @@ -25,6 +25,7 @@ static bool timeElapsed = false; #endif void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -40,9 +41,11 @@ void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { glquery->_rangeQueryDepth = _queryStage._rangeQueryDepth; (void)CHECK_GL_ERROR(); } +#endif } void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -65,9 +68,11 @@ void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } +#endif } void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); if (glGetQueryObjectui64vEXT == NULL) return; @@ -87,6 +92,7 @@ void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { } (void)CHECK_GL_ERROR(); } +#endif } void GLBackend::resetQueryStage() { diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp new file mode 100644 index 0000000000..fd44ad462f --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp @@ -0,0 +1,560 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 2017/12/28 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLShader.h" +#include + +using namespace gpu; +using namespace gpu::gl; + +// GLSL version +std::string GLBackend::getBackendShaderHeader() const { + return std::string("#version 310 es"); +} + +// Shader domain +static const size_t NUM_SHADER_DOMAINS = 2; + +// GL Shader type enums +// Must match the order of type specified in gpu::Shader::Type +static const std::array SHADER_DOMAINS{ { + GL_VERTEX_SHADER, + GL_FRAGMENT_SHADER, + // GL_GEOMETRY_SHADER, +} }; + +// Domain specific defines +// Must match the order of type specified in gpu::Shader::Type +static const std::array DOMAIN_DEFINES{ { + "#define GPU_VERTEX_SHADER", + "#define GPU_PIXEL_SHADER", + // "#define GPU_GEOMETRY_SHADER", +} }; + +// Stereo specific defines +static const std::string stereoVersion{ +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN" +#endif +#ifdef GPU_STEREO_DRAWCALL_DOUBLED +#ifdef GPU_STEREO_CAMERA_BUFFER + "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED" +#else + "#define GPU_TRANSFORM_IS_STEREO" +#endif +#endif +}; + +// Versions specific of the shader +static const std::array VERSION_DEFINES { { + "", + stereoVersion +} }; + +GLShader* GLBackend::compileBackendShader(const Shader& shader) { + // Any GLSLprogram ? normally yes... + const std::string& shaderSource = shader.getSource().getCode(); + GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; + GLShader::ShaderObjects shaderObjects; + + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& shaderObject = shaderObjects[version]; + + std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version] + + "\n#extension GL_EXT_texture_buffer : enable" + + "\nprecision lowp float; // check precision 2" + + "\nprecision lowp samplerBuffer;" + + "\nprecision lowp sampler2DShadow;"; + std::string error; + +#ifdef SEPARATE_PROGRAM + bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram, error); +#else + bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error); +#endif + if (!result) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str(); + return nullptr; + } + } + + // So far so good, the shader is created successfully + GLShader* object = new GLShader(this->shared_from_this()); + object->_shaderObjects = shaderObjects; + + return object; +} + +GLShader* GLBackend::compileBackendProgram(const Shader& program) { + if (!program.isProgram()) { + return nullptr; + } + + GLShader::ShaderObjects programObjects; + + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& programObject = programObjects[version]; + + // Let's go through every shaders and make sure they are ready to go + std::vector shaderGLObjects; + for (auto subShader : program.getShaders()) { + auto object = GLShader::sync((*this), *subShader); + if (object) { + shaderGLObjects.push_back(object->_shaderObjects[version].glshader); + } else { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; + return nullptr; + } + } + + std::string error; + GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error); + if (glprogram == 0) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << error.c_str(); + return nullptr; + } + + programObject.glprogram = glprogram; + + makeProgramBindings(programObject); + } + + // So far so good, the program versions have all been created successfully + GLShader* object = new GLShader(this->shared_from_this()); + object->_shaderObjects = programObjects; + + return object; +} + +GLBackend::ElementResource GLBackend::getFormatFromGLUniform(GLenum gltype) { + switch (gltype) { + case GL_FLOAT: + return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC2: + return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC3: + return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC4: + return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + + case GL_INT: + return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC2: + return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC3: + return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC4: + return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); + + case GL_UNSIGNED_INT: + return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC2: + return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC3: + return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC4: + return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); + + case GL_BOOL: + return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC2: + return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC3: + return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC4: + return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); + + case GL_FLOAT_MAT2: + return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT3: + return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT4: + return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + + //{GL_FLOAT_MAT2x3 mat2x3}, + //{GL_FLOAT_MAT2x4 mat2x4}, + //{GL_FLOAT_MAT3x2 mat3x2}, + //{GL_FLOAT_MAT3x4 mat3x4}, + //{GL_FLOAT_MAT4x2 mat4x2}, + //{GL_FLOAT_MAT4x3 mat4x3}, + //{GL_DOUBLE_MAT2 dmat2}, + //{GL_DOUBLE_MAT3 dmat3}, + //{GL_DOUBLE_MAT4 dmat4}, + //{GL_DOUBLE_MAT2x3 dmat2x3}, + //{GL_DOUBLE_MAT2x4 dmat2x4}, + //{GL_DOUBLE_MAT3x2 dmat3x2}, + //{GL_DOUBLE_MAT3x4 dmat3x4}, + //{GL_DOUBLE_MAT4x2 dmat4x2}, + //{GL_DOUBLE_MAT4x3 dmat4x3}, + + case GL_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); + + case GL_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); + case GL_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + + case GL_SAMPLER_2D_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); + case GL_SAMPLER_CUBE_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); + + case GL_SAMPLER_2D_ARRAY_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); + + // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, + // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, + + case GL_SAMPLER_BUFFER: + return ElementResource(Element(SCALAR, gpu::FLOAT, RESOURCE_BUFFER), Resource::BUFFER); + + // {GL_SAMPLER_2D_RECT sampler2DRect}, + // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, + + case GL_INT_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); + case GL_INT_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_INT_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + + // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, + // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, + + case GL_UNSIGNED_INT_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); + case GL_UNSIGNED_INT_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + //{GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, + //{GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, + + //{GL_IMAGE_1D image1D}, + //{GL_IMAGE_2D image2D}, + //{GL_IMAGE_3D image3D}, + //{GL_IMAGE_2D_RECT image2DRect}, + //{GL_IMAGE_CUBE imageCube}, + //{GL_IMAGE_BUFFER imageBuffer}, + //{GL_IMAGE_1D_ARRAY image1DArray}, + //{GL_IMAGE_2D_ARRAY image2DArray}, + //{GL_IMAGE_2D_MULTISAMPLE image2DMS}, + //{GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, + //{GL_INT_IMAGE_1D iimage1D}, + //{GL_INT_IMAGE_2D iimage2D}, + //{GL_INT_IMAGE_3D iimage3D}, + //{GL_INT_IMAGE_2D_RECT iimage2DRect}, + //{GL_INT_IMAGE_CUBE iimageCube}, + //{GL_INT_IMAGE_BUFFER iimageBuffer}, + //{GL_INT_IMAGE_1D_ARRAY iimage1DArray}, + //{GL_INT_IMAGE_2D_ARRAY iimage2DArray}, + //{GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, + //{GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, + //{GL_UNSIGNED_INT_IMAGE_1D uimage1D}, + //{GL_UNSIGNED_INT_IMAGE_2D uimage2D}, + //{GL_UNSIGNED_INT_IMAGE_3D uimage3D}, + //{GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, + //{GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot + + //{GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, + //{GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, + //{GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, + //{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, + //{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, + //{GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} +#if 0 + case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); + case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); + case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); +#endif + + default: + return ElementResource(Element(), Resource::BUFFER); + } +}; + +int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { + GLint uniformsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); + + for (int i = 0; i < uniformsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + GLint location = glGetUniformLocation(glprogram, name); + const GLint INVALID_UNIFORM_LOCATION = -1; + + // Try to make sense of the gltype + auto elementResource = getFormatFromGLUniform(type); + + // The uniform as a standard var type + if (location != INVALID_UNIFORM_LOCATION) { + // Let's make sure the name doesn't contains an array element + std::string sname(name); + auto foundBracket = sname.find_first_of('['); + if (foundBracket != std::string::npos) { + // std::string arrayname = sname.substr(0, foundBracket); + + if (sname[foundBracket + 1] == '0') { + sname = sname.substr(0, foundBracket); + } else { + // skip this uniform since it's not the first element of an array + continue; + } + } + + if (elementResource._resource == Resource::BUFFER) { + uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); + } else { + // For texture/Sampler, the location is the actual binding value + GLint binding = -1; + glGetUniformiv(glprogram, location, &binding); + + auto requestedBinding = slotBindings.find(std::string(sname)); + if (requestedBinding != slotBindings.end()) { + if (binding != (*requestedBinding)._location) { + binding = (*requestedBinding)._location; + for (auto i = 0; i < size; i++) { + // If we are working with an array of textures, reserve for each elemet + glProgramUniform1i(glprogram, location + i, binding + i); + } + } + } + + textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + } + } + } + + return uniformsCount; +} + +int GLBackend::makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { + GLint buffersCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); + + // fast exit + if (buffersCount == 0) { + return 0; + } + + GLint maxNumUniformBufferSlots = 0; + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); + std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); + + struct UniformBlockInfo { + using Vector = std::vector; + const GLuint index{ 0 }; + const std::string name; + GLint binding{ -1 }; + GLint size{ 0 }; + + static std::string getName(GLuint glprogram, GLuint i) { + static const GLint NAME_LENGTH = 256; + GLint length = 0; + GLchar nameBuffer[NAME_LENGTH]; + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); + glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); + return std::string(nameBuffer); + } + + UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); + } + }; + + UniformBlockInfo::Vector uniformBlocks; + uniformBlocks.reserve(buffersCount); + for (int i = 0; i < buffersCount; i++) { + uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); + } + + for (auto& info : uniformBlocks) { + auto requestedBinding = slotBindings.find(info.name); + if (requestedBinding != slotBindings.end()) { + info.binding = (*requestedBinding)._location; + glUniformBlockBinding(glprogram, info.index, info.binding); + uniformBufferSlotMap[info.binding] = info.index; + } + } + + for (auto& info : uniformBlocks) { + if (slotBindings.count(info.name)) { + continue; + } + + // If the binding is 0, or the binding maps to an already used binding + if (info.binding == 0 || !isUnusedSlot(uniformBufferSlotMap[info.binding])) { + // If no binding was assigned then just do it finding a free slot + auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), GLBackend::isUnusedSlot); + if (slotIt != uniformBufferSlotMap.end()) { + info.binding = slotIt - uniformBufferSlotMap.begin(); + glUniformBlockBinding(glprogram, info.index, info.binding); + } else { + // This should neve happen, an active ubo cannot find an available slot among the max available?! + info.binding = -1; + } + } + + uniformBufferSlotMap[info.binding] = info.index; + } + + for (auto& info : uniformBlocks) { + static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); + buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); + } + return buffersCount; +} + +int GLBackend::makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { + GLint inputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + GLint binding = glGetAttribLocation(glprogram, name); + + auto elementResource = getFormatFromGLUniform(type); + inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); + } + + return inputsCount; +} + +int GLBackend::makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { + /* GLint outputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + auto element = getFormatFromGLUniform(type); + outputs.insert(Shader::Slot(name, i, element)); + } + */ + return 0; //inputsCount; +} + +void GLBackend::makeProgramBindings(ShaderObject& shaderObject) { + if (!shaderObject.glprogram) { + return; + } + GLuint glprogram = shaderObject.glprogram; + GLint loc = -1; + + //Check for gpu specific attribute slotBindings + loc = glGetAttribLocation(glprogram, "inPosition"); + if (loc >= 0 && loc != gpu::Stream::POSITION) { + glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); + } + + loc = glGetAttribLocation(glprogram, "inNormal"); + if (loc >= 0 && loc != gpu::Stream::NORMAL) { + glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); + } + + loc = glGetAttribLocation(glprogram, "inColor"); + if (loc >= 0 && loc != gpu::Stream::COLOR) { + glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); + } + + loc = glGetAttribLocation(glprogram, "inTexCoord0"); + if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); + } + + loc = glGetAttribLocation(glprogram, "inTangent"); + if (loc >= 0 && loc != gpu::Stream::TANGENT) { + glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); + } + + char attribName[] = "inTexCoordn"; + for (auto i = 0; i < 4; i++) { + auto streamId = gpu::Stream::TEXCOORD1 + i; + + attribName[strlen(attribName) - 1] = '1' + i; + loc = glGetAttribLocation(glprogram, attribName); + if (loc >= 0 && loc != streamId) { + glBindAttribLocation(glprogram, streamId, attribName); + } + } + + loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); + if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); + } + + loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); + if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); + } + + loc = glGetAttribLocation(glprogram, "_drawCallInfo"); + if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { + glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); + } + + // Link again to take into account the assigned attrib location + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + if (!linked) { + qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; + } +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp index 0a7db78b11..4a5c772b8b 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp @@ -11,6 +11,8 @@ #include "GLBackend.h" #include "GLState.h" +#include + using namespace gpu; using namespace gpu::gl; @@ -96,13 +98,11 @@ void GLBackend::do_setStateFrontFaceClockwise(bool isClockwise) { void GLBackend::do_setStateDepthClampEnable(bool enable) { if (_pipeline._stateCache.depthClampEnable != enable) { - if (enable) { - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthClampEnable GL_DEPTH_CLAMP"; - //glEnable(GL_DEPTH_CLAMP); - } else { - //glDisable(GL_DEPTH_CLAMP); - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthClampEnable GL_DEPTH_CLAMP"; - } + //if (enable) { + // glEnable(GL_DEPTH_CLAMP); + //} else { + // glDisable(GL_DEPTH_CLAMP); + //} (void)CHECK_GL_ERROR(); _pipeline._stateCache.depthClampEnable = enable; @@ -124,14 +124,14 @@ void GLBackend::do_setStateScissorEnable(bool enable) { void GLBackend::do_setStateMultisampleEnable(bool enable) { if (_pipeline._stateCache.multisampleEnable != enable) { +#if !defined(USE_GLES) if (enable) { - //glEnable(GL_MULTISAMPLE); - qDebug() << "TODO: GLBackendState.cpp:do_setStateMultisampleEnable GL_MULTISAMPLE"; + glEnable(GL_MULTISAMPLE); } else { - //glDisable(GL_MULTISAMPLE); - qDebug() << "TODO: GLBackendState.cpp:do_setStateMultisampleEnable GL_MULTISAMPLE"; + glDisable(GL_MULTISAMPLE); } (void)CHECK_GL_ERROR(); +#endif _pipeline._stateCache.multisampleEnable = enable; } @@ -139,14 +139,14 @@ void GLBackend::do_setStateMultisampleEnable(bool enable) { void GLBackend::do_setStateAntialiasedLineEnable(bool enable) { if (_pipeline._stateCache.antialisedLineEnable != enable) { +#if !defined(USE_GLES) if (enable) { - //glEnable(GL_LINE_SMOOTH); - qDebug() << "TODO: GLBackendState.cpp:do_setStateAntialiasedLineEnable GL_LINE_SMOOTH"; + glEnable(GL_LINE_SMOOTH); } else { - //glDisable(GL_LINE_SMOOTH); - qDebug() << "TODO: GLBackendState.cpp:do_setStateAntialiasedLineEnable GL_LINE_SMOOTH"; + glDisable(GL_LINE_SMOOTH); } (void)CHECK_GL_ERROR(); +#endif _pipeline._stateCache.antialisedLineEnable = enable; } @@ -156,17 +156,17 @@ void GLBackend::do_setStateDepthBias(Vec2 bias) { if ((bias.x != _pipeline._stateCache.depthBias) || (bias.y != _pipeline._stateCache.depthBiasSlopeScale)) { if ((bias.x != 0.0f) || (bias.y != 0.0f)) { glEnable(GL_POLYGON_OFFSET_FILL); - //glEnable(GL_POLYGON_OFFSET_LINE); - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_LINE"; - //glEnable(GL_POLYGON_OFFSET_POINT); - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_POINT"; +#if !defined(USE_GLES) + glEnable(GL_POLYGON_OFFSET_LINE); + glEnable(GL_POLYGON_OFFSET_POINT); +#endif glPolygonOffset(bias.x, bias.y); } else { glDisable(GL_POLYGON_OFFSET_FILL); - //glDisable(GL_POLYGON_OFFSET_LINE); - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_LINE"; - //glDisable(GL_POLYGON_OFFSET_POINT); - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_POINT"; +#if !defined(USE_GLES) + glDisable(GL_POLYGON_OFFSET_LINE); + glDisable(GL_POLYGON_OFFSET_POINT); +#endif } (void)CHECK_GL_ERROR(); @@ -190,7 +190,7 @@ void GLBackend::do_setStateDepthTest(State::DepthTest test) { glDepthFunc(COMPARISON_TO_GL[test.getFunction()]); } if (CHECK_GL_ERROR()) { - qDebug() << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled") + qCDebug(gpulogging) << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled") << "Mask=" << (test.getWriteMask() ? "Write" : "no Write") << "Func=" << test.getFunction() << "Raw=" << test.getRaw(); diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp index 4be7682a4f..ace33766ce 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp @@ -15,13 +15,56 @@ using namespace gpu; using namespace gpu::gl; -bool GLBackend::isTextureReady(const TexturePointer& texture) { - // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(texture, true); - qDebug() << "GLBackendTexture isTextureReady syncGPUObject"; - return object && object->isReady(); + +GLuint GLBackend::getTextureID(const TexturePointer& texture) { + GLTexture* object = syncGPUObject(texture); + + if (!object) { + return 0; + } + + return object->_id; } +GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { + const Texture& texture = *texturePointer; + // Special case external textures + if (TextureUsageType::EXTERNAL == texture.getUsageType()) { + Texture::ExternalUpdates updates = texture.getUpdates(); + if (!updates.empty()) { + Texture::ExternalRecycler recycler = texture.getExternalRecycler(); + Q_ASSERT(recycler); + // Discard any superfluous updates + while (updates.size() > 1) { + const auto& update = updates.front(); + // Superfluous updates will never have been read, but we want to ensure the previous + // writes to them are complete before they're written again, so return them with the + // same fences they arrived with. This can happen on any thread because no GL context + // work is involved + recycler(update.first, update.second); + updates.pop_front(); + } + + // The last texture remaining is the one we'll use to create the GLTexture + const auto& update = updates.front(); + // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence + if (update.second) { + GLsync fence = static_cast(update.second); + glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(fence); + } + + // Create the new texture object (replaces any previous texture object) + new GLExternalTexture(shared_from_this(), texture, update.first); + } + + // Return the texture object (if any) associated with the texture, without extensive logic + // (external textures are + return Backend::getGPUObject(texture); + } + + return nullptr; +} void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); @@ -30,8 +73,7 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { } // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(resourceTexture, false); - qDebug() << "GLBackendTexture do_generateTextureMips syncGPUObject"; + GLTexture* object = syncGPUObject(resourceTexture); if (!object) { return; } diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp index 3068e24dac..f286a5cca9 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp @@ -15,7 +15,6 @@ using namespace gpu::gl; // Transform Stage void GLBackend::do_setModelTransform(const Batch& batch, size_t paramOffset) { - qDebug() << "do_setModelTransform"; } void GLBackend::do_setViewTransform(const Batch& batch, size_t paramOffset) { @@ -38,7 +37,7 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) glViewport(vp.x, vp.y, vp.z, vp.w); // Where we assign the GL viewport - if (_stereo._enable) { + if (_stereo.isStereo()) { vp.z /= 2; if (_stereo._pass) { vp.x += vp.z; @@ -103,7 +102,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo if (_invalidView) { // Apply the correction - if (_viewIsCamera && _correction.correction != glm::mat4()) { + if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) { // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? Transform result; _view.mult(result, _view, _correction.correction); @@ -120,7 +119,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo size_t offset = _cameraUboSize * _cameras.size(); _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); - if (stereo._enable) { + if (stereo.isStereo()) { #ifdef GPU_STEREO_CAMERA_BUFFER _cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view))); #else @@ -152,7 +151,7 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta #ifdef GPU_STEREO_CAMERA_BUFFER bindCurrentCamera(0); #else - if (!stereo._enable) { + if (!stereo.isStereo()) { bindCurrentCamera(0); } #endif @@ -162,51 +161,11 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta void GLBackend::TransformStageState::bindCurrentCamera(int eye) const { if (_currentCameraOffset != INVALID_OFFSET) { - //qDebug() << "GLBackend::TransformStageState::bindCurrentCamera"; glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _cameraBuffer, _currentCameraOffset + eye * _cameraUboSize, sizeof(CameraBufferElement)); } } -void GLBackend::updateTransform(const Batch& batch) { - _transform.update(_commandIndex, _stereo); - - auto& drawCallInfoBuffer = batch.getDrawCallInfoBuffer(); - if (batch._currentNamedCall.empty()) { - (void)CHECK_GL_ERROR(); - auto& drawCallInfo = drawCallInfoBuffer[_currentDraw]; - glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is disabled - (void)CHECK_GL_ERROR(); - GLint current_vao, current_vbo, maxVertexAtribs; - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, ¤t_vao); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤t_vbo); - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAtribs); - glVertexAttribI4i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused, 0, 0); - - //int values[] = {drawCallInfo.index, drawCallInfo.unused}; - //glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_INT, 0, (const GLvoid *) values); - - /* - //glDisableVertexAttribArray currentvao 1 current vbo 0 - GL_INVALID_OPERATION is generated - a non-zero vertex array object is bound, - zero is bound to the GL_ARRAY_BUFFER buffer object binding point and - the pointer argument is not NULL. TRUE - */ - //qDebug() << "GLBackend::updateTransform glVertexAttribIPointer done"; - (void)CHECK_GL_ERROR(); - - } else { - //qDebug() << "GLBackend::updateTransform else"; - glEnableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is enabled - glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer); - glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_UNSIGNED_SHORT, 0, - _transform._drawCallInfoOffsets[batch._currentNamedCall]); - glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, 1); - } - - (void)CHECK_GL_ERROR(); -} - void GLBackend::resetTransformStage() { - + glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); + _transform._enabledDrawcallInfoBuffer = false; } diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp index 150bb2be70..0bca9e86d9 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp @@ -21,28 +21,27 @@ GLFramebuffer::~GLFramebuffer() { } } -bool GLFramebuffer::checkStatus(GLenum target) const { - bool result = false; +bool GLFramebuffer::checkStatus() const { switch (_status) { case GL_FRAMEBUFFER_COMPLETE: // Success ! - result = true; - break; + return true; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; + qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; break; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT."; + qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT."; break; -/* TODO: case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER."; - break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER."; - break; */ + //case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + // qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER."; + // break; + //case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + // qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER."; + // break; case GL_FRAMEBUFFER_UNSUPPORTED: - qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; + qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; break; } - return result; + return false; } diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h index a2fd0999f3..5a388e1965 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h +++ b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h @@ -64,7 +64,7 @@ public: protected: GLenum _status { GL_FRAMEBUFFER_COMPLETE }; virtual void update() = 0; - bool checkStatus(GLenum target) const; + bool checkStatus() const; GLFramebuffer(const std::weak_ptr& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {} ~GLFramebuffer(); diff --git a/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp index 09c09de353..ebf1a55232 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp @@ -51,10 +51,7 @@ GLPipeline* GLPipeline::sync(GLBackend& backend, const Pipeline& pipeline) { // Special case for view correction matrices, any pipeline that declares the correction buffer // uniform will automatically have it provided without any client code necessary. // Required for stable lighting in the HMD. - //CLIMAX_MERGE_START - //getbuffers() doesnt exist anymore.. use get uniformbuffers()? object->_cameraCorrection = shader->getUniformBuffers().findLocation("cameraCorrectionBuffer"); - //CLIMAX_MERGE_END object->_program = programObject; object->_state = stateObject; diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp index b728010470..7ed9121978 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp @@ -30,126 +30,6 @@ GLShader::~GLShader() { } } -// GLSL version -static const std::string glslVersion { - "#version 310 es" -}; - -// Shader domain -static const size_t NUM_SHADER_DOMAINS = 3; - -// GL Shader type enums -// Must match the order of type specified in gpu::Shader::Type -static const std::array SHADER_DOMAINS { { - GL_VERTEX_SHADER, - GL_FRAGMENT_SHADER, - //GL_GEOMETRY_SHADER, -} }; - -// Domain specific defines -// Must match the order of type specified in gpu::Shader::Type -static const std::array DOMAIN_DEFINES { { - "#define GPU_VERTEX_SHADER", - "#define GPU_PIXEL_SHADER", - "#define GPU_GEOMETRY_SHADER", -} }; - -// Stereo specific defines -static const std::string stereoVersion { -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN" -#endif -#ifdef GPU_STEREO_DRAWCALL_DOUBLED -#ifdef GPU_STEREO_CAMERA_BUFFER - "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED" -#else - "#define GPU_TRANSFORM_IS_STEREO" -#endif -#endif -}; - -// Versions specific of the shader -static const std::array VERSION_DEFINES { { - "", - stereoVersion -} }; - -GLShader* compileBackendShader(GLBackend& backend, const Shader& shader) { - // Any GLSLprogram ? normally yes... - const std::string& shaderSource = shader.getSource().getCode(); - GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; - GLShader::ShaderObjects shaderObjects; - - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& shaderObject = shaderObjects[version]; - std::string shaderDefines = glslVersion + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version] - + "\n" + "#extension GL_EXT_texture_buffer : enable" - + "\nprecision lowp float; // check precision 2" - + "\nprecision lowp samplerBuffer;" - + "\nprecision lowp sampler2DShadow;"; - // TODO Delete bool result = compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram); - std::string error; - - -#ifdef SEPARATE_PROGRAM - bool result = ::gl::compileShader(shaderDomain, shaderSource.c_str(), shaderDefines.c_str(), shaderObject.glshader, shaderObject.glprogram, error); -#else - bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error); -#endif - if (!result) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str(); - return nullptr; - } - } - - // So far so good, the shader is created successfully - GLShader* object = new GLShader(backend.shared_from_this()); - object->_shaderObjects = shaderObjects; - - return object; -} - -GLShader* compileBackendProgram(GLBackend& backend, const Shader& program) { - if (!program.isProgram()) { - return nullptr; - } - - GLShader::ShaderObjects programObjects; - - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& programObject = programObjects[version]; - - // Let's go through every shaders and make sure they are ready to go - std::vector< GLuint > shaderGLObjects; - for (auto subShader : program.getShaders()) { - auto object = GLShader::sync(backend, *subShader); - if (object) { - shaderGLObjects.push_back(object->_shaderObjects[version].glshader); - } else { - qCDebug(gpugllogging) << "GLShader::compileBackendProgram - One of the shaders of the program is not compiled?"; - return nullptr; - } - } - - std::string error; - GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error); - if (glprogram == 0) { - qCWarning(gpugllogging) << error.c_str(); - return nullptr; - } - - programObject.glprogram = glprogram; - - makeProgramBindings(programObject); - } - - // So far so good, the program versions have all been created successfully - GLShader* object = new GLShader(backend.shared_from_this()); - object->_shaderObjects = programObjects; - - return object; -} - GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { GLShader* object = Backend::getGPUObject(shader); @@ -159,13 +39,13 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { } // need to have a gpu object? if (shader.isProgram()) { - GLShader* tempObject = compileBackendProgram(backend, shader); + GLShader* tempObject = backend.compileBackendProgram(shader); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); } } else if (shader.isDomain()) { - GLShader* tempObject = compileBackendShader(backend, shader); + GLShader* tempObject = backend.compileBackendShader(shader); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); @@ -189,21 +69,21 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin auto& shaderObject = object->_shaderObjects[version]; if (shaderObject.glprogram) { Shader::SlotSet buffers; - makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers); + backend.makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers); Shader::SlotSet uniforms; Shader::SlotSet textures; Shader::SlotSet samplers; - makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); + backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); Shader::SlotSet resourceBuffers; - makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); + backend.makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); Shader::SlotSet inputs; - makeInputSlots(shaderObject.glprogram, slotBindings, inputs); + backend.makeInputSlots(shaderObject.glprogram, slotBindings, inputs); Shader::SlotSet outputs; - makeOutputSlots(shaderObject.glprogram, slotBindings, outputs); + backend.makeOutputSlots(shaderObject.glprogram, slotBindings, outputs); // Define the public slots only from the default version if (version == 0) { @@ -222,3 +102,5 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin return true; } + + diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.h b/libraries/gpu-gles/src/gpu/gl/GLShader.h index e03b487a60..dcf2dc330d 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.h @@ -12,6 +12,13 @@ namespace gpu { namespace gl { +struct ShaderObject { + GLuint glshader { 0 }; + GLuint glprogram { 0 }; + GLint transformCameraSlot { -1 }; + GLint transformObjectSlot { -1 }; +}; + class GLShader : public GPUObject { public: static GLShader* sync(GLBackend& backend, const Shader& shader); diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.cpp b/libraries/gpu-gles/src/gpu/gl/GLShared.cpp index 5d340889a6..f818a221b2 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShared.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLShared.cpp @@ -17,6 +17,7 @@ Q_LOGGING_CATEGORY(gpugllogging, "hifi.gpu.gl") Q_LOGGING_CATEGORY(trace_render_gpu_gl, "trace.render.gpu.gl") +Q_LOGGING_CATEGORY(trace_render_gpu_gl_detail, "trace.render.gpu.gl.detail") namespace gpu { namespace gl { @@ -27,22 +28,25 @@ bool checkGLError(const char* name) { } else { switch (error) { case GL_INVALID_ENUM: - qCDebug(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; + qCWarning(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; break; case GL_INVALID_VALUE: - qCDebug(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; + qCWarning(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; break; case GL_INVALID_OPERATION: - qCDebug(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; + qCWarning(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; break; case GL_INVALID_FRAMEBUFFER_OPERATION: - qCDebug(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; + qCWarning(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; break; case GL_OUT_OF_MEMORY: - qCDebug(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; + qCWarning(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; break; - default: - qCDebug(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error; + case GL_STACK_UNDERFLOW: + qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to underflow."; + break; + case GL_STACK_OVERFLOW: + qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to overflow."; break; } return true; @@ -50,12 +54,8 @@ bool checkGLError(const char* name) { } bool checkGLErrorDebug(const char* name) { -#ifdef DEBUG + // FIXME, disable in debug mode when near release return checkGLError(name); -#else - Q_UNUSED(name); - return false; -#endif } gpu::Size getFreeDedicatedMemory() { @@ -86,43 +86,6 @@ gpu::Size getFreeDedicatedMemory() { return result; } -gpu::Size getDedicatedMemory() { - static Size dedicatedMemory { 0 }; - static std::once_flag once; - std::call_once(once, [&] { - if (!dedicatedMemory) { - GLint atiGpuMemory[4]; - // not really total memory, but close enough if called early enough in the application lifecycle - //glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, atiGpuMemory); - qDebug() << "TODO: GLShared.cpp.cpp:initInput GL_TEXTURE_FREE_MEMORY_ATI"; - if (GL_NO_ERROR == glGetError()) { - dedicatedMemory = KB_TO_BYTES(atiGpuMemory[0]); - } - } - - if (!dedicatedMemory) { - GLint nvGpuMemory { 0 }; - qDebug() << "TODO: GLShared.cpp.cpp:initInput GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX"; - //glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &nvGpuMemory); - if (GL_NO_ERROR == glGetError()) { - dedicatedMemory = KB_TO_BYTES(nvGpuMemory); - } - } - - if (!dedicatedMemory) { - auto gpuIdent = GPUIdent::getInstance(); - if (gpuIdent && gpuIdent->isValid()) { - dedicatedMemory = MB_TO_BYTES(gpuIdent->getMemory()); - } - } - }); - - return dedicatedMemory; -} - - - - ComparisonFunction comparisonFuncFromGL(GLenum func) { if (func == GL_NEVER) { return NEVER; @@ -219,21 +182,17 @@ State::BlendArg blendArgFromGL(GLenum blendArg) { void getCurrentGLState(State::Data& state) { { - GLint modes[2]; + //GLint modes[2]; //glGetIntegerv(GL_POLYGON_MODE, modes); - qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_POLYGON_MODE"; - qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_FILL"; - qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_LINE"; - - if (modes[0] == 0 /*GL_FILL*/) { - state.fillMode = State::FILL_FACE; - } else { - if (modes[0] == 0 /*GL_LINE*/) { - state.fillMode = State::FILL_LINE; - } else { - state.fillMode = State::FILL_POINT; - } - } + //if (modes[0] == GL_FILL) { + // state.fillMode = State::FILL_FACE; + //} else { + // if (modes[0] == GL_LINE) { + // state.fillMode = State::FILL_LINE; + // } else { + // state.fillMode = State::FILL_POINT; + // } + //} } { if (glIsEnabled(GL_CULL_FACE)) { @@ -248,15 +207,10 @@ void getCurrentGLState(State::Data& state) { GLint winding; glGetIntegerv(GL_FRONT_FACE, &winding); state.frontFaceClockwise = (winding == GL_CW); - //state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP); - qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_DEPTH_CLAMP"; + state.depthClampEnable = false; //glIsEnabled(GL_DEPTH_CLAMP_EXT); state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST); - //state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE); - qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_MULTISAMPLE"; - - //state.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH); - qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_LINE_SMOOTH"; - + state.multisampleEnable = false; //glIsEnabled(GL_MULTISAMPLE_EXT); + state.antialisedLineEnable = false; //glIsEnabled(GL_LINE_SMOOTH); } { if (glIsEnabled(GL_POLYGON_OFFSET_FILL)) { @@ -354,504 +308,6 @@ void getCurrentGLState(State::Data& state) { } -class ElementResource { -public: - gpu::Element _element; - uint16 _resource; - - ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} -}; - -ElementResource getFormatFromGLUniform(GLenum gltype) { - switch (gltype) { - case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - /* - case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - */ - case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); - - case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); -#if defined(Q_OS_WIN) - case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); -#endif - - case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); - - - case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - - /* {GL_FLOAT_MAT2x3 mat2x3}, - {GL_FLOAT_MAT2x4 mat2x4}, - {GL_FLOAT_MAT3x2 mat3x2}, - {GL_FLOAT_MAT3x4 mat3x4}, - {GL_FLOAT_MAT4x2 mat4x2}, - {GL_FLOAT_MAT4x3 mat4x3}, - {GL_DOUBLE_MAT2 dmat2}, - {GL_DOUBLE_MAT3 dmat3}, - {GL_DOUBLE_MAT4 dmat4}, - {GL_DOUBLE_MAT2x3 dmat2x3}, - {GL_DOUBLE_MAT2x4 dmat2x4}, - {GL_DOUBLE_MAT3x2 dmat3x2}, - {GL_DOUBLE_MAT3x4 dmat3x4}, - {GL_DOUBLE_MAT4x2 dmat4x2}, - {GL_DOUBLE_MAT4x3 dmat4x3}, - */ - - //qDebug() << "TODO: GLShared.cpp.cpp:ElementResource GL_SAMPLER_1D"; - //case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); - case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); - - case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); - case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); - -#if defined(Q_OS_WIN) - case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); -#endif - - case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); -#if defined(Q_OS_WIN) - case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); - - case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); -#endif - - // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, - // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, - - // {GL_SAMPLER_BUFFER samplerBuffer}, - // {GL_SAMPLER_2D_RECT sampler2DRect}, - // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, - -#if defined(Q_OS_WIN) - case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); - case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); - case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - - // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, - // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, - - case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); - case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); - case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); -#endif - // {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, - // {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, - /* - {GL_IMAGE_1D image1D}, - {GL_IMAGE_2D image2D}, - {GL_IMAGE_3D image3D}, - {GL_IMAGE_2D_RECT image2DRect}, - {GL_IMAGE_CUBE imageCube}, - {GL_IMAGE_BUFFER imageBuffer}, - {GL_IMAGE_1D_ARRAY image1DArray}, - {GL_IMAGE_2D_ARRAY image2DArray}, - {GL_IMAGE_2D_MULTISAMPLE image2DMS}, - {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, - {GL_INT_IMAGE_1D iimage1D}, - {GL_INT_IMAGE_2D iimage2D}, - {GL_INT_IMAGE_3D iimage3D}, - {GL_INT_IMAGE_2D_RECT iimage2DRect}, - {GL_INT_IMAGE_CUBE iimageCube}, - {GL_INT_IMAGE_BUFFER iimageBuffer}, - {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, - {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, - {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, - {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, - {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, - {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, - {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, - {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, - {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot - - {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, - {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, - {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} - */ - default: - return ElementResource(Element(), Resource::BUFFER); - } - -}; - -int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, - Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { - GLint uniformsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); - - for (int i = 0; i < uniformsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - GLint location = glGetUniformLocation(glprogram, name); - const GLint INVALID_UNIFORM_LOCATION = -1; - - // Try to make sense of the gltype - auto elementResource = getFormatFromGLUniform(type); - - // The uniform as a standard var type - if (location != INVALID_UNIFORM_LOCATION) { - // Let's make sure the name doesn't contains an array element - std::string sname(name); - auto foundBracket = sname.find_first_of('['); - if (foundBracket != std::string::npos) { - // std::string arrayname = sname.substr(0, foundBracket); - - if (sname[foundBracket + 1] == '0') { - sname = sname.substr(0, foundBracket); - } else { - // skip this uniform since it's not the first element of an array - continue; - } - } - - if (elementResource._resource == Resource::BUFFER) { - uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); - } else { - // For texture/Sampler, the location is the actual binding value - GLint binding = -1; - glGetUniformiv(glprogram, location, &binding); - - auto requestedBinding = slotBindings.find(std::string(sname)); - if (requestedBinding != slotBindings.end()) { - if (binding != (*requestedBinding)._location) { - binding = (*requestedBinding)._location; - glProgramUniform1i(glprogram, location, binding); - } - } - - textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); - samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); - } - } - } - - return uniformsCount; -} - -const GLint UNUSED_SLOT = -1; -bool isUnusedSlot(GLint binding) { - return (binding == UNUSED_SLOT); -} - -int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { - GLint buffersCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); - - // fast exit - if (buffersCount == 0) { - return 0; - } - - GLint maxNumUniformBufferSlots = 0; - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); - std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); - - struct UniformBlockInfo { - using Vector = std::vector; - const GLuint index{ 0 }; - const std::string name; - GLint binding{ -1 }; - GLint size{ 0 }; - - static std::string getName(GLuint glprogram, GLuint i) { - static const GLint NAME_LENGTH = 256; - GLint length = 0; - GLchar nameBuffer[NAME_LENGTH]; - glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); - glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); - return std::string(nameBuffer); - } - - UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { - glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); - glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); - } - }; - - UniformBlockInfo::Vector uniformBlocks; - uniformBlocks.reserve(buffersCount); - for (int i = 0; i < buffersCount; i++) { - uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); - } - - for (auto& info : uniformBlocks) { - auto requestedBinding = slotBindings.find(info.name); - if (requestedBinding != slotBindings.end()) { - info.binding = (*requestedBinding)._location; - glUniformBlockBinding(glprogram, info.index, info.binding); - uniformBufferSlotMap[info.binding] = info.index; - } - } - - for (auto& info : uniformBlocks) { - if (slotBindings.count(info.name)) { - continue; - } - - // If the binding is 0, or the binding maps to an already used binding - if (info.binding == 0 || uniformBufferSlotMap[info.binding] != UNUSED_SLOT) { - // If no binding was assigned then just do it finding a free slot - auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot); - if (slotIt != uniformBufferSlotMap.end()) { - info.binding = slotIt - uniformBufferSlotMap.begin(); - glUniformBlockBinding(glprogram, info.index, info.binding); - } else { - // This should neve happen, an active ubo cannot find an available slot among the max available?! - info.binding = -1; - } - } - - uniformBufferSlotMap[info.binding] = info.index; - } - - for (auto& info : uniformBlocks) { - static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); - buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); - } - return buffersCount; -} -//CLIMAX_MERGE_START -//This has been copied over from gl45backendshader.cpp -int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { - GLint buffersCount = 0; - glGetProgramInterfaceiv(glprogram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &buffersCount); - - // fast exit - if (buffersCount == 0) { - return 0; - } - - GLint maxNumResourceBufferSlots = 0; - glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxNumResourceBufferSlots); - std::vector resourceBufferSlotMap(maxNumResourceBufferSlots, -1); - - struct ResourceBlockInfo { - using Vector = std::vector; - const GLuint index{ 0 }; - const std::string name; - GLint binding{ -1 }; - GLint size{ 0 }; - - static std::string getName(GLuint glprogram, GLuint i) { - static const GLint NAME_LENGTH = 256; - GLint length = 0; - GLchar nameBuffer[NAME_LENGTH]; - glGetProgramResourceName(glprogram, GL_SHADER_STORAGE_BLOCK, i, NAME_LENGTH, &length, nameBuffer); - return std::string(nameBuffer); - } - - ResourceBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { - GLenum props[2] = { GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE}; - glGetProgramResourceiv(glprogram, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, nullptr, &binding); - } - }; - - ResourceBlockInfo::Vector resourceBlocks; - resourceBlocks.reserve(buffersCount); - for (int i = 0; i < buffersCount; i++) { - resourceBlocks.push_back(ResourceBlockInfo(glprogram, i)); - } - - for (auto& info : resourceBlocks) { - auto requestedBinding = slotBindings.find(info.name); - if (requestedBinding != slotBindings.end()) { - info.binding = (*requestedBinding)._location; - glUniformBlockBinding(glprogram, info.index, info.binding); - resourceBufferSlotMap[info.binding] = info.index; - } - } - - for (auto& info : resourceBlocks) { - if (slotBindings.count(info.name)) { - continue; - } - - // If the binding is -1, or the binding maps to an already used binding - if (info.binding == -1 || !isUnusedSlot(resourceBufferSlotMap[info.binding])) { - // If no binding was assigned then just do it finding a free slot - auto slotIt = std::find_if(resourceBufferSlotMap.begin(), resourceBufferSlotMap.end(), isUnusedSlot); - if (slotIt != resourceBufferSlotMap.end()) { - info.binding = slotIt - resourceBufferSlotMap.begin(); - glUniformBlockBinding(glprogram, info.index, info.binding); - } else { - // This should never happen, an active ssbo cannot find an available slot among the max available?! - info.binding = -1; - } - } - - resourceBufferSlotMap[info.binding] = info.index; - } - - for (auto& info : resourceBlocks) { - static const Element element(SCALAR, gpu::UINT32, gpu::RESOURCE_BUFFER); - resourceBuffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); - } - return buffersCount; -} -//CLIMAX_MERGE_END - -int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { - GLint inputsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); - - for (int i = 0; i < inputsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - - GLint binding = glGetAttribLocation(glprogram, name); - - auto elementResource = getFormatFromGLUniform(type); - inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); - } - - return inputsCount; -} - -int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { - /* GLint outputsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); - - for (int i = 0; i < inputsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - - auto element = getFormatFromGLUniform(type); - outputs.insert(Shader::Slot(name, i, element)); - } - */ - return 0; //inputsCount; -} - -void makeProgramBindings(ShaderObject& shaderObject) { - if (!shaderObject.glprogram) { - return; - } - GLuint glprogram = shaderObject.glprogram; - GLint loc = -1; - - //Check for gpu specific attribute slotBindings - loc = glGetAttribLocation(glprogram, "inPosition"); - if (loc >= 0 && loc != gpu::Stream::POSITION) { - glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); - } - - loc = glGetAttribLocation(glprogram, "inNormal"); - if (loc >= 0 && loc != gpu::Stream::NORMAL) { - glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); - } - - loc = glGetAttribLocation(glprogram, "inColor"); - if (loc >= 0 && loc != gpu::Stream::COLOR) { - glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); - } - - loc = glGetAttribLocation(glprogram, "inTexCoord0"); - if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { - glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); - } - - loc = glGetAttribLocation(glprogram, "inTangent"); - if (loc >= 0 && loc != gpu::Stream::TANGENT) { - glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); - } - - loc = glGetAttribLocation(glprogram, "inTexCoord1"); - if (loc >= 0 && loc != gpu::Stream::TEXCOORD1) { - glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "inTexCoord1"); - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); - } - - loc = glGetAttribLocation(glprogram, "_drawCallInfo"); - if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { - glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); - } - - // Link again to take into account the assigned attrib location - glLinkProgram(glprogram); - - GLint linked = 0; - glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) { - qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; - } - - // now assign the ubo binding, then DON't relink! - - //Check for gpu specific uniform slotBindings - loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer"); - if (loc >= 0) { - // FIXME GLES - // glShaderStorageBlockBinding(glprogram, loc, TRANSFORM_OBJECT_SLOT); - shaderObject.transformObjectSlot = TRANSFORM_OBJECT_SLOT; - } - - loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); - if (loc >= 0) { - glUniformBlockBinding(glprogram, loc, TRANSFORM_CAMERA_SLOT); - shaderObject.transformCameraSlot = TRANSFORM_CAMERA_SLOT; - } - - (void)CHECK_GL_ERROR(); -} - void serverWait() { auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); assert(fence); diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.h b/libraries/gpu-gles/src/gpu/gl/GLShared.h index 54209b106d..dcce896a37 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gles/src/gpu/gl/GLShared.h @@ -16,18 +16,18 @@ Q_DECLARE_LOGGING_CATEGORY(gpugllogging) Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl) +Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail) + +#define BUFFER_OFFSET(bytes) ((GLubyte*) nullptr + (bytes)) namespace gpu { namespace gl { - static const GLint TRANSFORM_OBJECT_SLOT { 14 }; // SSBO binding slot - // Create a fence and inject a GPU wait on the fence void serverWait(); // Create a fence and synchronously wait on the fence void clientWait(); -gpu::Size getDedicatedMemory(); gpu::Size getFreeDedicatedMemory(); ComparisonFunction comparisonFuncFromGL(GLenum func); State::StencilOp stencilOpFromGL(GLenum stencilOp); @@ -35,25 +35,6 @@ State::BlendOp blendOpFromGL(GLenum blendOp); State::BlendArg blendArgFromGL(GLenum blendArg); void getCurrentGLState(State::Data& state); -struct ShaderObject { - GLuint glshader { 0 }; - GLuint glprogram { 0 }; - GLint transformCameraSlot { -1 }; - GLint transformObjectSlot { -1 }; -}; - -int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, - Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers); -int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); -int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); -int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); -//CLIMAX_MERGE_START -//makeResourceBufferSlots has been added to glbacked as a virtual function and is being used in gl42 and gl45 overrides. -//Since these files dont exist in the andoid version create a stub here. -int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers); -//CLIMAX_MERGE_END -void makeProgramBindings(ShaderObject& shaderObject); - enum GLSyncState { // The object is currently undergoing no processing, although it's content // may be out of date, or it's storage may be invalid relative to the @@ -128,7 +109,9 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = { GL_SHORT, GL_UNSIGNED_SHORT, GL_BYTE, - GL_UNSIGNED_BYTE + GL_UNSIGNED_BYTE, + GL_UNSIGNED_BYTE, + GL_INT_2_10_10_10_REV, }; bool checkGLError(const char* name = nullptr); @@ -156,6 +139,7 @@ class GLQuery; class GLState; class GLShader; class GLTexture; +struct ShaderObject; } } // namespace gpu::gl diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp index 9808d389f1..2a39901ee7 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp @@ -11,13 +11,58 @@ using namespace gpu; using namespace gpu::gl; +bool GLTexelFormat::isCompressed() const { + switch (internalFormat) { + case GL_COMPRESSED_R11_EAC: + case GL_COMPRESSED_SIGNED_R11_EAC: + case GL_COMPRESSED_RG11_EAC: + case GL_COMPRESSED_SIGNED_RG11_EAC: + case GL_COMPRESSED_RGB8_ETC2: + case GL_COMPRESSED_SRGB8_ETC2: + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_RGBA8_ETC2_EAC: + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + case GL_COMPRESSED_RGBA_ASTC_4x4: + case GL_COMPRESSED_RGBA_ASTC_5x4: + case GL_COMPRESSED_RGBA_ASTC_5x5: + case GL_COMPRESSED_RGBA_ASTC_6x5: + case GL_COMPRESSED_RGBA_ASTC_6x6: + case GL_COMPRESSED_RGBA_ASTC_8x5: + case GL_COMPRESSED_RGBA_ASTC_8x6: + case GL_COMPRESSED_RGBA_ASTC_8x8: + case GL_COMPRESSED_RGBA_ASTC_10x5: + case GL_COMPRESSED_RGBA_ASTC_10x6: + case GL_COMPRESSED_RGBA_ASTC_10x8: + case GL_COMPRESSED_RGBA_ASTC_10x10: + case GL_COMPRESSED_RGBA_ASTC_12x10: + case GL_COMPRESSED_RGBA_ASTC_12x12: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12: + return true; + default: + return false; + } +} GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { -// qDebug() << "GLTexelFormat::evalGLTexelFormatInternal " << dstFormat.getDimension() << ", " << dstFormat.getSemantic() << ", " << dstFormat.getType(); GLenum result = GL_RGBA8; switch (dstFormat.getDimension()) { case gpu::SCALAR: { switch (dstFormat.getSemantic()) { + case gpu::RED: case gpu::RGB: case gpu::RGBA: case gpu::SRGB: @@ -44,6 +89,12 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { case gpu::INT16: result = GL_R16I; break; + case gpu::NUINT16: + //result = GL_R16_EXT; + break; + case gpu::NINT16: + //result = GL_R16_SNORM_EXT; + break; case gpu::HALF: result = GL_R16F; break; @@ -55,8 +106,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { break; case gpu::NUINT8: if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { - //result = GL_SLUMINANCE8; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormatInternal GL_SLUMINANCE8"; + result = GL_SLUMINANCE8_NV; } else { result = GL_R8; } @@ -65,7 +115,6 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { result = GL_R8_SNORM; break; default: - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormatInternal " << dstFormat.getType(); Q_UNREACHABLE(); break; } @@ -74,10 +123,19 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { // the type should be float result = GL_R11F_G11F_B10F; break; - + case gpu::RGB9E5: + // the type should be float + result = GL_RGB9_E5; + break; case gpu::DEPTH: result = GL_DEPTH_COMPONENT16; switch (dstFormat.getType()) { + case gpu::UINT32: + case gpu::INT32: + case gpu::NUINT32: + case gpu::NINT32: + result = GL_DEPTH_COMPONENT32_OES; + break; case gpu::FLOAT: result = GL_DEPTH_COMPONENT32F; break; @@ -105,7 +163,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } @@ -114,10 +172,11 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: result = GL_RG8; break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -131,11 +190,10 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { break; case gpu::SRGB: case gpu::SRGBA: - //result = GL_SRGB8; // standard 2.2 gamma correction color - result = GL_RGB8; // standard 2.2 gamma correction color + result = GL_SRGB8; // standard 2.2 gamma correction color break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -163,6 +221,12 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { case gpu::INT16: result = GL_RGBA16I; break; + case gpu::NUINT16: + //result = GL_RGBA16_EXT; + break; + case gpu::NINT16: + //result = GL_RGBA16_SNORM_EXT; + break; case gpu::HALF: result = GL_RGBA16F; break; @@ -175,45 +239,40 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { case gpu::NUINT8: result = GL_RGBA8; break; + case gpu::NUINT2: + //result = GL_RGBA2; + break; case gpu::NINT8: result = GL_RGBA8_SNORM; break; - case gpu::COMPRESSED: - case gpu::NUINT2: - case gpu::NINT16: - case gpu::NUINT16: + case gpu::NINT2_10_10_10: case gpu::NUINT32: case gpu::NINT32: + case gpu::COMPRESSED: case gpu::NUM_TYPES: // quiet compiler Q_UNREACHABLE(); } break; case gpu::SRGB: - //result = GL_SRGB8; - result = GL_RGB8; - qDebug() << "SRGBA Here 2"; + result = GL_SRGB8; break; case gpu::SRGBA: result = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } - default: qCDebug(gpugllogging) << "Unknown combination of texel format"; } //qDebug() << "GLTexelFormat::evalGLTexelFormatInternal result " << result; - return result; } GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) { -// qDebug() << "GLTexelFormat::evalGLTexelFormat dst.getDimension=" << dstFormat.getDimension() << " dst.getType=" << dstFormat.getType() << " dst.getSemantic=" << dstFormat.getSemantic(); -// qDebug() << "GLTexelFormat::evalGLTexelFormat src.getDimension=" << srcFormat.getDimension() << " src.getType=" << srcFormat.getType() << " src.getSemantic=" << srcFormat.getSemantic(); if (dstFormat != srcFormat) { GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }; @@ -224,20 +283,15 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; switch (dstFormat.getSemantic()) { + case gpu::RED: case gpu::RGB: case gpu::RGBA: texel.internalFormat = GL_R8; break; - //CLIMAX_MERGE_START - // case gpu::COMPRESSED_R: - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RED_RGTC1"; - // //texel.internalFormat = GL_COMPRESSED_RED_RGTC1; - // break; - //CLIMAX_MERGE_END - case gpu::DEPTH: - texel.internalFormat = GL_DEPTH_COMPONENT32_OES; + texel.format = GL_DEPTH_COMPONENT; + texel.internalFormat = GL_DEPTH_COMPONENT32F; break; case gpu::DEPTH_STENCIL: texel.type = GL_UNSIGNED_INT_24_8; @@ -245,7 +299,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_DEPTH24_STENCIL8; break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } @@ -257,10 +311,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: texel.internalFormat = GL_RG8; break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -276,19 +331,8 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E case gpu::RGBA: texel.internalFormat = GL_RGB8; break; - //CLIMAX_MERGE_START - //not needed? - // case gpu::COMPRESSED_RGB: - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGB"; - // //texel.internalFormat = GL_COMPRESSED_RGB; - // break; - // case gpu::COMPRESSED_SRGB: - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB"; - // //texel.internalFormat = GL_COMPRESSED_SRGB; - // break; - //CLIMAX_MERGE_END default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -301,8 +345,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (srcFormat.getSemantic()) { case gpu::BGRA: case gpu::SBGRA: - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_BGRA"; - //texel.format = GL_BGRA; + texel.format = GL_RGBA; // GL_BGRA_EXT; break; case gpu::RGB: case gpu::RGBA: @@ -320,49 +363,19 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_RGBA8; break; case gpu::SRGB: - //texel.internalFormat = GL_SRGB8; - texel.internalFormat = GL_RGB8; - qDebug() << "SRGBA Here 3"; + texel.internalFormat = GL_SRGB8; break; case gpu::SRGBA: texel.internalFormat = GL_SRGB8_ALPHA8; break; - - //CLIMAX_MERGE_START - // case gpu::COMPRESSED_RGBA: - // //texel.internalFormat = GL_COMPRESSED_RGBA; - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGBA"; - // break; - // case gpu::COMPRESSED_SRGBA: - // //texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA; - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB_ALPHA"; - // break; - //CLIMAX_MERGE_END - // FIXME: WE will want to support this later - /* - case gpu::COMPRESSED_BC3_RGBA: - texel.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - break; - case gpu::COMPRESSED_BC3_SRGBA: - texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; - break; - - case gpu::COMPRESSED_BC7_RGBA: - texel.internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; - break; - case gpu::COMPRESSED_BC7_SRGBA: - texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; - break; - */ - default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } return texel; } else { @@ -374,12 +387,8 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; switch (dstFormat.getSemantic()) { - //CLIMAX_MERGE_START - // case gpu::COMPRESSED_R: { - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RED_RGTC1"; - // //texel.internalFormat = GL_COMPRESSED_RED_RGTC1; - // break; - // } + + case gpu::RED: case gpu::RGB: case gpu::RGBA: case gpu::SRGB: @@ -415,13 +424,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; } case gpu::NUINT16: { - //texel.internalFormat = GL_R16; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_R16"; + texel.internalFormat = GL_R16_EXT; break; } case gpu::NINT16: { - //texel.internalFormat = GL_R16_SNORM; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_R16_SNORM"; + texel.internalFormat = GL_R16_SNORM_EXT; break; } case gpu::HALF: { @@ -438,9 +445,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E } case gpu::NUINT8: { if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { -// texel.internalFormat = GL_SLUMINANCE8; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_SLUMINANCE8"; - + texel.internalFormat = GL_SLUMINANCE8_NV; } else { texel.internalFormat = GL_R8; } @@ -461,10 +466,16 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E case gpu::R11G11B10: texel.format = GL_RGB; - // the type should be float + texel.type = GL_UNSIGNED_INT_10F_11F_11F_REV; texel.internalFormat = GL_R11F_G11F_B10F; break; + case gpu::RGB9E5: + texel.format = GL_RGB; + texel.type = GL_UNSIGNED_INT_5_9_9_9_REV; + texel.internalFormat = GL_RGB9_E5; + break; + case gpu::DEPTH: texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it texel.internalFormat = GL_DEPTH_COMPONENT32_OES; @@ -497,6 +508,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E } case gpu::COMPRESSED: case gpu::NUINT2: + case gpu::NINT2_10_10_10: case gpu::NUM_TYPES: { // quiet compiler Q_UNREACHABLE(); } @@ -508,7 +520,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_DEPTH24_STENCIL8; break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -521,10 +533,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: texel.internalFormat = GL_RG8; break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -542,20 +555,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; case gpu::SRGB: case gpu::SRGBA: - //texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color - texel.internalFormat = GL_RGB8; // standard 2.2 gamma correction color + texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color break; - //CLIMAX_MERGE_START - // case gpu::COMPRESSED_RGB: - // //texel.internalFormat = GL_COMPRESSED_RGB; - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGB"; - // break; - // case gpu::COMPRESSED_SRGB: - // //texel.internalFormat = GL_COMPRESSED_SRGB; - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB"; - // break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } @@ -592,13 +595,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; case gpu::NUINT16: texel.format = GL_RGBA; - //texel.internalFormat = GL_RGBA16; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_RGBA16"; + //texel.internalFormat = GL_RGBA16_EXT; break; case gpu::NINT16: texel.format = GL_RGBA; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_RGBA16_SNORM"; - //texel.internalFormat = GL_RGBA16_SNORM; + //texel.internalFormat = GL_RGBA16_SNORM_EXT; break; case gpu::HALF: texel.format = GL_RGBA; @@ -620,39 +621,32 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.format = GL_RGBA; texel.internalFormat = GL_RGBA8_SNORM; break; + case gpu::NUINT2: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8; + break; case gpu::NUINT32: case gpu::NINT32: + case gpu::NINT2_10_10_10: case gpu::COMPRESSED: - case gpu::NUINT2: case gpu::NUM_TYPES: // quiet compiler Q_UNREACHABLE(); } break; case gpu::SRGB: - //texel.internalFormat = GL_SRGB8; - texel.internalFormat = GL_RGB8; // standard 2.2 gamma correction color + texel.internalFormat = GL_SRGB8; break; case gpu::SRGBA: texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color break; - //CLIMAX_MERGE_START - // case gpu::COMPRESSED_RGBA: - // //texel.internalFormat = GL_COMPRESSED_RGBA; - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGBA"; - // break; - // case gpu::COMPRESSED_SRGBA: - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB_ALPHA"; - // //texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA; - // break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } default: qCDebug(gpugllogging) << "Unknown combination of texel format"; } - //qDebug() << "GLTexelFormat::evalGLTexelFormat Texel.type " << texel.type << " - texel.format=" << texel.format << " texel.internalFormat=" << texel.internalFormat; return texel; } } diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h index 94ded3dc23..8f37f6b604 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h +++ b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h @@ -18,6 +18,11 @@ public: GLenum format; GLenum type; + GLTexelFormat(GLenum glinternalFormat, GLenum glformat, GLenum gltype) : internalFormat(glinternalFormat), format(glformat), type(gltype) {} + GLTexelFormat(GLenum glinternalFormat) : internalFormat(glinternalFormat) {} + + bool isCompressed() const; + static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) { return evalGLTexelFormat(dstFormat, dstFormat); } diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp index 5f5e3a9be1..08b3f87094 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp @@ -8,35 +8,28 @@ #include "GLTexture.h" +#include #include -#include "GLTextureTransfer.h" #include "GLBackend.h" using namespace gpu; using namespace gpu::gl; -std::shared_ptr GLTexture::_textureTransferHelper; -// FIXME placeholder for texture memory over-use -#define DEFAULT_MAX_MEMORY_MB 256 -#define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f -#define OVER_MEMORY_PRESSURE 2.0f - -const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = { +const GLenum GLTexture::CUBE_FACE_LAYOUT[GLTexture::TEXTURE_CUBE_NUM_FACES] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z }; + const GLenum GLTexture::WRAP_MODES[Sampler::NUM_WRAP_MODES] = { GL_REPEAT, // WRAP_REPEAT, GL_MIRRORED_REPEAT, // WRAP_MIRROR, GL_CLAMP_TO_EDGE, // WRAP_CLAMP, - GL_CLAMP_TO_BORDER_EXT, // WRAP_BORDER, - - //GL_MIRROR_CLAMP_TO_EDGE_EXT // WRAP_MIRROR_ONCE, -// qDebug() << "TODO: GLTexture.cpp:WRAP_MODES GL_MIRROR_CLAMP_TO_EDGE_EXT"; + GL_CLAMP_TO_BORDER, // WRAP_BORDER, + GL_MIRRORED_REPEAT //GL_MIRROR_CLAMP_TO_EDGE_EXT // WRAP_MIRROR_ONCE, }; const GLFilterMode GLTexture::FILTER_MODES[Sampler::NUM_FILTERS] = { @@ -73,6 +66,17 @@ GLenum GLTexture::getGLTextureType(const Texture& texture) { return GL_TEXTURE_2D; } +uint8_t GLTexture::getFaceCount(GLenum target) { + switch (target) { + case GL_TEXTURE_2D: + return TEXTURE_2D_NUM_FACES; + case GL_TEXTURE_CUBE_MAP: + return TEXTURE_CUBE_NUM_FACES; + default: + Q_UNREACHABLE(); + break; + } +} const std::vector& GLTexture::getFaceTargets(GLenum target) { static std::vector cubeFaceTargets { @@ -96,228 +100,602 @@ const std::vector& GLTexture::getFaceTargets(GLenum target) { return faceTargets; } -// Default texture memory = GPU total memory - 2GB -#define GPU_MEMORY_RESERVE_BYTES MB_TO_BYTES(2048) -// Minimum texture memory = 1GB -#define TEXTURE_MEMORY_MIN_BYTES MB_TO_BYTES(1024) - - -float GLTexture::getMemoryPressure() { - // Check for an explicit memory limit - auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage(); - - - // If no memory limit has been set, use a percentage of the total dedicated memory - if (!availableTextureMemory) { -#if 0 - auto totalMemory = getDedicatedMemory(); - if ((GPU_MEMORY_RESERVE_BYTES + TEXTURE_MEMORY_MIN_BYTES) > totalMemory) { - availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; - } else { - availableTextureMemory = totalMemory - GPU_MEMORY_RESERVE_BYTES; - } -#else - // Hardcode texture limit for sparse textures at 1 GB for now - availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; -#endif - } - - // Return the consumed texture memory divided by the available texture memory. - //CLIMAX_MERGE_START - //auto consumedGpuMemory = Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage(); - //float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory; - //static Context::Size lastConsumedGpuMemory = 0; - //if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) { - // lastConsumedGpuMemory = consumedGpuMemory; - // qCDebug(gpugllogging) << "Exceeded max allowed texture memory: " << consumedGpuMemory << " / " << availableTextureMemory; - //} - //return memoryPressure; - return 0; - -} - - -// Create the texture and allocate storage -GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : - GLObject(backend, texture, id), - _external(false), - _source(texture.source()), - _storageStamp(texture.getStamp()), - _target(getGLTextureType(texture)), - _internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())), - _maxMip(texture.getMaxMip()), - _minMip(texture.getMinMip()), - _virtualSize(texture.evalTotalSize()), - _transferrable(transferrable) -{ - //qDebug() << "GLTexture::GLTexture building GLTexture with _internalFormat" << _internalFormat; - auto strongBackend = _backend.lock(); - strongBackend->recycle(); - //CLIMAX_MERGE_START - //Backend::incrementTextureGPUCount(); - //Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize); - //CLIMAX_MERGE_END - Backend::setGPUObject(texture, this); -} - GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : - GLObject(backend, texture, id), - _external(true), - _source(texture.source()), - _storageStamp(0), - _target(getGLTextureType(texture)), - _internalFormat(GL_RGBA8), - // FIXME force mips to 0? - _maxMip(texture.getMaxMip()), - _minMip(texture.getMinMip()), - _virtualSize(0), - _transferrable(false) + GLObject(backend, texture, id), + _source(texture.source()), + _target(getGLTextureType(texture)), + _texelFormat(GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())) { Backend::setGPUObject(texture, this); - - // FIXME Is this necessary? - //withPreservedTexture([this] { - // syncSampler(); - // if (_gpuObject.isAutogenerateMips()) { - // generateMips(); - // } - //}); } GLTexture::~GLTexture() { auto backend = _backend.lock(); - if (backend) { - if (_external) { - auto recycler = _gpuObject.getExternalRecycler(); - if (recycler) { - backend->releaseExternalTexture(_id, recycler); - } else { - qWarning() << "No recycler available for texture " << _id << " possible leak"; - } - } else if (_id) { - // WARNING! Sparse textures do not use this code path. See GL45BackendTexture for - // the GL45Texture destructor for doing any required work tracking GPU stats - backend->releaseTexture(_id, _size); - } - - ////CLIMAX_MERGE_START - //if (!_external && !_transferrable) { - // Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0); - //} + if (backend && _id) { + backend->releaseTexture(_id, 0); } - //Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); - //CLIMAX_MERGE_END } -void GLTexture::createTexture() { - withPreservedTexture([&] { - allocateStorage(); - (void)CHECK_GL_ERROR(); - syncSampler(); - (void)CHECK_GL_ERROR(); +Size GLTexture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { + return 0; + } + auto dim = _gpuObject.evalMipDimensions(sourceMip); + auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); + auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); + if (mipData) { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); + return copyMipFaceLinesFromTexture(targetMip, face, dim, 0, texelFormat.internalFormat, texelFormat.format, texelFormat.type, mipSize, mipData->readData()); + } else { + qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); + } + return 0; +} + + +GLExternalTexture::GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) + : Parent(backend, texture, id) { + Backend::textureExternalCount.increment(); +} + +GLExternalTexture::~GLExternalTexture() { + auto backend = _backend.lock(); + if (backend) { + auto recycler = _gpuObject.getExternalRecycler(); + if (recycler) { + backend->releaseExternalTexture(_id, recycler); + } else { + qCWarning(gpugllogging) << "No recycler available for texture " << _id << " possible leak"; + } + const_cast(_id) = 0; + } + Backend::textureExternalCount.decrement(); +} + + +// Variable sized textures +using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState; +using WorkQueue = GLVariableAllocationSupport::WorkQueue; +using TransferJobPointer = GLVariableAllocationSupport::TransferJobPointer; + +std::list GLVariableAllocationSupport::_memoryManagedTextures; +MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle }; +std::atomic GLVariableAllocationSupport::_memoryPressureStateStale { false }; +const uvec3 GLVariableAllocationSupport::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; +WorkQueue GLVariableAllocationSupport::_transferQueue; +WorkQueue GLVariableAllocationSupport::_promoteQueue; +WorkQueue GLVariableAllocationSupport::_demoteQueue; +size_t GLVariableAllocationSupport::_frameTexturesCreated { 0 }; + +#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f +#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f +#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) + +static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); + +using TransferJob = GLVariableAllocationSupport::TransferJob; + +const uvec3 GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; +const size_t GLVariableAllocationSupport::MAX_TRANSFER_SIZE = GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.x * GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.y * 4; + +#if THREADED_TEXTURE_BUFFERING + +TexturePointer GLVariableAllocationSupport::_currentTransferTexture; +TransferJobPointer GLVariableAllocationSupport::_currentTransferJob; +QThreadPool* TransferJob::_bufferThreadPool { nullptr }; + +void TransferJob::startBufferingThread() { + static std::once_flag once; + std::call_once(once, [&] { + _bufferThreadPool = new QThreadPool(qApp); + _bufferThreadPool->setMaxThreadCount(1); }); } -void GLTexture::withPreservedTexture(std::function f) const { - GLint boundTex = -1; - switch (_target) { - case GL_TEXTURE_2D: - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); - break; +#endif - case GL_TEXTURE_CUBE_MAP: - glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); - break; +TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) + : _parent(parent) { - default: - qFatal("Unsupported texture type"); + auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + GLenum format; + GLenum internalFormat; + GLenum type; + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); + format = texelFormat.format; + internalFormat = texelFormat.internalFormat; + type = texelFormat.type; + _transferSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face); + + // If we're copying a subsection of the mip, do additional calculations to find the size and offset of the segment + if (0 != lines) { + transferDimensions.y = lines; + auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + auto bytesPerLine = (uint32_t)_transferSize / dimensions.y; + _transferOffset = bytesPerLine * lineOffset; + _transferSize = bytesPerLine * lines; } - (void)CHECK_GL_ERROR(); - glBindTexture(_target, _texture); - f(); - glBindTexture(_target, boundTex); - (void)CHECK_GL_ERROR(); + Backend::texturePendingGPUTransferMemSize.update(0, _transferSize); + + if (_transferSize > GLVariableAllocationSupport::MAX_TRANSFER_SIZE) { + qCWarning(gpugllogging) << "Transfer size of " << _transferSize << " exceeds theoretical maximum transfer size"; + } + + // Buffering can invoke disk IO, so it should be off of the main and render threads + _bufferingLambda = [=] { + auto mipStorage = _parent._gpuObject.accessStoredMipFace(sourceMip, face); + if (mipStorage) { + _mipData = mipStorage->createView(_transferSize, _transferOffset); + } else { + qCWarning(gpugllogging) << "Buffering failed because mip could not be retrieved from texture " << _parent._source.c_str() ; + } + }; + + _transferLambda = [=] { + if (_mipData) { + _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _mipData->size(), _mipData->readData()); + _mipData.reset(); + } else { + qCWarning(gpugllogging) << "Transfer failed because mip could not be retrieved from texture " << _parent._source.c_str(); + } + }; } -void GLTexture::setSize(GLuint size) const { - ////CLIMAX_MERGE_START - //if (!_external && !_transferrable) { - // Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0); - //} - //Backend::updateTextureGPUMemoryUsage(_size, size); - const_cast(_size) = size; +TransferJob::TransferJob(const GLTexture& parent, std::function transferLambda) + : _parent(parent), _bufferingRequired(false), _transferLambda(transferLambda) { } -bool GLTexture::isInvalid() const { - return _storageStamp < _gpuObject.getStamp(); +TransferJob::~TransferJob() { + Backend::texturePendingGPUTransferMemSize.update(_transferSize, 0); } -bool GLTexture::isOutdated() const { - return GLSyncState::Idle == _syncState && _contentStamp < _gpuObject.getDataStamp(); -} - -bool GLTexture::isReady() const { - // If we have an invalid texture, we're never ready - if (isInvalid()) { +bool TransferJob::tryTransfer() { +#if THREADED_TEXTURE_BUFFERING + // Are we ready to transfer + if (!bufferingCompleted()) { + startBuffering(); return false; } - - auto syncState = _syncState.load(); - if (isOutdated() || Idle != syncState) { - return false; +#else + if (_bufferingRequired) { + _bufferingLambda(); } - +#endif + _transferLambda(); return true; } +#if THREADED_TEXTURE_BUFFERING +bool TransferJob::bufferingRequired() const { + if (!_bufferingRequired) { + return false; + } -// Do any post-transfer operations that might be required on the main context / rendering thread -void GLTexture::postTransfer() { - //CLIMAX_MERGE_START - - // setSyncState(GLSyncState::Idle); - // ++_transferCount; + // The default state of a QFuture is with status Canceled | Started | Finished, + // so we have to check isCancelled before we check the actual state + if (_bufferingStatus.isCanceled()) { + return true; + } - // // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory - // switch (_gpuObject.getType()) { - // case Texture::TEX_2D: - // for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - // if (_gpuObject.isStoredMipFaceAvailable(i)) { - // _gpuObject.notifyMipFaceGPULoaded(i); - // } - // } - // break; - - // case Texture::TEX_CUBE: - // // transfer pixels from each faces - // for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) { - // for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - // if (_gpuObject.isStoredMipFaceAvailable(i, f)) { - // _gpuObject.notifyMipFaceGPULoaded(i, f); - // } - // } - // } - // break; - - // default: - // qCWarning(gpugllogging) << __FUNCTION__ << " case for Texture Type " << _gpuObject.getType() << " not supported"; - // break; - // } - //CLIMAX_MERGE_END + return !_bufferingStatus.isStarted(); } -void GLTexture::initTextureTransferHelper() { - _textureTransferHelper = std::make_shared(); +bool TransferJob::bufferingCompleted() const { + if (!_bufferingRequired) { + return true; + } + + // The default state of a QFuture is with status Canceled | Started | Finished, + // so we have to check isCancelled before we check the actual state + if (_bufferingStatus.isCanceled()) { + return false; + } + + return _bufferingStatus.isFinished(); } -void GLTexture::startTransfer() { - createTexture(); +void TransferJob::startBuffering() { + if (bufferingRequired()) { + assert(_bufferingStatus.isCanceled()); + _bufferingStatus = QtConcurrent::run(_bufferThreadPool, [=] { + _bufferingLambda(); + }); + assert(!_bufferingStatus.isCanceled()); + assert(_bufferingStatus.isStarted()); + } +} +#endif + +GLVariableAllocationSupport::GLVariableAllocationSupport() { + _memoryPressureStateStale = true; } -void GLTexture::finishTransfer() { - if (_gpuObject.isAutogenerateMips()) { - generateMips(); +GLVariableAllocationSupport::~GLVariableAllocationSupport() { + _memoryPressureStateStale = true; +} + +void GLVariableAllocationSupport::addMemoryManagedTexture(const TexturePointer& texturePointer) { + _memoryManagedTextures.push_back(texturePointer); + if (MemoryPressureState::Idle != _memoryPressureState) { + addToWorkQueue(texturePointer); } } +void GLVariableAllocationSupport::addToWorkQueue(const TexturePointer& texturePointer) { + GLTexture* gltexture = Backend::getGPUObject(*texturePointer); + GLVariableAllocationSupport* vargltexture = dynamic_cast(gltexture); + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + if (vargltexture->canDemote()) { + // Demote largest first + _demoteQueue.push({ texturePointer, (float)gltexture->size() }); + } + break; + + case MemoryPressureState::Undersubscribed: + if (vargltexture->canPromote()) { + // Promote smallest first + _promoteQueue.push({ texturePointer, 1.0f / (float)gltexture->size() }); + } + break; + + case MemoryPressureState::Transfer: + if (vargltexture->hasPendingTransfers()) { + // Transfer priority given to smaller mips first + _transferQueue.push({ texturePointer, 1.0f / (float)gltexture->_gpuObject.evalMipSize(vargltexture->_populatedMip) }); + } + break; + + case MemoryPressureState::Idle: + Q_UNREACHABLE(); + break; + } +} + +WorkQueue& GLVariableAllocationSupport::getActiveWorkQueue() { + static WorkQueue empty; + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return _demoteQueue; + + case MemoryPressureState::Undersubscribed: + return _promoteQueue; + + case MemoryPressureState::Transfer: + return _transferQueue; + + case MemoryPressureState::Idle: + Q_UNREACHABLE(); + break; + } + return empty; +} + +// FIXME hack for stats display +QString getTextureMemoryPressureModeString() { + switch (GLVariableAllocationSupport::_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return "Oversubscribed"; + + case MemoryPressureState::Undersubscribed: + return "Undersubscribed"; + + case MemoryPressureState::Transfer: + return "Transfer"; + + case MemoryPressureState::Idle: + return "Idle"; + } + Q_UNREACHABLE(); + return "Unknown"; +} + +void GLVariableAllocationSupport::updateMemoryPressure() { + static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + + size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + if (0 == allowedMemoryAllocation) { + allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; + } + + // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale + // so that we react + if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { + _memoryPressureStateStale = true; + lastAllowedMemoryAllocation = allowedMemoryAllocation; + } + + if (!_memoryPressureStateStale.exchange(false)) { + return; + } + + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + + // Clear any defunct textures (weak pointers that no longer have a valid texture) + _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { + return weakPointer.expired(); + }); + + // Convert weak pointers to strong. This new list may still contain nulls if a texture was + // deleted on another thread between the previous line and this one + std::vector strongTextures; { + strongTextures.reserve(_memoryManagedTextures.size()); + std::transform( + _memoryManagedTextures.begin(), _memoryManagedTextures.end(), + std::back_inserter(strongTextures), + [](const TextureWeakPointer& p) { return p.lock(); }); + } + + size_t totalVariableMemoryAllocation = 0; + size_t idealMemoryAllocation = 0; + bool canDemote = false; + bool canPromote = false; + bool hasTransfers = false; + for (const auto& texture : strongTextures) { + // Race conditions can still leave nulls in the list, so we need to check + if (!texture) { + continue; + } + GLTexture* gltexture = Backend::getGPUObject(*texture); + GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); + // Track how much the texture thinks it should be using + idealMemoryAllocation += texture->evalTotalSize(); + // Track how much we're actually using + totalVariableMemoryAllocation += gltexture->size(); + canDemote |= vartexture->canDemote(); + canPromote |= vartexture->canPromote(); + hasTransfers |= vartexture->hasPendingTransfers(); + } + + size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; + float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; + + auto newState = MemoryPressureState::Idle; + if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) { + newState = MemoryPressureState::Undersubscribed; + } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { + newState = MemoryPressureState::Oversubscribed; + } else if (hasTransfers) { + newState = MemoryPressureState::Transfer; + } + + if (newState != _memoryPressureState) { + _memoryPressureState = newState; + // Clear the existing queue + _transferQueue = WorkQueue(); + _promoteQueue = WorkQueue(); + _demoteQueue = WorkQueue(); + + // Populate the existing textures into the queue + if (_memoryPressureState != MemoryPressureState::Idle) { + for (const auto& texture : strongTextures) { + // Race conditions can still leave nulls in the list, so we need to check + if (!texture) { + continue; + } + addToWorkQueue(texture); + } + } + } +} + +TexturePointer GLVariableAllocationSupport::getNextWorkQueueItem(WorkQueue& workQueue) { + while (!workQueue.empty()) { + auto workTarget = workQueue.top(); + + auto texture = workTarget.first.lock(); + if (!texture) { + workQueue.pop(); + continue; + } + + // Check whether the resulting texture can actually have work performed + GLTexture* gltexture = Backend::getGPUObject(*texture); + GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + if (vartexture->canDemote()) { + return texture; + } + break; + + case MemoryPressureState::Undersubscribed: + if (vartexture->canPromote()) { + return texture; + } + break; + + case MemoryPressureState::Transfer: + if (vartexture->hasPendingTransfers()) { + return texture; + } + break; + + case MemoryPressureState::Idle: + Q_UNREACHABLE(); + break; + } + + // If we got here, then the texture has no work to do in the current state, + // so pop it off the queue and continue + workQueue.pop(); + } + + return TexturePointer(); +} + +void GLVariableAllocationSupport::processWorkQueue(WorkQueue& workQueue) { + if (workQueue.empty()) { + return; + } + + // Get the front of the work queue to perform work + auto texture = getNextWorkQueueItem(workQueue); + if (!texture) { + return; + } + + // Grab the first item off the demote queue + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + + GLTexture* gltexture = Backend::getGPUObject(*texture); + GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + vartexture->demote(); + workQueue.pop(); + addToWorkQueue(texture); + _memoryPressureStateStale = true; + break; + + case MemoryPressureState::Undersubscribed: + vartexture->promote(); + workQueue.pop(); + addToWorkQueue(texture); + _memoryPressureStateStale = true; + break; + + case MemoryPressureState::Transfer: + if (vartexture->executeNextTransfer(texture)) { + workQueue.pop(); + addToWorkQueue(texture); + +#if THREADED_TEXTURE_BUFFERING + // Eagerly start the next buffering job if possible + texture = getNextWorkQueueItem(workQueue); + if (texture) { + gltexture = Backend::getGPUObject(*texture); + vartexture = dynamic_cast(gltexture); + vartexture->executeNextBuffer(texture); + } +#endif + } + break; + + case MemoryPressureState::Idle: + Q_UNREACHABLE(); + break; + } +} + +void GLVariableAllocationSupport::processWorkQueues() { + if (MemoryPressureState::Idle == _memoryPressureState) { + return; + } + + auto& workQueue = getActiveWorkQueue(); + // Do work on the front of the queue + processWorkQueue(workQueue); + + if (workQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; + _memoryPressureStateStale = true; + } +} + +void GLVariableAllocationSupport::manageMemory() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + updateMemoryPressure(); + processWorkQueues(); +} + +bool GLVariableAllocationSupport::executeNextTransfer(const TexturePointer& currentTexture) { +#if THREADED_TEXTURE_BUFFERING + // If a transfer job is active on the buffering thread, but has not completed it's buffering lambda, + // then we need to exit early, since we don't want to have the transfer job leave scope while it's + // being used in another thread -- See https://highfidelity.fogbugz.com/f/cases/4626 + if (_currentTransferJob && !_currentTransferJob->bufferingCompleted()) { + return false; + } +#endif + + if (_populatedMip <= _allocatedMip) { +#if THREADED_TEXTURE_BUFFERING + _currentTransferJob.reset(); + _currentTransferTexture.reset(); +#endif + return true; + } + + // If the transfer queue is empty, rebuild it + if (_pendingTransfers.empty()) { + populateTransferQueue(); + } + + bool result = false; + if (!_pendingTransfers.empty()) { +#if THREADED_TEXTURE_BUFFERING + // If there is a current transfer, but it's not the top of the pending transfer queue, then it's an orphan, so we want to abandon it. + if (_currentTransferJob && _currentTransferJob != _pendingTransfers.front()) { + _currentTransferJob.reset(); + } + + if (!_currentTransferJob) { + // Keeping hold of a strong pointer to the transfer job ensures that if the pending transfer queue is rebuilt, the transfer job + // doesn't leave scope, causing a crash in the buffering thread + _currentTransferJob = _pendingTransfers.front(); + + // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture + _currentTransferTexture = currentTexture; + } + + // transfer jobs use asynchronous buffering of the texture data because it may involve disk IO, so we execute a try here to determine if the buffering + // is complete + if (_currentTransferJob->tryTransfer()) { + _pendingTransfers.pop(); + // Once a given job is finished, release the shared pointers keeping them alive + _currentTransferTexture.reset(); + _currentTransferJob.reset(); + result = true; + } +#else + if (_pendingTransfers.front()->tryTransfer()) { + _pendingTransfers.pop(); + result = true; + } +#endif + } + return result; +} + +#if THREADED_TEXTURE_BUFFERING +void GLVariableAllocationSupport::executeNextBuffer(const TexturePointer& currentTexture) { + if (_currentTransferJob && !_currentTransferJob->bufferingCompleted()) { + return; + } + + // If the transfer queue is empty, rebuild it + if (_pendingTransfers.empty()) { + populateTransferQueue(); + } + + if (!_pendingTransfers.empty()) { + if (!_currentTransferJob) { + _currentTransferJob = _pendingTransfers.front(); + _currentTransferTexture = currentTexture; + } + + _currentTransferJob->startBuffering(); + } +} +#endif + +void GLVariableAllocationSupport::incrementPopulatedSize(Size delta) const { + _populatedSize += delta; + // Keep the 2 code paths to be able to debug + if (_size < _populatedSize) { + Backend::textureResourcePopulatedGPUMemSize.update(0, delta); + } else { + Backend::textureResourcePopulatedGPUMemSize.update(0, delta); + } +} +void GLVariableAllocationSupport::decrementPopulatedSize(Size delta) const { + _populatedSize -= delta; + // Keep the 2 code paths to be able to debug + if (_size < _populatedSize) { + Backend::textureResourcePopulatedGPUMemSize.update(delta, 0); + } else { + Backend::textureResourcePopulatedGPUMemSize.update(delta, 0); + } +} \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.h b/libraries/gpu-gles/src/gpu/gl/GLTexture.h index 03353ae67d..ce27d02033 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gles/src/gpu/gl/GLTexture.h @@ -8,10 +8,15 @@ #ifndef hifi_gpu_gl_GLTexture_h #define hifi_gpu_gl_GLTexture_h +#include +#include + #include "GLShared.h" -#include "GLTextureTransfer.h" #include "GLBackend.h" #include "GLTexelFormat.h" +#include + +#define THREADED_TEXTURE_BUFFERING 1 namespace gpu { namespace gl { @@ -20,214 +25,187 @@ struct GLFilterMode { GLint magFilter; }; +class GLVariableAllocationSupport { + friend class GLBackend; + +public: + GLVariableAllocationSupport(); + virtual ~GLVariableAllocationSupport(); + + enum class MemoryPressureState { + Idle, + Transfer, + Oversubscribed, + Undersubscribed, + }; + + using QueuePair = std::pair; + struct QueuePairLess { + bool operator()(const QueuePair& a, const QueuePair& b) { + return a.second < b.second; + } + }; + using WorkQueue = std::priority_queue, QueuePairLess>; + + class TransferJob { + using VoidLambda = std::function; + using VoidLambdaQueue = std::queue; + const GLTexture& _parent; + Texture::PixelsPointer _mipData; + size_t _transferOffset { 0 }; + size_t _transferSize { 0 }; + + bool _bufferingRequired { true }; + VoidLambda _transferLambda; + VoidLambda _bufferingLambda; + +#if THREADED_TEXTURE_BUFFERING + // Indicates if a transfer from backing storage to interal storage has started + QFuture _bufferingStatus; + static QThreadPool* _bufferThreadPool; +#endif + + public: + TransferJob(const TransferJob& other) = delete; + TransferJob(const GLTexture& parent, std::function transferLambda); + TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); + ~TransferJob(); + bool tryTransfer(); + +#if THREADED_TEXTURE_BUFFERING + void startBuffering(); + bool bufferingRequired() const; + bool bufferingCompleted() const; + static void startBufferingThread(); +#endif + + private: + void transfer(); + }; + + using TransferJobPointer = std::shared_ptr; + using TransferQueue = std::queue; + static MemoryPressureState _memoryPressureState; + +public: + static void addMemoryManagedTexture(const TexturePointer& texturePointer); + +protected: + static size_t _frameTexturesCreated; + static std::atomic _memoryPressureStateStale; + static std::list _memoryManagedTextures; + static WorkQueue _transferQueue; + static WorkQueue _promoteQueue; + static WorkQueue _demoteQueue; +#if THREADED_TEXTURE_BUFFERING + static TexturePointer _currentTransferTexture; + static TransferJobPointer _currentTransferJob; +#endif + static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; + static const uvec3 MAX_TRANSFER_DIMENSIONS; + static const size_t MAX_TRANSFER_SIZE; + + + static void updateMemoryPressure(); + static void processWorkQueues(); + static void processWorkQueue(WorkQueue& workQueue); + static TexturePointer getNextWorkQueueItem(WorkQueue& workQueue); + static void addToWorkQueue(const TexturePointer& texture); + static WorkQueue& getActiveWorkQueue(); + + static void manageMemory(); + + //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } + bool canPromote() const { return _allocatedMip > _minAllocatedMip; } + bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } + bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } +#if THREADED_TEXTURE_BUFFERING + void executeNextBuffer(const TexturePointer& currentTexture); +#endif + bool executeNextTransfer(const TexturePointer& currentTexture); + virtual void populateTransferQueue() = 0; + virtual void promote() = 0; + virtual void demote() = 0; + + // THe amount of memory currently allocated + Size _size { 0 }; + + // The amount of memory currnently populated + void incrementPopulatedSize(Size delta) const; + void decrementPopulatedSize(Size delta) const; + mutable Size _populatedSize { 0 }; + + // The allocated mip level, relative to the number of mips in the gpu::Texture object + // The relationship between a given glMip to the original gpu::Texture mip is always + // glMip + _allocatedMip + uint16 _allocatedMip { 0 }; + // The populated mip level, relative to the number of mips in the gpu::Texture object + // This must always be >= the allocated mip + uint16 _populatedMip { 0 }; + // The highest (lowest resolution) mip that we will support, relative to the number + // of mips in the gpu::Texture object + uint16 _maxAllocatedMip { 0 }; + // The lowest (highest resolution) mip that we will support, relative to the number + // of mips in the gpu::Texture object + uint16 _minAllocatedMip { 0 }; + // Contains a series of lambdas that when executed will transfer data to the GPU, modify + // the _populatedMip and update the sampler in order to fully populate the allocated texture + // until _populatedMip == _allocatedMip + TransferQueue _pendingTransfers; +}; class GLTexture : public GLObject { + using Parent = GLObject; + friend class GLBackend; + friend class GLVariableAllocationSupport; public: static const uint16_t INVALID_MIP { (uint16_t)-1 }; static const uint8_t INVALID_FACE { (uint8_t)-1 }; - static void initTextureTransferHelper(); - static std::shared_ptr _textureTransferHelper; - - template - static GLTexture* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) { - const Texture& texture = *texturePointer; - - // Special case external textures - //CLIMAX_MERGE_START - //Z:/HiFi_Android/HiFi_GIT/libraries/gpu-gl-android/src/gpu/gl/../gles/../gl/GLTexture.h:37:32: error: no member named 'isExternal' in 'gpu::Texture::Usage' - // The only instance of this being used again. replace. - // if (texture.getUsage().isExternal()) { - // Texture::ExternalUpdates updates = texture.getUpdates(); - // if (!updates.empty()) { - // Texture::ExternalRecycler recycler = texture.getExternalRecycler(); - // Q_ASSERT(recycler); - // // Discard any superfluous updates - // while (updates.size() > 1) { - // const auto& update = updates.front(); - // // Superfluous updates will never have been read, but we want to ensure the previous - // // writes to them are complete before they're written again, so return them with the - // // same fences they arrived with. This can happen on any thread because no GL context - // // work is involved - // recycler(update.first, update.second); - // updates.pop_front(); - // } - - // // The last texture remaining is the one we'll use to create the GLTexture - // const auto& update = updates.front(); - // // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence - // if (update.second) { - // GLsync fence = static_cast(update.second); - // glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); - // glDeleteSync(fence); - // } - - // // Create the new texture object (replaces any previous texture object) - // new GLTextureType(backend.shared_from_this(), texture, update.first); - // } - - - // Return the texture object (if any) associated with the texture, without extensive logic - // (external textures are - //return Backend::getGPUObject(texture); - //} - //CLIMAX_MERGE_END - if (!texture.isDefined()) { - // NO texture definition yet so let's avoid thinking - return nullptr; - } - - // If the object hasn't been created, or the object definition is out of date, drop and re-create - GLTexture* object = Backend::getGPUObject(texture); - - // Create the texture if need be (force re-creation if the storage stamp changes - // for easier use of immutable storage) - if (!object || object->isInvalid()) { - // This automatically any previous texture - object = new GLTextureType(backend.shared_from_this(), texture, needTransfer); - if (!object->_transferrable) { - object->createTexture(); - object->_contentStamp = texture.getDataStamp(); - object->updateSize(); - object->postTransfer(); - } - } - - // Object maybe doens't neet to be tranasferred after creation - if (!object->_transferrable) { - return object; - } - - // If we just did a transfer, return the object after doing post-transfer work - if (GLSyncState::Transferred == object->getSyncState()) { - object->postTransfer(); - } - - if (object->isOutdated()) { - // Object might be outdated, if so, start the transfer - // (outdated objects that are already in transfer will have reported 'true' for ready() - _textureTransferHelper->transferTexture(texturePointer); - return nullptr; - } - - if (!object->isReady()) { - return nullptr; - } - - ((GLTexture*)object)->updateMips(); - - return object; - } - - template - static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) { - if (!texture) { - return 0; - } - GLTexture* object { nullptr }; - if (shouldSync) { - object = sync(backend, texture, shouldSync); - } else { - object = Backend::getGPUObject(*texture); - } - - if (!object) { - return 0; - } - - if (!shouldSync) { - return object->_id; - } - - // Don't return textures that are in transfer state - if ((object->getSyncState() != GLSyncState::Idle) || - // Don't return transferrable textures that have never completed transfer - (!object->_transferrable || 0 != object->_transferCount)) { - return 0; - } - - return object->_id; - } - ~GLTexture(); - // Is this texture generated outside the GPU library? - const bool _external; const GLuint& _texture { _id }; const std::string _source; - const Stamp _storageStamp; const GLenum _target; - const GLenum _internalFormat; - const uint16 _maxMip; - uint16 _minMip; - const GLuint _virtualSize; // theoretical size as expected - Stamp _contentStamp { 0 }; - const bool _transferrable; - Size _transferCount { 0 }; - GLuint size() const { return _size; } - GLSyncState getSyncState() const { return _syncState; } + GLTexelFormat _texelFormat; - // Is the storage out of date relative to the gpu texture? - bool isInvalid() const; + static const std::vector& getFaceTargets(GLenum textureType); + static uint8_t getFaceCount(GLenum textureType); + static GLenum getGLTextureType(const Texture& texture); - // Is the content out of date relative to the gpu texture? - bool isOutdated() const; - - // Is the texture in a state where it can be rendered with no work? - bool isReady() const; - - // Execute any post-move operations that must occur only on the main thread - virtual void postTransfer(); - - uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; } - - static const size_t CUBE_NUM_FACES = 6; - static const GLenum CUBE_FACE_LAYOUT[6]; + static const uint8_t TEXTURE_2D_NUM_FACES = 1; + static const uint8_t TEXTURE_CUBE_NUM_FACES = 6; + static const GLenum CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES]; static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS]; static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; - // Return a floating point value indicating how much of the allowed - // texture memory we are currently consuming. A value of 0 indicates - // no texture memory usage, while a value of 1 indicates all available / allowed memory - // is consumed. A value above 1 indicates that there is a problem. - static float getMemoryPressure(); protected: - - static const std::vector& getFaceTargets(GLenum textureType); - - static GLenum getGLTextureType(const Texture& texture); - - - const GLuint _size { 0 }; // true size as reported by the gl api - std::atomic _syncState { GLSyncState::Idle }; - - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable); - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); - - void setSyncState(GLSyncState syncState) { _syncState = syncState; } - - void createTexture(); - - virtual void updateMips() {} - virtual void allocateStorage() const = 0; - virtual void updateSize() const = 0; - virtual void syncSampler() const = 0; + virtual Size size() const = 0; virtual void generateMips() const = 0; - virtual void withPreservedTexture(std::function f) const; + virtual void syncSampler() const = 0; -protected: - void setSize(GLuint size) const; + virtual Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const = 0; + virtual Size copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const final; + virtual void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {} // Only relevant for Variable Allocation textures - virtual void startTransfer(); - // Returns true if this is the last block required to complete transfer - virtual bool continueTransfer() { return false; } - virtual void finishTransfer(); - -private: - friend class GLTextureTransferHelper; - friend class GLBackend; + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); }; +class GLExternalTexture : public GLTexture { + using Parent = GLTexture; + friend class GLBackend; +public: + ~GLExternalTexture(); +protected: + GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); + void generateMips() const override {} + void syncSampler() const override {} + Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override { return 0;} + + Size size() const override { return 0; } +}; + + } } #endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp deleted file mode 100644 index cec46cb90d..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/04/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 "GLTextureTransfer.h" - -#include -#include - -#include "GLShared.h" -#include "GLTexture.h" - -#ifdef HAVE_NSIGHT -#include "nvToolsExt.h" -std::unordered_map _map; -#endif - - -#ifdef TEXTURE_TRANSFER_PBOS -#define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024) -#define TEXTURE_TRANSFER_PBO_COUNT 128 -#endif - -using namespace gpu; -using namespace gpu::gl; - -GLTextureTransferHelper::GLTextureTransferHelper() { -#ifdef THREADED_TEXTURE_TRANSFER - setObjectName("TextureTransferThread"); - _context.create(); - initialize(true, QThread::LowPriority); - // Clean shutdown on UNIX, otherwise _canvas is freed early - connect(qApp, &QCoreApplication::aboutToQuit, [&] { terminate(); }); -#else - initialize(false, QThread::LowPriority); -#endif -} - -GLTextureTransferHelper::~GLTextureTransferHelper() { -#ifdef THREADED_TEXTURE_TRANSFER - if (isStillRunning()) { - terminate(); - } -#else - terminate(); -#endif -} - -void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { - GLTexture* object = Backend::getGPUObject(*texturePointer); - - //CLIMAX_MERGE_START - //Backend::incrementTextureGPUTransferCount(); - object->setSyncState(GLSyncState::Pending); - Lock lock(_mutex); - _pendingTextures.push_back(texturePointer); -} - -void GLTextureTransferHelper::setup() { -#ifdef THREADED_TEXTURE_TRANSFER - _context.makeCurrent(); - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // FIXME don't use opengl 4.5 DSA functionality without verifying it's present - glCreateRenderbuffers(1, &_drawRenderbuffer); - glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128); - glCreateFramebuffers(1, &_drawFramebuffer); - glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer); - glCreateFramebuffers(1, &_readFramebuffer); -#endif - -#ifdef TEXTURE_TRANSFER_PBOS - std::array pbos; - glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]); - for (uint32_t i = 0; i < TEXTURE_TRANSFER_PBO_COUNT; ++i) { - TextureTransferBlock newBlock; - newBlock._pbo = pbos[i]; - glNamedBufferStorage(newBlock._pbo, TEXTURE_TRANSFER_BLOCK_SIZE, 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); - newBlock._mapped = glMapNamedBufferRange(newBlock._pbo, 0, TEXTURE_TRANSFER_BLOCK_SIZE, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); - _readyQueue.push(newBlock); - } -#endif -#endif -} - -void GLTextureTransferHelper::shutdown() { -#ifdef THREADED_TEXTURE_TRANSFER - _context.makeCurrent(); -#endif - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); - glDeleteFramebuffers(1, &_drawFramebuffer); - _drawFramebuffer = 0; - glDeleteFramebuffers(1, &_readFramebuffer); - _readFramebuffer = 0; - - glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0); - glDeleteRenderbuffers(1, &_drawRenderbuffer); - _drawRenderbuffer = 0; -#endif -} - -void GLTextureTransferHelper::queueExecution(VoidLambda lambda) { - Lock lock(_mutex); - _pendingCommands.push_back(lambda); -} - -#define MAX_TRANSFERS_PER_PASS 2 - -bool GLTextureTransferHelper::process() { - // Take any new textures or commands off the queue - VoidLambdaList pendingCommands; - TextureList newTransferTextures; - { - Lock lock(_mutex); - newTransferTextures.swap(_pendingTextures); - pendingCommands.swap(_pendingCommands); - } - - if (!pendingCommands.empty()) { - for (auto command : pendingCommands) { - command(); - } - glFlush(); - } - - if (!newTransferTextures.empty()) { - for (auto& texturePointer : newTransferTextures) { -#ifdef HAVE_NSIGHT - _map[texturePointer] = nvtxRangeStart("TextureTansfer"); -#endif - GLTexture* object = Backend::getGPUObject(*texturePointer); - object->startTransfer(); - _transferringTextures.push_back(texturePointer); - _textureIterator = _transferringTextures.begin(); - } - _transferringTextures.sort([](const gpu::TexturePointer& a, const gpu::TexturePointer& b)->bool { - return a->getSize() < b->getSize(); - }); - } - - // No transfers in progress, sleep - if (_transferringTextures.empty()) { -#ifdef THREADED_TEXTURE_TRANSFER - QThread::usleep(1); -#endif - return true; - } - - static auto lastReport = usecTimestampNow(); - auto now = usecTimestampNow(); - auto lastReportInterval = now - lastReport; - if (lastReportInterval > USECS_PER_SECOND * 4) { - lastReport = now; - qDebug() << "Texture list " << _transferringTextures.size(); - } - - size_t transferCount = 0; - for (_textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) { - if (++transferCount > MAX_TRANSFERS_PER_PASS) { - break; - } - auto texture = *_textureIterator; - GLTexture* gltexture = Backend::getGPUObject(*texture); - if (gltexture->continueTransfer()) { - ++_textureIterator; - continue; - } - - gltexture->finishTransfer(); - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use -#endif - -#ifdef THREADED_TEXTURE_TRANSFER - clientWait(); -#endif - gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp(); - gltexture->updateSize(); - gltexture->setSyncState(gpu::gl::GLSyncState::Transferred); - //CLIMAX_MERGE_START - //Backend::decrementTextureGPUTransferCount(); -#ifdef HAVE_NSIGHT - // Mark the texture as transferred - nvtxRangeEnd(_map[texture]); - _map.erase(texture); -#endif - _textureIterator = _transferringTextures.erase(_textureIterator); - } - -#ifdef THREADED_TEXTURE_TRANSFER - if (!_transferringTextures.empty()) { - // Don't saturate the GPU - clientWait(); - } else { - // Don't saturate the CPU - QThread::msleep(1); - } -#endif - - return true; -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h deleted file mode 100644 index a23c282fd4..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h +++ /dev/null @@ -1,78 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/04/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 -// -#ifndef hifi_gpu_gl_GLTextureTransfer_h -#define hifi_gpu_gl_GLTextureTransfer_h - -#include -#include - -#include - -#include - -#include "GLShared.h" - -#ifdef Q_OS_WIN -#define THREADED_TEXTURE_TRANSFER -#endif - -#ifdef THREADED_TEXTURE_TRANSFER -// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread -// also, the current draw code is implicitly using OpenGL 4.5 functionality -//#define TEXTURE_TRANSFER_FORCE_DRAW -// FIXME PBO's increase the complexity and don't seem to work reliably -//#define TEXTURE_TRANSFER_PBOS -#endif - -namespace gpu { namespace gl { - -using TextureList = std::list; -using TextureListIterator = TextureList::iterator; - -class GLTextureTransferHelper : public GenericThread { -public: - using VoidLambda = std::function; - using VoidLambdaList = std::list; - using Pointer = std::shared_ptr; - GLTextureTransferHelper(); - ~GLTextureTransferHelper(); - void transferTexture(const gpu::TexturePointer& texturePointer); - void queueExecution(VoidLambda lambda); - - void setup() override; - void shutdown() override; - bool process() override; - -private: -#ifdef THREADED_TEXTURE_TRANSFER - ::gl::OffscreenContext _context; -#endif - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread - GLuint _drawRenderbuffer { 0 }; - GLuint _drawFramebuffer { 0 }; - GLuint _readFramebuffer { 0 }; -#endif - - // A mutex for protecting items access on the render and transfer threads - Mutex _mutex; - // Commands that have been submitted for execution on the texture transfer thread - VoidLambdaList _pendingCommands; - // Textures that have been submitted for transfer - TextureList _pendingTextures; - // Textures currently in the transfer process - // Only used on the transfer thread - TextureList _transferringTextures; - TextureListIterator _textureIterator; - -}; - -} } - -#endif \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h index 69a417d952..aeda054e72 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h @@ -27,6 +27,12 @@ class GLESBackend : public GLBackend { friend class Context; public: + static const GLint TRANSFORM_OBJECT_SLOT { 31 }; + static const GLint RESOURCE_TRANSFER_TEX_UNIT { 32 }; + static const GLint RESOURCE_TRANSFER_EXTRA_TEX_UNIT { 33 }; + static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 34 }; + static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 35 }; + static bool supportedTextureFormat(const gpu::Element& format); explicit GLESBackend(bool syncCache) : Parent(syncCache) {} GLESBackend() : Parent() {} virtual ~GLESBackend() { @@ -38,33 +44,95 @@ public: static const std::string GLES_VERSION; const std::string& getVersion() const override { return GLES_VERSION; } - class GLESTexture : public GLTexture { using Parent = GLTexture; - GLuint allocate(); - public: - GLESTexture(const std::weak_ptr& backend, const Texture& buffer, GLuint externalId); - GLESTexture(const std::weak_ptr& backend, const Texture& buffer, bool transferrable); - + friend class GLESBackend; + GLuint allocate(const Texture& texture); protected: - void transferMip(uint16_t mipLevel, uint8_t face) const; - void startTransfer() override; - void allocateStorage() const override; - void updateSize() const override; - void syncSampler() const override; + GLESTexture(const std::weak_ptr& backend, const Texture& buffer); void generateMips() const override; + Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override; + void syncSampler() const override; + + void withPreservedTexture(std::function f) const; }; + // + // Textures that have fixed allocation sizes and cannot be managed at runtime + // + + class GLESFixedAllocationTexture : public GLESTexture { + using Parent = GLESTexture; + friend class GLESBackend; + + public: + GLESFixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GLESFixedAllocationTexture(); + + protected: + Size size() const override { return _size; } + void allocateStorage() const; + void syncSampler() const override; + const Size _size { 0 }; + }; + + class GLESAttachmentTexture : public GLESFixedAllocationTexture { + using Parent = GLESFixedAllocationTexture; + friend class GLESBackend; + protected: + GLESAttachmentTexture(const std::weak_ptr& backend, const Texture& texture); + ~GLESAttachmentTexture(); + }; + + class GLESStrictResourceTexture : public GLESFixedAllocationTexture { + using Parent = GLESFixedAllocationTexture; + friend class GLESBackend; + protected: + GLESStrictResourceTexture(const std::weak_ptr& backend, const Texture& texture); + ~GLESStrictResourceTexture(); + }; + + class GLESVariableAllocationTexture : public GLESTexture, public GLVariableAllocationSupport { + using Parent = GLESTexture; + friend class GLESBackend; + using PromoteLambda = std::function; + + + protected: + GLESVariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GLESVariableAllocationTexture(); + + void allocateStorage(uint16 allocatedMip); + void syncSampler() const override; + void promote() override; + void demote() override; + void populateTransferQueue() override; + + Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override; + Size copyMipsFromTexture(); + + void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) override; + + Size size() const override { return _size; } + }; + + class GLESResourceTexture : public GLESVariableAllocationTexture { + using Parent = GLESVariableAllocationTexture; + friend class GLESBackend; + protected: + GLESResourceTexture(const std::weak_ptr& backend, const Texture& texture); + ~GLESResourceTexture(); + }; protected: GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; GLuint getBufferID(const Buffer& buffer) override; + GLuint getResourceBufferID(const Buffer& buffer); GLBuffer* syncGPUObject(const Buffer& buffer) override; - GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override; - GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override; + GLTexture* syncGPUObject(const TexturePointer& texture) override; GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; @@ -78,17 +146,25 @@ protected: void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) override; // Input Stage - void updateInput() override; void resetInputStage() override; + void updateInput() override; // Synchronize the state cache of this Backend with the actual real state of the GL Context void transferTransformState(const Batch& batch) const override; void initTransform() override; - void updateTransform(const Batch& batch); - void resetTransformStage(); + void updateTransform(const Batch& batch) override; + + // Resource Stage + bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) override; + void releaseResourceBuffer(uint32_t slot) override; // Output stage void do_blit(const Batch& batch, size_t paramOffset) override; + + std::string getBackendShaderHeader() const override; + void makeProgramBindings(ShaderObject& shaderObject) override; + int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override; + }; } } diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp index f6bdea45af..05bda34d7f 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp @@ -67,3 +67,27 @@ GLuint GLESBackend::getBufferID(const Buffer& buffer) { GLBuffer* GLESBackend::syncGPUObject(const Buffer& buffer) { return GLESBuffer::sync(*this, buffer); } + +bool GLESBackend::bindResourceBuffer(uint32_t slot, BufferPointer& buffer) { + GLBuffer* object = syncGPUObject((*buffer)); + if (object) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, object->_id); + + (void)CHECK_GL_ERROR(); + + _resource._buffers[slot] = buffer; + + return true; + } + + return false; +} + +void GLESBackend::releaseResourceBuffer(uint32_t slot) { + auto& buf = _resource._buffers[slot]; + if (buf) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, 0); + buf.reset(); + } +} + diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp index 0b7c525fbb..dc4025247e 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp @@ -53,10 +53,12 @@ public: GL_COLOR_ATTACHMENT15 }; int unit = 0; + auto backend = _backend.lock(); for (auto& b : _gpuObject.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } else { gltexture = nullptr; } @@ -81,9 +83,11 @@ public: } if (_gpuObject.getDepthStamp() != _depthStamp) { + auto backend = _backend.lock(); auto surface = _gpuObject.getDepthStencilBuffer(); if (_gpuObject.hasDepthStencil() && surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } if (gltexture) { @@ -99,8 +103,8 @@ public: if (!_colorBuffers.empty()) { glDrawBuffers((GLsizei)_colorBuffers.size(), _colorBuffers.data()); } else { - static const std::vector NO_BUFFERS{ GL_NONE }; - glDrawBuffers((GLsizei)NO_BUFFERS.size(), NO_BUFFERS.data()); + GLenum DrawBuffers[1] = {GL_NONE}; + glDrawBuffers(1, DrawBuffers); } // Now check for completness @@ -111,7 +115,7 @@ public: glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO); } - checkStatus(GL_DRAW_FRAMEBUFFER); + checkStatus(); } @@ -120,7 +124,7 @@ public: : Parent(backend, framebuffer, allocate()) { } }; -gl::GLFramebuffer* gpu::gles::GLESBackend::syncGPUObject(const Framebuffer& framebuffer) { +gl::GLFramebuffer* GLESBackend::syncGPUObject(const Framebuffer& framebuffer) { return GLESFramebuffer::sync(*this, framebuffer); } diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp new file mode 100644 index 0000000000..01a87978c2 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp @@ -0,0 +1,113 @@ +// +// Created by Sam Gateau on 2017/04/13 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" +#include "../gl/GLShader.h" + +using namespace gpu; +using namespace gpu::gl; +using namespace gpu::gles; + +// GLSL version +std::string GLESBackend::getBackendShaderHeader() const { + return Parent::getBackendShaderHeader(); +} + +int GLESBackend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { + GLint ssboCount = 0; + GLint uniformsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); + + for (int i = 0; i < uniformsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + GLint location = glGetUniformLocation(glprogram, name); + const GLint INVALID_UNIFORM_LOCATION = -1; + + // Try to make sense of the gltype + auto elementResource = getFormatFromGLUniform(type); + + // The uniform as a standard var type + if (location != INVALID_UNIFORM_LOCATION) { + + if (elementResource._resource == Resource::BUFFER) { + if (elementResource._element.getSemantic() == gpu::RESOURCE_BUFFER) { + // Let's make sure the name doesn't contains an array element + std::string sname(name); + auto foundBracket = sname.find_first_of('['); + if (foundBracket != std::string::npos) { + // std::string arrayname = sname.substr(0, foundBracket); + + if (sname[foundBracket + 1] == '0') { + sname = sname.substr(0, foundBracket); + } else { + // skip this uniform since it's not the first element of an array + continue; + } + } + + // For texture/Sampler, the location is the actual binding value + GLint binding = -1; + glGetUniformiv(glprogram, location, &binding); + + if (binding == GLESBackend::TRANSFORM_OBJECT_SLOT) { + continue; + } + + auto requestedBinding = slotBindings.find(std::string(sname)); + if (requestedBinding != slotBindings.end()) { + GLint requestedLoc = (*requestedBinding)._location + GLESBackend::RESOURCE_BUFFER_SLOT0_TEX_UNIT; + if (binding != requestedLoc) { + binding = requestedLoc; + } + } else { + binding += GLESBackend::RESOURCE_BUFFER_SLOT0_TEX_UNIT; + } + glProgramUniform1i(glprogram, location, binding); + + ssboCount++; + resourceBuffers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + } + } + } + } + + return ssboCount; +} + +void GLESBackend::makeProgramBindings(ShaderObject& shaderObject) { + if (!shaderObject.glprogram) { + return; + } + GLuint glprogram = shaderObject.glprogram; + GLint loc = -1; + + GLBackend::makeProgramBindings(shaderObject); + + // now assign the ubo binding, then DON't relink! + + //Check for gpu specific uniform slotBindings + loc = glGetUniformLocation(glprogram, "transformObjectBuffer"); + if (loc >= 0) { + glProgramUniform1i(glprogram, loc, GLESBackend::TRANSFORM_OBJECT_SLOT); + shaderObject.transformObjectSlot = GLESBackend::TRANSFORM_OBJECT_SLOT; + } + + loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT); + shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT; + } + + (void)CHECK_GL_ERROR(); +} + diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp index 31a98edd12..d2fa2aabab 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp @@ -12,41 +12,105 @@ #include #include -#include -// #include "../gl/GLTexelFormat.h" +#include "../gl/GLTexelFormat.h" using namespace gpu; using namespace gpu::gl; using namespace gpu::gles; -//using GL41TexelFormat = GLTexelFormat; +bool GLESBackend::supportedTextureFormat(const gpu::Element& format) { + // FIXME distinguish between GLES and GL compressed formats after support + // for the former is added to gpu::Element + return !format.isCompressed(); +} + +GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texturePointer) { + if (!texturePointer) { + return nullptr; + } + + const Texture& texture = *texturePointer; + if (TextureUsageType::EXTERNAL == texture.getUsageType()) { + return Parent::syncGPUObject(texturePointer); + } + + if (!texture.isDefined()) { + // NO texture definition yet so let's avoid thinking + return nullptr; + } + + // Check whether the texture is in a format we can deal with + auto format = texture.getTexelFormat(); + if (!supportedTextureFormat(format)) { + return nullptr; + } + + GLESTexture* object = Backend::getGPUObject(texture); + if (!object) { + switch (texture.getUsageType()) { + case TextureUsageType::RENDERBUFFER: + object = new GLESAttachmentTexture(shared_from_this(), texture); + break; + + case TextureUsageType::RESOURCE: +// FIXME disabling variable allocation textures for now, while debugging android rendering +// and crashes +#if 0 + qCDebug(gpugllogging) << "variable / Strict texture " << texture.source().c_str(); + object = new GLESResourceTexture(shared_from_this(), texture); + GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer); + break; +#endif + case TextureUsageType::STRICT_RESOURCE: + qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str(); + object = new GLESStrictResourceTexture(shared_from_this(), texture); + break; + + default: + Q_UNREACHABLE(); + } + } else { + if (texture.getUsageType() == TextureUsageType::RESOURCE) { + auto varTex = static_cast (object); + + if (varTex->_minAllocatedMip > 0) { + auto minAvailableMip = texture.minAvailableMipLevel(); + if (minAvailableMip < varTex->_minAllocatedMip) { + varTex->_minAllocatedMip = minAvailableMip; + GLESVariableAllocationTexture::_memoryPressureStateStale = true; + } + } + } + } + + return object; +} + using GLESTexture = GLESBackend::GLESTexture; -GLuint GLESTexture::allocate() { - //CLIMAX_MERGE_START - //Backend::incrementTextureGPUCount(); - //CLIMAX_MERGE_END +GLESTexture::GLESTexture(const std::weak_ptr& backend, const Texture& texture) + : GLTexture(backend, texture, allocate(texture)) { +} + +void GLESTexture::withPreservedTexture(std::function f) const { + glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_TEX_UNIT); + glBindTexture(_target, _texture); + (void)CHECK_GL_ERROR(); + + f(); + glBindTexture(_target, 0); + (void)CHECK_GL_ERROR(); +} + + +GLuint GLESTexture::allocate(const Texture& texture) { GLuint result; glGenTextures(1, &result); return result; } -GLuint GLESBackend::getTextureID(const TexturePointer& texture, bool transfer) { - return GLESTexture::getId(*this, texture, transfer); -} -GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texture, bool transfer) { - return GLESTexture::sync(*this, texture, transfer); -} - -GLESTexture::GLESTexture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) - : GLTexture(backend, texture, externalId) { -} - -GLESTexture::GLESTexture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) - : GLTexture(backend, texture, allocate(), transferrable) { -} void GLESTexture::generateMips() const { withPreservedTexture([&] { @@ -55,102 +119,88 @@ void GLESTexture::generateMips() const { (void)CHECK_GL_ERROR(); } -void GLESTexture::allocateStorage() const { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); - (void)CHECK_GL_ERROR(); - glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); - (void)CHECK_GL_ERROR(); -/* if (GLEW_VERSION_4_2 && !_gpuObject.getTexelFormat().isCompressed()) { - // Get the dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip); - glTexStorage2D(_target, usedMipLevels(), texelFormat.internalFormat, dimensions.x, dimensions.y); - (void)CHECK_GL_ERROR(); - } else {*/ - for (uint16_t l = _minMip; l <= _maxMip; l++) { - // Get the mip level dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(l); - for (GLenum target : getFaceTargets(_target)) { - glTexImage2D(target, l - _minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); - (void)CHECK_GL_ERROR(); - } - } - //} -} - -void GLESTexture::updateSize() const { - setSize(_virtualSize); - if (!_id) { - return; - } - - if (_gpuObject.getTexelFormat().isCompressed()) { - GLenum proxyType = GL_TEXTURE_2D; - GLuint numFaces = 1; - if (_gpuObject.getType() == gpu::Texture::TEX_CUBE) { - proxyType = CUBE_FACE_LAYOUT[0]; - numFaces = (GLuint)CUBE_NUM_FACES; - } - GLint gpuSize{ 0 }; - glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize); - (void)CHECK_GL_ERROR(); - - if (gpuSize) { - for (GLuint level = _minMip; level < _maxMip; level++) { - GLint levelSize{ 0 }; - //glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize); - //qDebug() << "TODO: GLBackendTexture.cpp:updateSize GL_TEXTURE_COMPRESSED_IMAGE_SIZE"; - levelSize *= numFaces; - - if (levelSize <= 0) { - break; - } - gpuSize += levelSize; - } - (void)CHECK_GL_ERROR(); - setSize(gpuSize); - return; - } - } -} - -// Move content bits from the CPU to the GPU for a given mip / face -void GLESTexture::transferMip(uint16_t mipLevel, uint8_t face) const { - auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - //GLenum target = getFaceTargets()[face]; - GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face]; - auto size = _gpuObject.evalMipDimensions(mipLevel); - glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - (void)CHECK_GL_ERROR(); -} - -void GLESTexture::startTransfer() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - Parent::startTransfer(); - - glBindTexture(_target, _id); - (void)CHECK_GL_ERROR(); - - // transfer pixels from each faces - uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1; - for (uint8_t f = 0; f < numFaces; f++) { - for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - if (_gpuObject.isStoredMipFaceAvailable(i, f)) { - transferMip(i, f); - } - } +bool isCompressedFormat(GLenum internalFormat) { + switch (internalFormat) { + case GL_COMPRESSED_R11_EAC: + case GL_COMPRESSED_SIGNED_R11_EAC: + case GL_COMPRESSED_RG11_EAC: + case GL_COMPRESSED_SIGNED_RG11_EAC: + case GL_COMPRESSED_RGB8_ETC2: + case GL_COMPRESSED_SRGB8_ETC2: + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_RGBA8_ETC2_EAC: + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + case GL_COMPRESSED_RGBA_ASTC_4x4: + case GL_COMPRESSED_RGBA_ASTC_5x4: + case GL_COMPRESSED_RGBA_ASTC_5x5: + case GL_COMPRESSED_RGBA_ASTC_6x5: + case GL_COMPRESSED_RGBA_ASTC_6x6: + case GL_COMPRESSED_RGBA_ASTC_8x5: + case GL_COMPRESSED_RGBA_ASTC_8x6: + case GL_COMPRESSED_RGBA_ASTC_8x8: + case GL_COMPRESSED_RGBA_ASTC_10x5: + case GL_COMPRESSED_RGBA_ASTC_10x6: + case GL_COMPRESSED_RGBA_ASTC_10x8: + case GL_COMPRESSED_RGBA_ASTC_10x10: + case GL_COMPRESSED_RGBA_ASTC_12x10: + case GL_COMPRESSED_RGBA_ASTC_12x12: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12: + return true; + default: + return false; } } -void GLESBackend::GLESTexture::syncSampler() const { + +Size GLESTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const { + Size amountCopied = sourceSize; + if (GL_TEXTURE_2D == _target) { + if (isCompressedFormat(internalFormat)) { + glCompressedTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, internalFormat, + static_cast(sourceSize), sourcePointer); + } else { + glTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); + } + } else if (GL_TEXTURE_CUBE_MAP == _target) { + auto target = GLTexture::CUBE_FACE_LAYOUT[face]; + if (isCompressedFormat(internalFormat)) { + glCompressedTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, internalFormat, + static_cast(sourceSize), sourcePointer); + } else { + glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); + } + } else { + // TODO: implement for android + assert(false); + amountCopied = 0; + } + (void)CHECK_GL_ERROR(); + return amountCopied; +} + +void GLESTexture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); + const auto& fm = FILTER_MODES[sampler.getFilter()]; glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter); glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, fm.magFilter); if (sampler.doComparison()) { - glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); // GL_COMPARE_R_TO_TEXTURE glTexParameteri(_target, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); } else { glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_NONE); @@ -160,17 +210,452 @@ void GLESBackend::GLESTexture::syncSampler() const { glTexParameteri(_target, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); glTexParameteri(_target, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); - glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR_EXT, (const float*)&sampler.getBorderColor()); - + glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); + //glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, (uint16)sampler.getMipOffset()); glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); + (void)CHECK_GL_ERROR(); - //qDebug() << "[GPU-GL-GLBackend] syncSampler 12 " << _target << "," << sampler.getMaxAnisotropy(); - //glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); - //(void)CHECK_GL_ERROR(); - //qDebug() << "[GPU-GL-GLBackend] syncSampler end"; +} + +using GLESFixedAllocationTexture = GLESBackend::GLESFixedAllocationTexture; + +GLESFixedAllocationTexture::GLESFixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GLESTexture(backend, texture), _size(texture.evalTotalSize()) { + withPreservedTexture([&] { + allocateStorage(); + syncSampler(); + }); +} + +GLESFixedAllocationTexture::~GLESFixedAllocationTexture() { +} + +void GLESFixedAllocationTexture::allocateStorage() const { + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto numMips = _gpuObject.getNumMips(); + + // glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); + for (GLint level = 0; level < numMips; level++) { + Vec3u dimensions = _gpuObject.evalMipDimensions(level); + for (GLenum target : getFaceTargets(_target)) { + glTexImage2D(target, level, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, nullptr); + } + } + + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, numMips - 1); + (void)CHECK_GL_ERROR(); +} + +void GLESFixedAllocationTexture::syncSampler() const { + Parent::syncSampler(); + const Sampler& sampler = _gpuObject.getSampler(); + auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); + + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, baseMip); + glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); + glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip())); +} + +// Renderbuffer attachment textures +using GLESAttachmentTexture = GLESBackend::GLESAttachmentTexture; + +GLESAttachmentTexture::GLESAttachmentTexture(const std::weak_ptr& backend, const Texture& texture) : GLESFixedAllocationTexture(backend, texture) { + Backend::textureFramebufferCount.increment(); + Backend::textureFramebufferGPUMemSize.update(0, size()); +} + +GLESAttachmentTexture::~GLESAttachmentTexture() { + Backend::textureFramebufferCount.decrement(); + Backend::textureFramebufferGPUMemSize.update(size(), 0); +} + +// Strict resource textures +using GLESStrictResourceTexture = GLESBackend::GLESStrictResourceTexture; + +GLESStrictResourceTexture::GLESStrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GLESFixedAllocationTexture(backend, texture) { + Backend::textureResidentCount.increment(); + Backend::textureResidentGPUMemSize.update(0, size()); + + withPreservedTexture([&] { + + auto mipLevels = _gpuObject.getNumMips(); + for (uint16_t sourceMip = 0; sourceMip < mipLevels; sourceMip++) { + uint16_t targetMip = sourceMip; + size_t maxFace = GLTexture::getFaceCount(_target); + for (uint8_t face = 0; face < maxFace; face++) { + copyMipFaceFromTexture(sourceMip, targetMip, face); + } + } + }); + + if (texture.isAutogenerateMips()) { + generateMips(); + } +} + +GLESStrictResourceTexture::~GLESStrictResourceTexture() { + Backend::textureResidentCount.decrement(); + Backend::textureResidentGPUMemSize.update(size(), 0); +} + +using GLESVariableAllocationTexture = GLESBackend::GLESVariableAllocationTexture; + +GLESVariableAllocationTexture::GLESVariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : + GLESTexture(backend, texture) +{ + Backend::textureResourceCount.increment(); + + auto mipLevels = texture.getNumMips(); + _allocatedMip = mipLevels; + _maxAllocatedMip = _populatedMip = mipLevels; + _minAllocatedMip = texture.minAvailableMipLevel(); + + uvec3 mipDimensions; + for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { + _maxAllocatedMip = _populatedMip = mip; + break; + } + } + + auto targetMip = _populatedMip - std::min(_populatedMip, 2); + uint16_t allocatedMip = std::max(_minAllocatedMip, targetMip); + + allocateStorage(allocatedMip); + _memoryPressureStateStale = true; + copyMipsFromTexture(); + + syncSampler(); +} + +GLESVariableAllocationTexture::~GLESVariableAllocationTexture() { + Backend::textureResourceCount.decrement(); + Backend::textureResourceGPUMemSize.update(_size, 0); + Backend::textureResourcePopulatedGPUMemSize.update(_populatedSize, 0); +} + +void GLESVariableAllocationTexture::allocateStorage(uint16 allocatedMip) { + _allocatedMip = allocatedMip; + + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto dimensions = _gpuObject.evalMipDimensions(_allocatedMip); + const auto totalMips = _gpuObject.getNumMips(); + const auto mips = totalMips - _allocatedMip; + withPreservedTexture([&] { + // FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension + glTexStorage2D(_target, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); CHECK_GL_ERROR(); + }); + auto mipLevels = _gpuObject.getNumMips(); + _size = 0; + for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) { + _size += _gpuObject.evalMipSize(mip); + } + Backend::textureResourceGPUMemSize.update(0, _size); } +Size GLESVariableAllocationTexture::copyMipsFromTexture() { + auto mipLevels = _gpuObject.getNumMips(); + size_t maxFace = GLTexture::getFaceCount(_target); + Size amount = 0; + for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) { + uint16_t targetMip = sourceMip - _allocatedMip; + for (uint8_t face = 0; face < maxFace; ++face) { + amount += copyMipFaceFromTexture(sourceMip, targetMip, face); + } + } + + + return amount; +} + +Size GLESVariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const { + Size amountCopied = 0; + withPreservedTexture([&] { + amountCopied = Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, internalFormat, format, type, sourceSize, sourcePointer); + }); + incrementPopulatedSize(amountCopied); + return amountCopied; +} + +void GLESVariableAllocationTexture::syncSampler() const { + withPreservedTexture([&] { + Parent::syncSampler(); + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); + }); +} + + +void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { + // DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT + + GLuint fbo { 0 }; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + + uint16_t mips = numMips; + // copy pre-existing mips + for (uint16_t mip = populatedMips; mip < mips; ++mip) { + auto mipDimensions = texture.evalMipDimensions(mip); + uint16_t targetMip = mip - destMipOffset; + uint16_t sourceMip = mip - srcMipOffset; + for (GLenum target : GLTexture::getFaceTargets(texTarget)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, srcId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + + // destroy the transfer framebuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); +} + +void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { + // DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT + + struct MipDesc { + GLint _faceSize; + GLint _size; + GLint _offset; + GLint _width; + GLint _height; + }; + std::vector sourceMips(numMips); + + std::vector bytes; + + glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); + glBindTexture(texTarget, srcId); + const auto& faceTargets = GLTexture::getFaceTargets(texTarget); + GLint internalFormat { 0 }; + + // Collect the mip description from the source texture + GLint bufferOffset { 0 }; + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t sourceLevel = mip - srcMipOffset; + + // Grab internal format once + if (internalFormat == 0) { + glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); + } + + // Collect the size of the first face, and then compute the total size offset needed for this mip level + auto mipDimensions = texture.evalMipDimensions(mip); + sourceMip._width = mipDimensions.x; + sourceMip._height = mipDimensions.y; +#ifdef DEBUG_COPY + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width); + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height); +#endif + // TODO: retrieve the size of a compressed image + assert(false); + //glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); + sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize; + sourceMip._offset = bufferOffset; + bufferOffset += sourceMip._size; + gpu::gl::checkGLError(); + } + (void)CHECK_GL_ERROR(); + + // Allocate the PBO to accomodate for all the mips to copy + GLuint pbo { 0 }; + glGenBuffers(1, &pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); + glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STATIC_COPY); + (void)CHECK_GL_ERROR(); + + // Transfer from source texture to pbo + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t sourceLevel = mip - srcMipOffset; + + for (GLint f = 0; f < (GLint)faceTargets.size(); f++) { + // TODO: implement for android + //glGetCompressedTexImage(faceTargets[f], sourceLevel, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); + } + (void)CHECK_GL_ERROR(); + } + + // Now populate the new texture from the pbo + glBindTexture(texTarget, 0); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); + + glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_TEX_UNIT); + + // Transfer from pbo to new texture + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t destLevel = mip - destMipOffset; + + for (GLint f = 0; f < (GLint)faceTargets.size(); f++) { +#ifdef DEBUG_COPY + GLint destWidth, destHeight, destSize; + glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth); + glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_HEIGHT, &destHeight); + glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize); +#endif + glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat, + sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); + gpu::gl::checkGLError(); + } + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glDeleteBuffers(1, &pbo); +} + +void GLESVariableAllocationTexture::copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { + uint16_t numMips = _gpuObject.getNumMips(); + withPreservedTexture([&] { + if (_texelFormat.isCompressed()) { + copyCompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips); + } else { + copyUncompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips); + } + }); +} + +void GLESVariableAllocationTexture::promote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Q_ASSERT(_allocatedMip > 0); + + uint16_t targetAllocatedMip = _allocatedMip - std::min(_allocatedMip, 2); + targetAllocatedMip = std::max(_minAllocatedMip, targetAllocatedMip); + + GLuint oldId = _id; + auto oldSize = _size; + uint16_t oldAllocatedMip = _allocatedMip; + + // create new texture + const_cast(_id) = allocate(_gpuObject); + + // allocate storage for new level + allocateStorage(targetAllocatedMip); + + // copy pre-existing mips + copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip); + + // destroy the old texture + glDeleteTextures(1, &oldId); + + // Update sampler + syncSampler(); + + // update the memory usage + Backend::textureResourceGPUMemSize.update(oldSize, 0); + // no change to Backend::textureResourcePopulatedGPUMemSize + + populateTransferQueue(); +} + +void GLESVariableAllocationTexture::demote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Q_ASSERT(_allocatedMip < _maxAllocatedMip); + auto oldId = _id; + auto oldSize = _size; + auto oldPopulatedMip = _populatedMip; + + // allocate new texture + const_cast(_id) = allocate(_gpuObject); + uint16_t oldAllocatedMip = _allocatedMip; + allocateStorage(_allocatedMip + 1); + _populatedMip = std::max(_populatedMip, _allocatedMip); + + + // copy pre-existing mips + copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip); + + // destroy the old texture + glDeleteTextures(1, &oldId); + + // Update sampler + syncSampler(); + + // update the memory usage + Backend::textureResourceGPUMemSize.update(oldSize, 0); + // Demoting unpopulate the memory delta + if (oldPopulatedMip != _populatedMip) { + auto numPopulatedDemoted = _populatedMip - oldPopulatedMip; + Size amountUnpopulated = 0; + for (int i = 0; i < numPopulatedDemoted; i++) { + amountUnpopulated += _gpuObject.evalMipSize(oldPopulatedMip + i); + } + decrementPopulatedSize(amountUnpopulated); + } + populateTransferQueue(); +} + + +void GLESVariableAllocationTexture::populateTransferQueue() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + if (_populatedMip <= _allocatedMip) { + return; + } + _pendingTransfers = TransferQueue(); + + const uint8_t maxFace = GLTexture::getFaceCount(_target); + uint16_t sourceMip = _populatedMip; + do { + --sourceMip; + auto targetMip = sourceMip - _allocatedMip; + auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); + for (uint8_t face = 0; face < maxFace; ++face) { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { + continue; + } + + // If the mip is less than the max transfer size, then just do it in one transfer + if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { + // Can the mip be transferred in one go + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); + continue; + } + + // break down the transfers into chunks so that no single transfer is + // consuming more than X bandwidth + // For compressed format, regions must be a multiple of the 4x4 tiles, so enforce 4 lines as the minimum block + auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); + const auto lines = mipDimensions.y; + const uint32_t BLOCK_NUM_LINES { 4 }; + const auto numBlocks = (lines + (BLOCK_NUM_LINES - 1)) / BLOCK_NUM_LINES; + auto bytesPerBlock = mipSize / numBlocks; + Q_ASSERT(0 == (mipSize % lines)); + uint32_t linesPerTransfer = BLOCK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerBlock); + uint32_t lineOffset = 0; + while (lineOffset < lines) { + uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); + lineOffset += linesToCopy; + } + } + + // queue up the sampler and populated mip change for after the transfer has completed + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); + } while (sourceMip != _allocatedMip); +} + +// resource textures +using GLESResourceTexture = GLESBackend::GLESResourceTexture; + +GLESResourceTexture::GLESResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GLESVariableAllocationTexture(backend, texture) { + if (texture.isAutogenerateMips()) { + generateMips(); + } +} + +GLESResourceTexture::~GLESResourceTexture() { +} + + diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp index 5050db6edd..7d33ca822d 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp @@ -38,9 +38,9 @@ void GLESBackend::transferTransformState(const Batch& batch) const { } if (!batch._objects.empty()) { - glBindBuffer(GL_SHADER_STORAGE_BUFFER, _transform._objectBuffer); - glBufferData(GL_SHADER_STORAGE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_STREAM_DRAW); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + glBindBuffer(GL_TEXTURE_BUFFER, _transform._objectBuffer); + glBufferData(GL_TEXTURE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_TEXTURE_BUFFER, 0); } if (!batch._namedData.empty()) { @@ -58,10 +58,44 @@ void GLESBackend::transferTransformState(const Batch& batch) const { glBindBuffer(GL_ARRAY_BUFFER, 0); } - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._objectBuffer); + glActiveTexture(GL_TEXTURE0 + GLESBackend::TRANSFORM_OBJECT_SLOT); + glBindTexture(GL_TEXTURE_BUFFER, _transform._objectBufferTexture); + if (!batch._objects.empty()) { + glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, _transform._objectBuffer); + } CHECK_GL_ERROR(); // Make sure the current Camera offset is unknown before render Draw _transform._currentCameraOffset = INVALID_OFFSET; } + + +void GLESBackend::updateTransform(const Batch& batch) { + _transform.update(_commandIndex, _stereo); + + auto& drawCallInfoBuffer = batch.getDrawCallInfoBuffer(); + if (batch._currentNamedCall.empty()) { + auto& drawCallInfo = drawCallInfoBuffer[_currentDraw]; + if (_transform._enabledDrawcallInfoBuffer) { + glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is disabled + _transform._enabledDrawcallInfoBuffer = false; + } + glVertexAttribI4i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused, 0, 0); + //glVertexAttribI2i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused); + } else { + if (!_transform._enabledDrawcallInfoBuffer) { + glEnableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is enabled +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, (isStereo() ? 2 : 1)); +#else + glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, 1); +#endif + _transform._enabledDrawcallInfoBuffer = true; + } + glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer); + glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_UNSIGNED_SHORT, 0, _transform._drawCallInfoOffsets[batch._currentNamedCall]); + } + + (void)CHECK_GL_ERROR(); +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index fca220c224..79f08ea5cc 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -269,6 +269,10 @@ public: void _glColor4f(float red, float green, float blue, float alpha); + // Maybe useful but shoudln't be public. Please convince me otherwise + // Well porting to gles i need it... + void runLambda(std::function f); + enum Command { COMMAND_draw = 0, COMMAND_drawIndexed, @@ -497,8 +501,7 @@ protected: void startNamedCall(const std::string& name); void stopNamedCall(); - // Maybe useful but shoudln't be public. Please convince me otherwise - void runLambda(std::function f); + void captureDrawCallInfoImpl(); }; diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index b3cf0fbba3..f470cc8aa9 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -134,7 +134,7 @@ public: float getAspectRatio() const { return getWidth() / (float) getHeight() ; } -#ifndef ANDROID +#if !defined(Q_OS_ANDROID) static const uint32 MAX_NUM_RENDER_BUFFERS = 8; #else static const uint32 MAX_NUM_RENDER_BUFFERS = 4; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 06208179e0..ad3dc5fada 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -539,7 +539,7 @@ public: static TexturePointer build(const ktx::KTXDescriptor& descriptor); static TexturePointer unserialize(const std::string& ktxFile); - static TexturePointer unserialize(const cache::FilePointer& cacheEntry); + static TexturePointer unserialize(const cache::FilePointer& cacheEntry, const std::string& source = std::string()); static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 883d9abf15..754bdca445 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -499,7 +499,9 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { GPUKTXPayload gpuktxKeyValue; if (!GPUKTXPayload::findInKeyValues(descriptor.keyValues, gpuktxKeyValue)) { qCWarning(gpulogging) << "Could not find GPUKTX key values."; - return TexturePointer(); + // FIXME use sensible defaults based on the texture type and format + gpuktxKeyValue._usageType = TextureUsageType::RESOURCE; + gpuktxKeyValue._usage = Texture::Usage::Builder().withColor().withAlpha().build(); } auto texture = create(gpuktxKeyValue._usageType, @@ -525,7 +527,7 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { return texture; } -TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry) { +TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry, const std::string& source) { std::unique_ptr ktxPointer = ktx::KTX::create(std::make_shared(cacheEntry->getFilepath().c_str())); if (!ktxPointer) { return nullptr; @@ -534,6 +536,9 @@ TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry) { auto texture = build(ktxPointer->toDescriptor()); if (texture) { texture->setKtxBacking(cacheEntry); + if (texture->source().empty()) { + texture->setSource(source); + } } return texture; @@ -548,6 +553,7 @@ TexturePointer Texture::unserialize(const std::string& ktxfile) { auto texture = build(ktxPointer->toDescriptor()); if (texture) { texture->setKtxBacking(ktxfile); + texture->setSource(ktxfile); } return texture; diff --git a/libraries/graphics/src/graphics/Light.slh b/libraries/graphics/src/graphics/Light.slh index 6b24f89c3c..e1202ed6a0 100644 --- a/libraries/graphics/src/graphics/Light.slh +++ b/libraries/graphics/src/graphics/Light.slh @@ -56,10 +56,10 @@ Light getLight(int index) { } <@else@> -uniform lightBuffer { +uniform keyLightBuffer { Light light; }; -Light getLight() { +Light getKeyLight() { return light; } diff --git a/libraries/graphics/src/graphics/Skybox.cpp b/libraries/graphics/src/graphics/Skybox.cpp index 6fb7226089..e05a66104a 100755 --- a/libraries/graphics/src/graphics/Skybox.cpp +++ b/libraries/graphics/src/graphics/Skybox.cpp @@ -89,12 +89,14 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky auto skyFS = gpu::Shader::createPixel(std::string(skybox_frag)); auto skyShader = gpu::Shader::createProgram(skyVS, skyFS); - gpu::Shader::BindingSet bindings; - bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), SKYBOX_SKYMAP_SLOT)); - bindings.insert(gpu::Shader::Binding(std::string("skyboxBuffer"), SKYBOX_CONSTANTS_SLOT)); - if (!gpu::Shader::makeProgram(*skyShader, bindings)) { + batch.runLambda([skyShader] { + gpu::Shader::BindingSet bindings; + bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), SKYBOX_SKYMAP_SLOT)); + bindings.insert(gpu::Shader::Binding(std::string("skyboxBuffer"), SKYBOX_CONSTANTS_SLOT)); + if (!gpu::Shader::makeProgram(*skyShader, bindings)) { - } + } + }); auto skyState = std::make_shared(); // Must match PrepareStencil::STENCIL_BACKGROUND diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 26edd93b8b..9115dd09e2 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -29,7 +29,7 @@ using namespace gpu; #if defined(Q_OS_ANDROID) -#define CPU_MIPMAPS 0 +#define CPU_MIPMAPS 1 #else #define CPU_MIPMAPS 1 #include @@ -49,6 +49,9 @@ static std::atomic compressGrayscaleTextures { false }; static std::atomic compressCubeTextures { false }; uint rectifyDimension(const uint& dimension) { + if (dimension == 0) { + return 0; + } if (dimension < SPARSE_PAGE_SIZE.x) { uint newSize = SPARSE_PAGE_SIZE.x; while (dimension <= newSize / 2) { @@ -676,6 +679,7 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic& abortProcessing = false, int face = -1) { #if CPU_MIPMAPS +#if !defined(Q_OS_ANDROID) PROFILE_RANGE(resource_parse, "generateMips"); if (image.format() == QIMAGE_HDR_FORMAT) { @@ -683,6 +687,16 @@ void generateMips(gpu::Texture* texture, QImage&& image, const std::atomic } else { generateLDRMips(texture, std::move(image), abortProcessing, face); } + +#else + //texture->setAutoGenerateMips(false); + texture->assignStoredMip(0, image.byteCount(), image.constBits()); + for (uint16 level = 1; level < texture->getNumMips(); ++level) { + QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); + QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + texture->assignStoredMip(level, mipImage.byteCount(), mipImage.constBits()); + } +#endif #else texture->setAutoGenerateMips(true); #endif @@ -723,9 +737,15 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma bool validAlpha = image.hasAlphaChannel(); bool alphaAsMask = false; +#if !defined(Q_OS_ANDROID) if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } +#else + if (image.format() != QImage::Format_RGBA8888) { + image = image.convertToFormat(QImage::Format_RGBA8888); + } +#endif if (validAlpha) { processTextureAlpha(image, validAlpha, alphaAsMask); @@ -765,6 +785,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma } theTexture->setUsage(usage.build()); theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); generateMips(theTexture.get(), std::move(image), abortProcessing); } @@ -871,6 +892,7 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); generateMips(theTexture.get(), std::move(image), abortProcessing); } @@ -907,6 +929,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); generateMips(theTexture.get(), std::move(image), abortProcessing); } diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 69c35c4a20..1f190111f2 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -163,7 +163,7 @@ void Midi::sendRawMessage(int device, int raw) { void Midi::sendMessage(int device, int channel, int type, int note, int velocity) { int message = (channel - 1) | (type << MIDI_SHIFT_STATUS); if (broadcastEnabled) { - for (int i = 0; i < midihout.size(); i++) { + for (int i = 1; i < midihout.size(); i++) { // Skip 0 (Microsoft GS Wavetable Synth) if (midihout[i] != NULL) { midiOutShortMsg(midihout[i], message | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY)); } @@ -174,9 +174,9 @@ void Midi::sendMessage(int device, int channel, int type, int note, int velocity } void Midi::sendNote(int status, int note, int velocity) { - for (int i = 0; i < midihout.size(); i++) { + for (int i = 1; i < midihout.size(); i++) { // Skip 0 (Microsoft GS Wavetable Synth) if (midihout[i] != NULL) { - midiOutShortMsg(midihout[i], status + (note << MIDI_SHIFT_NOTE) + (velocity << MIDI_SHIFT_VELOCITY)); + midiOutShortMsg(midihout[i], status | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY)); } } } @@ -283,9 +283,6 @@ void Midi::midiHardwareChange() { Midi::Midi() { instance = this; -#if defined Q_OS_WIN32 - midiOutExclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing (Lags) -#endif MidiSetup(); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 8153dba3a5..fb75123b43 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -50,8 +50,10 @@ Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image") Q_LOGGING_CATEGORY(trace_resource_parse_image_raw, "trace.resource.parse.image.raw") Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.ktx") +#if !defined(DISABLE_KTX_CACHE) const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; const std::string TextureCache::KTX_EXT { "ktx" }; +#endif static const QString RESOURCE_SCHEME = "resource"; static const QUrl SPECTATOR_CAMERA_FRAME_URL("resource://spectatorCameraFrame"); @@ -61,7 +63,9 @@ static const float SKYBOX_LOAD_PRIORITY { 10.0f }; // Make sure skybox loads fir static const float HIGH_MIPS_LOAD_PRIORITY { 9.0f }; // Make sure high mips loads after skybox but before models TextureCache::TextureCache() { +#if !defined(DISABLE_KTX_CACHE) _ktxCache->initialize(); +#endif setUnusedResourceCacheSize(0); setObjectName("TextureCache"); } @@ -261,8 +265,12 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { /// Returns a texture version of an image file gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) { QImage image = QImage(path); + if (image.isNull()) { + qCWarning(networking) << "Unable to load required resource texture" << path; + return nullptr; + } auto loader = image::TextureUsage::getTextureLoaderForType(type, options); - return gpu::TexturePointer(loader(std::move(image), QUrl::fromLocalFile(path).fileName().toStdString(), false)); + return gpu::TexturePointer(loader(std::move(image), path.toStdString(), false)); } QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, @@ -742,16 +750,20 @@ void NetworkTexture::handleFinishedInitialLoad() { gpu::TexturePointer texture = textureCache->getTextureByHash(hash); +#if !defined(DISABLE_KTX_CACHE) if (!texture) { auto ktxFile = textureCache->_ktxCache->getFile(hash); if (ktxFile) { texture = gpu::Texture::unserialize(ktxFile); if (texture) { texture = textureCache->cacheTextureByHash(hash, texture); + if (texture->source().empty()) { + texture->setSource(url.toString().toStdString()); + } } } } - +#endif if (!texture) { auto memKtx = ktx::KTX::createBare(*header, keyValues); @@ -766,6 +778,7 @@ void NetworkTexture::handleFinishedInitialLoad() { // Move ktx to file const char* data = reinterpret_cast(memKtx->_storage->data()); +#if !defined(DISABLE_KTX_CACHE) size_t length = memKtx->_storage->size(); cache::FilePointer file; auto& ktxCache = textureCache->_ktxCache; @@ -777,11 +790,14 @@ void NetworkTexture::handleFinishedInitialLoad() { Q_ARG(int, 0)); return; } +#endif auto newKtxDescriptor = memKtx->toDescriptor(); texture = gpu::Texture::build(newKtxDescriptor); +#if !defined(DISABLE_KTX_CACHE) texture->setKtxBacking(file); +#endif texture->setSource(filename); auto& images = originalKtxDescriptor->images; @@ -924,11 +940,12 @@ void ImageReader::read() { // If we already have a live texture with the same hash, use it auto texture = textureCache->getTextureByHash(hash); +#if !defined(DISABLE_KTX_CACHE) // If there is no live texture, check if there's an existing KTX file if (!texture) { auto ktxFile = textureCache->_ktxCache->getFile(hash); if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile); + texture = gpu::Texture::unserialize(ktxFile, _url.toString().toStdString()); if (texture) { texture = textureCache->cacheTextureByHash(hash, texture); } else { @@ -936,6 +953,7 @@ void ImageReader::read() { } } } +#endif // If we found the texture either because it's in use or via KTX deserialization, // set the image and return immediately. @@ -971,6 +989,7 @@ void ImageReader::read() { // Save the image into a KTXFile if (texture && textureCache) { +#if !defined(DISABLE_KTX_CACHE) auto memKtx = gpu::Texture::serialize(*texture); // Move the texture into a memory mapped file @@ -987,7 +1006,7 @@ void ImageReader::read() { } else { qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; } - +#endif // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 742d003d02..74395d8948 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -194,10 +194,12 @@ private: TextureCache(); virtual ~TextureCache(); +#if !defined(DISABLE_KTX_CACHE) static const std::string KTX_DIRNAME; static const std::string KTX_EXT; std::shared_ptr _ktxCache { std::make_shared(KTX_DIRNAME, KTX_EXT) }; +#endif // Map from image hashes to texture weak pointers std::unordered_map> _texturesByHashes; std::mutex _texturesByHashesMutex; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index e317f0f250..fa5507fcf7 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -29,15 +29,11 @@ #include "UserActivityLogger.h" #include "udt/PacketHeaders.h" -#ifdef Q_OS_ANDROID -const QString DEFAULT_HIFI_ADDRESS = "hifi://android/0.0,0.0,-200"; -#else #if USE_STABLE_GLOBAL_SERVICES const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome/hello"; #else const QString DEFAULT_HIFI_ADDRESS = "hifi://dev-welcome/hello"; #endif -#endif const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 7302e0e997..0173a1fad7 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -73,6 +73,7 @@ public: * Get Interface's protocol version. * @function location.protocolVersion * @returns {string} A string uniquely identifying the version of the metaverse protocol that Interface is using. + * @deprecated This function is deprecated and will be removed. Use {@link Window.protocolSignature} instead. */ Q_INVOKABLE QString protocolVersion(); diff --git a/libraries/networking/src/FileResourceRequest.cpp b/libraries/networking/src/FileResourceRequest.cpp index dfff21dae6..b55f1839a0 100644 --- a/libraries/networking/src/FileResourceRequest.cpp +++ b/libraries/networking/src/FileResourceRequest.cpp @@ -11,24 +11,37 @@ #include "FileResourceRequest.h" -#include - -#include +#include +#include +#include #include +#include + +#include "ResourceManager.h" void FileResourceRequest::doSend() { auto statTracker = DependencyManager::get(); statTracker->incrementStat(STAT_FILE_REQUEST_STARTED); int fileSize = 0; - QString filename = _url.toLocalFile(); - - // sometimes on windows, we see the toLocalFile() return null, - // in this case we will attempt to simply use the url as a string - if (filename.isEmpty()) { - filename = _url.toString(); + QString filename; + if (_url.scheme() == URL_SCHEME_QRC) { + filename = ":/" + _url.path(); + } else { + filename = _url.toLocalFile(); + // sometimes on windows, we see the toLocalFile() return null, + // in this case we will attempt to simply use the url as a string + if (filename.isEmpty()) { + filename = _url.toString(); + } } + + // Allow platform specific versions of files loaded out of a resource cache via file:// + QFileSelector fileSelector; + fileSelector.setExtraSelectors(FileUtils::getFileSelectors()); + filename = fileSelector.select(filename); + if (!_byteRange.isValid()) { _result = ResourceRequest::InvalidByteRange; } else { diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index 3ee66f89c1..17dcd9728d 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -11,6 +11,8 @@ #include "ResourceManager.h" +#include + #include #include #include @@ -24,7 +26,6 @@ #include "NetworkAccessManager.h" #include "NetworkLogging.h" - ResourceManager::ResourceManager() { _thread.setObjectName("Resource Manager Thread"); @@ -53,7 +54,7 @@ QString ResourceManager::normalizeURL(const QString& urlString) { copy = _prefixMap; } - foreach(const auto& entry, copy) { + foreach (const auto& entry, copy) { const auto& prefix = entry.first; const auto& replacement = entry.second; if (result.startsWith(prefix)) { @@ -64,13 +65,24 @@ QString ResourceManager::normalizeURL(const QString& urlString) { return result; } +const QSet& getKnownUrls() { + static QSet knownUrls; + static std::once_flag once; + std::call_once(once, [] { + knownUrls.insert(URL_SCHEME_QRC); + knownUrls.insert(URL_SCHEME_FILE); + knownUrls.insert(URL_SCHEME_HTTP); + knownUrls.insert(URL_SCHEME_HTTPS); + knownUrls.insert(URL_SCHEME_FTP); + knownUrls.insert(URL_SCHEME_ATP); + }); + return knownUrls; +} + QUrl ResourceManager::normalizeURL(const QUrl& originalUrl) { QUrl url = QUrl(normalizeURL(originalUrl.toString())); auto scheme = url.scheme(); - if (!(scheme == URL_SCHEME_FILE || - scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP || - scheme == URL_SCHEME_ATP)) { - + if (!getKnownUrls().contains(scheme)) { // check the degenerative file case: on windows we can often have urls of the form c:/filename // this checks for and works around that case. QUrl urlWithFileScheme{ URL_SCHEME_FILE + ":///" + url.toString() }; @@ -94,7 +106,7 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q ResourceRequest* request = nullptr; - if (scheme == URL_SCHEME_FILE) { + if (scheme == URL_SCHEME_FILE || scheme == URL_SCHEME_QRC) { request = new FileResourceRequest(normalizedURL); } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { request = new HTTPResourceRequest(normalizedURL); @@ -113,15 +125,14 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q return request; } - bool ResourceManager::resourceExists(const QUrl& url) { auto scheme = url.scheme(); if (scheme == URL_SCHEME_FILE) { - QFileInfo file { url.toString() }; + QFileInfo file{ url.toString() }; return file.exists(); } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { auto& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest request { url }; + QNetworkRequest request{ url }; request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); @@ -159,4 +170,3 @@ bool ResourceManager::resourceExists(const QUrl& url) { qCDebug(networking) << "Unknown scheme (" << scheme << ") for URL: " << url.url(); return false; } - diff --git a/libraries/networking/src/ResourceManager.h b/libraries/networking/src/ResourceManager.h index fdfd05736e..5728a7bd32 100644 --- a/libraries/networking/src/ResourceManager.h +++ b/libraries/networking/src/ResourceManager.h @@ -22,6 +22,7 @@ #include "ResourceRequest.h" +const QString URL_SCHEME_QRC = "qrc"; const QString URL_SCHEME_FILE = "file"; const QString URL_SCHEME_HTTP = "http"; const QString URL_SCHEME_HTTPS = "https"; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 711aeb2cc2..c48c6bfc0b 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::UpdatedMannequinDefaultAvatar); + return static_cast(AvatarMixerPacketVersion::AvatarJointDefaultPoseFlags); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); case PacketType::ICEServerHeartbeat: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 458666571c..0f69691bd5 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -247,7 +247,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { AvatarIdentitySequenceFront, IsReplicatedInAvatarIdentity, AvatarIdentityLookAtSnapping, - UpdatedMannequinDefaultAvatar + UpdatedMannequinDefaultAvatar, + AvatarJointDefaultPoseFlags }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 33ac887f4f..420da5a1e0 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -256,25 +256,32 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { assert(_entity); assert(entityTreeIsLocked()); measureBodyAcceleration(); - bool positionSuccess; - _entity->setWorldPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false); - if (!positionSuccess) { - static QString repeatedMessage = - LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform " - "setPosition failed.*"); - qCDebug(physics) << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID(); + + // If transform or velocities are flagged as dirty it means a network or scripted change + // occured between the beginning and end of the stepSimulation() and we DON'T want to apply + // these physics simulation results. + uint32_t flags = _entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES); + if (!flags) { + // flags are clear + _entity->setWorldTransform(bulletToGLM(worldTrans.getOrigin()), bulletToGLM(worldTrans.getRotation())); + _entity->setWorldVelocity(getBodyLinearVelocity()); + _entity->setWorldAngularVelocity(getBodyAngularVelocity()); + _entity->setLastSimulated(usecTimestampNow()); + } else { + // only set properties NOT flagged + if (!(flags & Simulation::DIRTY_TRANSFORM)) { + _entity->setWorldTransform(bulletToGLM(worldTrans.getOrigin()), bulletToGLM(worldTrans.getRotation())); + } + if (!(flags & Simulation::DIRTY_LINEAR_VELOCITY)) { + _entity->setWorldVelocity(getBodyLinearVelocity()); + } + if (!(flags & Simulation::DIRTY_ANGULAR_VELOCITY)) { + _entity->setWorldAngularVelocity(getBodyAngularVelocity()); + } + if (flags != (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) { + _entity->setLastSimulated(usecTimestampNow()); + } } - bool orientationSuccess; - _entity->setWorldOrientation(bulletToGLM(worldTrans.getRotation()), orientationSuccess, false); - if (!orientationSuccess) { - static QString repeatedMessage = - LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform " - "setOrientation failed.*"); - qCDebug(physics) << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID(); - } - _entity->setVelocity(getBodyLinearVelocity()); - _entity->setAngularVelocity(getBodyAngularVelocity()); - _entity->setLastSimulated(usecTimestampNow()); if (_entity->getSimulatorID().isNull()) { _loopsWithoutOwner++; @@ -530,9 +537,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ if (!_body->isActive()) { // make sure all derivatives are zero - _entity->setVelocity(Vectors::ZERO); - _entity->setAngularVelocity(Vectors::ZERO); - _entity->setAcceleration(Vectors::ZERO); + zeroCleanObjectVelocities(); _numInactiveUpdates++; } else { glm::vec3 gravity = _entity->getGravity(); @@ -559,9 +564,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ if (movingSlowly) { // velocities might not be zero, but we'll fake them as such, which will hopefully help convince // other simulating observers to deactivate their own copies - glm::vec3 zero(0.0f); - _entity->setVelocity(zero); - _entity->setAngularVelocity(zero); + zeroCleanObjectVelocities(); } } _numInactiveUpdates = 0; @@ -818,3 +821,22 @@ bool EntityMotionState::shouldBeLocallyOwned() const { void EntityMotionState::upgradeOutgoingPriority(uint8_t priority) { _outgoingPriority = glm::max(_outgoingPriority, priority); } + +void EntityMotionState::zeroCleanObjectVelocities() const { + // If transform or velocities are flagged as dirty it means a network or scripted change + // occured between the beginning and end of the stepSimulation() and we DON'T want to apply + // these physics simulation results. + uint32_t flags = _entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES); + if (!flags) { + _entity->setWorldVelocity(glm::vec3(0.0f)); + _entity->setWorldAngularVelocity(glm::vec3(0.0f)); + } else { + if (!(flags & Simulation::DIRTY_LINEAR_VELOCITY)) { + _entity->setWorldVelocity(glm::vec3(0.0f)); + } + if (!(flags & Simulation::DIRTY_ANGULAR_VELOCITY)) { + _entity->setWorldAngularVelocity(glm::vec3(0.0f)); + } + } + _entity->setAcceleration(glm::vec3(0.0f)); +} diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index ddfd7e1e4c..784273d600 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -87,6 +87,7 @@ public: protected: // changes _outgoingPriority only if priority is larger void upgradeOutgoingPriority(uint8_t priority); + void zeroCleanObjectVelocities() const; #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS bool entityTreeIsLocked() const; diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index 2901b4796e..fae645fdbc 100644 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -21,21 +21,25 @@ <@include LightDirectional.slh@> -<@func prepareGlobalLight(isScattering)@> - // prepareGlobalLight - // Transform directions to worldspace - vec3 fragNormal = vec3((normal)); - vec3 fragEyeVector = vec3(invViewMat * vec4(-1.0*position, 0.0)); - vec3 fragEyeDir = normalize(fragEyeVector); - +<@func fetchGlobalLight()@> // Get light - Light light = getLight(); + Light light = getKeyLight(); LightAmbient lightAmbient = getLightAmbient(); vec3 lightDirection = getLightDirection(light); vec3 lightIrradiance = getLightIrradiance(light); vec3 color = vec3(0.0); +<@endfunc@> + +<@func prepareGlobalLight(isScattering)@> + // prepareGlobalLight + // Transform directions to worldspace + vec3 fragNormal = vec3((normal)); + vec3 fragEyeVector = vec3(invViewMat * vec4(-position, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + + <$fetchGlobalLight()$> <@endfunc@> @@ -147,7 +151,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu <@func declareEvalLightmappedColor()@> vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 normal, vec3 albedo, vec3 lightmap) { - Light light = getLight(); + Light light = getKeyLight(); LightAmbient ambient = getLightAmbient(); // Catch normals perpendicular to the projection plane, hence the magic number for the threshold @@ -175,11 +179,12 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur <$declareLightingAmbient(1, 1, 1)$> <$declareLightingDirectional()$> -vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) { +vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity, vec3 prevLighting) { <$prepareGlobalLight()$> SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + color = prevLighting; color += emissive * isEmissiveEnabled(); // Ambient @@ -238,6 +243,44 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze( return color; } + +vec3 evalGlobalLightingAlphaBlendedWithHaze( + mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, + vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, SurfaceData surface, float opacity, vec3 prevLighting) +{ + <$fetchGlobalLight()$> + + color = prevLighting; + color += emissive * isEmissiveEnabled(); + + // Ambient + vec3 ambientDiffuse; + vec3 ambientSpecular; + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance); + + // Directional + vec3 directionalDiffuse; + vec3 directionalSpecular; + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); + + color += ambientDiffuse + directionalDiffuse; + color += (ambientSpecular + directionalSpecular) / opacity; + + // Haze + if ((hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) { + vec4 colorV4 = computeHazeColor( + vec4(color, 1.0), // fragment original color + position, // fragment position in eye coordinates + surface.eyeDir, // fragment eye vector in world coordinates + invViewMat[3].y, // eye height in world coordinates + lightDirection // keylight direction vector + ); + + color = colorV4.rgb; + } + + return color; +} <@endfunc@> diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index b4fc2fd32d..d0d9708c04 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -11,9 +11,12 @@ #include "DeferredLightingEffect.h" +#include + #include #include #include +#include #include #include @@ -42,6 +45,7 @@ using namespace render; struct LightLocations { int radius{ -1 }; + int keyLightBufferUnit{ -1 }; int lightBufferUnit{ -1 }; int ambientBufferUnit { -1 }; int lightIndexBufferUnit { -1 }; @@ -144,6 +148,29 @@ void DeferredLightingEffect::unsetKeyLightBatch(gpu::Batch& batch, int lightBuff } } +void DeferredLightingEffect::setupLocalLightsBatch(gpu::Batch& batch, + int clusterGridBufferUnit, int clusterContentBufferUnit, int frustumGridBufferUnit, + const LightClustersPointer& lightClusters) { + // Bind the global list of lights and the visible lights this frame + batch.setUniformBuffer(_localLightLocations->lightBufferUnit, lightClusters->_lightStage->getLightArrayBuffer()); + + batch.setUniformBuffer(frustumGridBufferUnit, lightClusters->_frustumGridBuffer); + batch.setUniformBuffer(clusterGridBufferUnit, lightClusters->_clusterGridBuffer); + batch.setUniformBuffer(clusterContentBufferUnit, lightClusters->_clusterContentBuffer); +} + +void DeferredLightingEffect::unsetLocalLightsBatch(gpu::Batch& batch, int clusterGridBufferUnit, int clusterContentBufferUnit, int frustumGridBufferUnit) { + if (clusterGridBufferUnit >= 0) { + batch.setUniformBuffer(clusterGridBufferUnit, nullptr); + } + if (clusterContentBufferUnit >= 0) { + batch.setUniformBuffer(clusterContentBufferUnit, nullptr); + } + if (frustumGridBufferUnit >= 0) { + batch.setUniformBuffer(frustumGridBufferUnit, nullptr); + } +} + static gpu::ShaderPointer makeLightProgram(const char* vertSource, const char* fragSource, LightLocationsPtr& locations) { auto VS = gpu::Shader::createVertex(std::string(vertSource)); auto PS = gpu::Shader::createPixel(std::string(fragSource)); @@ -186,6 +213,7 @@ static gpu::ShaderPointer makeLightProgram(const char* vertSource, const char* f locations->texcoordFrameTransform = program->getUniforms().findLocation("texcoordFrameTransform"); + locations->keyLightBufferUnit = program->getUniformBuffers().findLocation("keyLightBuffer"); locations->lightBufferUnit = program->getUniformBuffers().findLocation("lightBuffer"); locations->ambientBufferUnit = program->getUniformBuffers().findLocation("lightAmbientBuffer"); locations->lightIndexBufferUnit = program->getUniformBuffers().findLocation("lightIndexBuffer"); @@ -558,16 +586,16 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, batch._glUniform4fv(locations->texcoordFrameTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform)); // Setup the global lighting - deferredLightingEffect->setupKeyLightBatch(args, batch, locations->lightBufferUnit, locations->ambientBufferUnit, SKYBOX_MAP_UNIT); + deferredLightingEffect->setupKeyLightBatch(args, batch, locations->keyLightBufferUnit, locations->ambientBufferUnit, SKYBOX_MAP_UNIT); // Haze if (haze) { - batch.setUniformBuffer(HAZE_MODEL_BUFFER_SLOT, haze->getHazeParametersBuffer()); + batch.setUniformBuffer(HAZE_MODEL_BUFFER_SLOT, haze->getHazeParametersBuffer()); } - + batch.draw(gpu::TRIANGLE_STRIP, 4); - deferredLightingEffect->unsetKeyLightBatch(batch, locations->lightBufferUnit, locations->ambientBufferUnit, SKYBOX_MAP_UNIT); + deferredLightingEffect->unsetKeyLightBatch(batch, locations->keyLightBufferUnit, locations->ambientBufferUnit, SKYBOX_MAP_UNIT); for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { batch.setResourceTexture(SHADOW_MAP_UNIT+i, nullptr); @@ -622,12 +650,8 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext auto& lightIndices = lightClusters->_visibleLightIndices; if (!lightIndices.empty() && lightIndices[0] > 0) { - // Bind the global list of lights and the visible lights this frame - batch.setUniformBuffer(deferredLightingEffect->_localLightLocations->lightBufferUnit, lightClusters->_lightStage->getLightArrayBuffer()); - - batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer); - batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer); - batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, lightClusters->_clusterContentBuffer); + deferredLightingEffect->setupLocalLightsBatch(batch, LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, + lightClusters); // Local light pipeline batch.setPipeline(deferredLightingEffect->_localLight); @@ -731,9 +755,11 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) { PROFILE_RANGE(render, "Process Default Skybox"); auto textureCache = DependencyManager::get(); - auto skyboxUrl = PathUtils::resourcesPath().toStdString() + "images/Default-Sky-9-cubemap.ktx"; + QFileSelector fileSelector; + fileSelector.setExtraSelectors(FileUtils::getFileSelectors()); + auto skyboxUrl = fileSelector.select(PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.ktx"); - _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl); + _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl.toStdString()); _defaultSkyboxAmbientTexture = _defaultSkyboxTexture; _defaultSkybox->setCubemap(_defaultSkyboxTexture); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 6d2c0a6819..1b776e6409 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -51,6 +51,9 @@ public: void setupKeyLightBatch(const RenderArgs* args, gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit); void unsetKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit); + void setupLocalLightsBatch(gpu::Batch& batch, int clusterGridBufferUnit, int clusterContentBufferUnit, int frustumGridBufferUnit, const LightClustersPointer& lightClusters); + void unsetLocalLightsBatch(gpu::Batch& batch, int clusterGridBufferUnit, int clusterContentBufferUnit, int frustumGridBufferUnit); + void setShadowMapEnabled(bool enable) { _shadowMapEnabled = enable; }; void setAmbientOcclusionEnabled(bool enable) { _ambientOcclusionEnabled = enable; } bool isAmbientOcclusionEnabled() const { return _ambientOcclusionEnabled; } diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index 64a106a09c..8cb2d41723 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -147,7 +147,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), HazeEffect_TransformBufferSlot)); slotBindings.insert(gpu::Shader::Binding(std::string("colorMap"), HazeEffect_ColorMapSlot)); slotBindings.insert(gpu::Shader::Binding(std::string("linearDepthMap"), HazeEffect_LinearDepthMapSlot)); - slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), HazeEffect_LightingMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("keyLightBuffer"), HazeEffect_LightingMapSlot)); gpu::Shader::makeProgram(*program, slotBindings); _hazePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); diff --git a/libraries/render-utils/src/Fade.slh b/libraries/render-utils/src/Fade.slh index b85a824ca3..a06c8c869e 100644 --- a/libraries/render-utils/src/Fade.slh +++ b/libraries/render-utils/src/Fade.slh @@ -34,7 +34,7 @@ vec2 hash2D(vec3 position) { } float noise3D(vec3 position) { - float n = textureLod(fadeMaskMap, hash2D(position), 0).r; + float n = textureLod(fadeMaskMap, hash2D(position), 0.0).r; return pow(n, 1.0/2.2); // Remove sRGB. Need to fix this later directly in the texture } @@ -44,7 +44,7 @@ float evalFadeNoiseGradient(FadeObjectParams params, vec3 position) { vec3 noisePositionFloored = floor(noisePosition); vec3 noisePositionFraction = fract(noisePosition); - noisePositionFraction = noisePositionFraction*noisePositionFraction*(3 - 2*noisePositionFraction); + noisePositionFraction = noisePositionFraction*noisePositionFraction*(3.0 - 2.0*noisePositionFraction); float noiseLowXLowYLowZ = noise3D(noisePositionFloored); float noiseLowXHighYLowZ = noise3D(noisePositionFloored+vec3(0,1,0)); @@ -84,7 +84,7 @@ float evalFadeAlpha(FadeObjectParams params, vec3 position) { } void applyFadeClip(FadeObjectParams params, vec3 position) { - if (evalFadeAlpha(params, position) < 0) { + if (evalFadeAlpha(params, position) < 0.0) { discard; } } @@ -95,14 +95,14 @@ void applyFade(FadeObjectParams params, vec3 position, out vec3 emissive) { alpha = -alpha; } - if (alpha < 0) { + if (alpha < 0.0) { discard; } float edgeMask = alpha * fadeParameters[params.category]._edgeWidthInvWidth.y; - float edgeAlpha = 1.0-clamp(edgeMask, 0, 1); + float edgeAlpha = 1.0-clamp(edgeMask, 0.0, 1.0); - edgeMask = step(edgeMask, 1.f); + edgeMask = step(edgeMask, 1.0); edgeAlpha *= edgeAlpha; // Square to have a nice ease out vec4 color = mix(fadeParameters[params.category]._innerEdgeColor, fadeParameters[params.category]._outerEdgeColor, edgeAlpha); emissive = color.rgb * edgeMask * color.a; diff --git a/libraries/render-utils/src/ForwardGlobalLight.slh b/libraries/render-utils/src/ForwardGlobalLight.slh index e86f0c7fe3..a55e2ea651 100644 --- a/libraries/render-utils/src/ForwardGlobalLight.slh +++ b/libraries/render-utils/src/ForwardGlobalLight.slh @@ -29,7 +29,7 @@ vec3 fragEyeDir = normalize(fragEyeVector); // Get light - Light light = getLight(); + Light light = getKeyLight(); LightAmbient lightAmbient = getLightAmbient(); vec3 lightDirection = getLightDirection(light); @@ -143,7 +143,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu <@func declareEvalLightmappedColor()@> vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 normal, vec3 albedo, vec3 lightmap) { - Light light = getLight(); + Light light = getKeyLight(); LightAmbient ambient = getLightAmbient(); // Catch normals perpendicular to the projection plane, hence the magic number for the threshold diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 1fd6ca2980..23473e74f2 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -639,7 +639,7 @@ render::ShapePipelinePointer GeometryCache::_simpleWirePipeline; uint8_t GeometryCache::CUSTOM_PIPELINE_NUMBER = 0; -render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) { +render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) { initializeShapePipelines(); if (key.isWireframe()) { @@ -2001,11 +2001,11 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const } void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { - if (!_standardDrawPipeline) { + static std::once_flag once; + std::call_once(once, [&]() { auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)); auto ps = gpu::Shader::createPixel(std::string(standardDrawTexture_frag)); auto program = gpu::Shader::createProgram(vs, ps); - gpu::Shader::makeProgram((*program)); auto state = std::make_shared(); @@ -2021,9 +2021,15 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS(); auto programNoBlend = gpu::Shader::createProgram(vs, noBlendPS); - gpu::Shader::makeProgram((*programNoBlend)); + _standardDrawPipelineNoBlend = gpu::Pipeline::create(programNoBlend, stateNoBlend); - } + + batch.runLambda([program, programNoBlend] { + gpu::Shader::makeProgram((*program)); + gpu::Shader::makeProgram((*programNoBlend)); + }); + }); + if (noBlend) { batch.setPipeline(_standardDrawPipelineNoBlend); } else { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 31aa4b10ea..63af30bb79 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -158,7 +158,7 @@ public: static void computeSimpleHullPointListForShape(int entityShape, const glm::vec3 &entityExtents, QVector &outPointList); static uint8_t CUSTOM_PIPELINE_NUMBER; - static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key); + static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch); static void registerShapePipeline() { if (!CUSTOM_PIPELINE_NUMBER) { CUSTOM_PIPELINE_NUMBER = render::ShapePipeline::registerCustomShapePipelineFactory(shapePipelineFactory); diff --git a/libraries/render-utils/src/Haze.slf b/libraries/render-utils/src/Haze.slf index 2d7daf1f98..e254266350 100644 --- a/libraries/render-utils/src/Haze.slf +++ b/libraries/render-utils/src/Haze.slf @@ -53,7 +53,7 @@ void main(void) { vec4 worldFragPos = viewInverse * eyeFragPos; vec4 worldEyePos = viewInverse[3]; - Light light = getLight(); + Light light = getKeyLight(); vec3 lightDirection = getLightDirection(light); outFragColor = computeHazeColor(fragColor, eyeFragPos.xyz, worldFragPos.xyz, worldEyePos.y, lightDirection); diff --git a/libraries/render-utils/src/LightClusterGrid.slh b/libraries/render-utils/src/LightClusterGrid.slh index 722d37814d..709e8c0f70 100644 --- a/libraries/render-utils/src/LightClusterGrid.slh +++ b/libraries/render-utils/src/LightClusterGrid.slh @@ -86,4 +86,24 @@ int clusterGrid_getClusterLightId(int index, int offset) { return (((elementIndex & 0x00000001) == 1) ? (element >> 16) : element) & 0x0000FFFF; } + +<@func fetchClusterInfo(fragWorldPos)@> + + // From frag world pos find the cluster + vec4 clusterEyePos = frustumGrid_worldToEye(<$fragWorldPos$>); + ivec3 clusterPos = frustumGrid_eyeToClusterPos(clusterEyePos.xyz); + + ivec3 cluster = clusterGrid_getCluster(frustumGrid_clusterToIndex(clusterPos)); + int numLights = cluster.x + cluster.y; + ivec3 dims = frustumGrid.dims.xyz; + +<@endfunc@> + +bool hasLocalLights(int numLights, ivec3 clusterPos, ivec3 dims) { + return numLights>0 + && all(greaterThanEqual(clusterPos, ivec3(0))) + && all(lessThan(clusterPos.xy, dims.xy)) + && clusterPos.z <= dims.z; +} + <@endif@> diff --git a/libraries/render-utils/src/LightLocal.slh b/libraries/render-utils/src/LightLocal.slh new file mode 100644 index 0000000000..515a065d2e --- /dev/null +++ b/libraries/render-utils/src/LightLocal.slh @@ -0,0 +1,148 @@ +// Generated on <$_SCRIBE_DATE$> +// +// Created by Olivier Prat on 15/01/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// Everything about light +<@include graphics/Light.slh@> +<$declareLightBuffer(256)$> +<@include LightingModel.slh@> + + +<@include LightPoint.slh@> +<$declareLightingPoint(supportScattering)$> +<@include LightSpot.slh@> +<$declareLightingSpot(supportScattering)$> + +<@include LightClusterGrid.slh@> + +vec4 evalLocalLighting(ivec3 cluster, int numLights, vec3 fragWorldPos, SurfaceData surface, + float fragMetallic, vec3 fragFresnel, vec3 fragAlbedo, float fragScattering, + vec4 midNormalCurvature, vec4 lowNormalCurvature, float opacity) { + vec4 fragColor = vec4(0.0); + vec3 fragSpecular = vec3(0.0); + vec3 fragDiffuse = vec3(0.0); + int lightClusterOffset = cluster.z; + + // Compute the rougness into gloss2 once: + bool withScattering = (fragScattering * isScatteringEnabled() > 0.0); + + int numLightTouching = 0; + for (int i = 0; i < cluster.x; i++) { + // Need the light now + int theLightIndex = clusterGrid_getClusterLightId(i, lightClusterOffset); + Light light = getLight(theLightIndex); + + // Clip againgst the light volume and Make the Light vector going from fragment to light center in world space + vec4 fragLightVecLen2; + vec4 fragLightDirLen; + + if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragWorldPos.xyz, fragLightVecLen2)) { + continue; + } + + // Allright we re in the light sphere volume + fragLightDirLen.w = length(fragLightVecLen2.xyz); + fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w; + if (dot(surface.normal, fragLightDirLen.xyz) < 0.0) { + continue; + } + + numLightTouching++; + + vec3 diffuse = vec3(1.0); + vec3 specular = vec3(0.1); + + // Allright we re valid in the volume + float fragLightDistance = fragLightDirLen.w; + vec3 fragLightDir = fragLightDirLen.xyz; + + updateSurfaceDataWithLight(surface, fragLightDir); + + // Eval attenuation + float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); + vec3 lightEnergy = radialAttenuation * getLightIrradiance(light); + + // Eval shading + if (withScattering) { + evalFragShadingScattering(diffuse, specular, fragMetallic, fragFresnel, surface, fragAlbedo, + fragScattering, midNormalCurvature, lowNormalCurvature ); + } else { + evalFragShadingGloss(diffuse, specular, fragMetallic, fragFresnel, surface, fragAlbedo); + } + + diffuse *= lightEnergy; + specular *= lightEnergy; + + fragDiffuse.rgb += diffuse; + fragSpecular.rgb += specular; + } + + for (int i = cluster.x; i < numLights; i++) { + // Need the light now + int theLightIndex = clusterGrid_getClusterLightId(i, lightClusterOffset); + Light light = getLight(theLightIndex); + + // Clip againgst the light volume and Make the Light vector going from fragment to light center in world space + vec4 fragLightVecLen2; + vec4 fragLightDirLen; + float cosSpotAngle; + + if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragWorldPos.xyz, fragLightVecLen2)) { + continue; + } + + // Allright we re in the light sphere volume + fragLightDirLen.w = length(fragLightVecLen2.xyz); + fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w; + if (dot(surface.normal, fragLightDirLen.xyz) < 0.0) { + continue; + } + + // Check spot + if (!lightVolume_clipFragToLightVolumeSpotSide(light.volume, fragLightDirLen, cosSpotAngle)) { + continue; + } + + numLightTouching++; + + vec3 diffuse = vec3(1.0); + vec3 specular = vec3(0.1); + + // Allright we re valid in the volume + float fragLightDistance = fragLightDirLen.w; + vec3 fragLightDir = fragLightDirLen.xyz; + + updateSurfaceDataWithLight(surface, fragLightDir); + + // Eval attenuation + float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); + float angularAttenuation = lightIrradiance_evalLightSpotAttenuation(light.irradiance, cosSpotAngle); + vec3 lightEnergy = radialAttenuation * angularAttenuation * getLightIrradiance(light); + + // Eval shading + if (withScattering) { + evalFragShadingScattering(diffuse, specular, fragMetallic, fragFresnel, surface, fragAlbedo, + fragScattering, midNormalCurvature, lowNormalCurvature ); + } else { + evalFragShadingGloss(diffuse, specular, fragMetallic, fragFresnel, surface, fragAlbedo); + } + + diffuse *= lightEnergy; + specular *= lightEnergy; + + fragDiffuse.rgb += diffuse; + fragSpecular.rgb += specular; + } + + fragDiffuse *= isDiffuseEnabled(); + fragSpecular *= isSpecularEnabled(); + + fragColor.rgb += fragDiffuse; + fragColor.rgb += fragSpecular / opacity; + return fragColor; +} \ No newline at end of file diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 7d08fdabaf..ddcbf8229c 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -186,7 +186,7 @@ float specularDistribution(SurfaceData surface) { // Add geometric factors G1(n,l) and G1(n,v) float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl); denom *= surface.smithInvG1NdotV * smithInvG1NdotL; - // Don't divide by PI as it will be done later + // Don't divide by PI as this is part of the light normalization factor float power = surface.roughness4 / denom; return power; } @@ -202,12 +202,11 @@ vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) { vec3 specular = fresnelColor * power * angleAttenuation; float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x); - diffuse /= 3.1415926; - // Diffuse is divided by PI but specular isn't because an infinitesimal volume light source - // has a multiplier of PI, says Naty Hoffman. + // We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that + // we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit + // by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means". // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf // page 23 paragraph "Punctual light sources") - return vec4(specular, diffuse); } @@ -222,9 +221,9 @@ vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) { vec3 specular = vec3(fresnelScalar) * power * angleAttenuation; float diffuse = angleAttenuation * (1.0 - fresnelScalar); - diffuse /= 3.1415926; - // Diffuse is divided by PI but specular isn't because an infinitesimal volume light source - // has a multiplier of PI, says Naty Hoffman. + // We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that + // we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit + // by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means". // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf // page 23 paragraph "Punctual light sources") return vec4(specular, diffuse); @@ -239,8 +238,9 @@ vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) { float power = specularDistribution(surface); vec3 specular = fresnelColor * power * angleAttenuation; - // Specular isn't divided by PI because an infinitesimal volume light source - // has a multiplier of PI, says Naty Hoffman. + // We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that + // we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit + // by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means". // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf // page 23 paragraph "Punctual light sources") return vec4(specular, 0.f); @@ -289,9 +289,8 @@ void evalFragShading(out vec3 diffuse, out vec3 specular, void evalFragShadingScattering(out vec3 diffuse, out vec3 specular, - float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo - ,float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature -) { + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo, + float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature) { vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); float NdotL = surface.ndotl; diffuse = mix(vec3(NdotL), brdf, scattering); @@ -305,8 +304,7 @@ void evalFragShadingScattering(out vec3 diffuse, out vec3 specular, } void evalFragShadingGloss(out vec3 diffuse, out vec3 specular, - float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo -) { + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo) { vec4 shading = evalPBRShading(metallic, fresnel, surface); diffuse = vec3(shading.w); diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled()); diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index 6c77fc4a91..40709b3668 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -17,7 +17,9 @@ const int MAX_TEXCOORDS = 2; struct TexMapArray { - mat4 _texcoordTransforms[MAX_TEXCOORDS]; +// mat4 _texcoordTransforms[MAX_TEXCOORDS]; + mat4 _texcoordTransforms0; + mat4 _texcoordTransforms1; vec4 _lightmapParams; }; @@ -31,13 +33,13 @@ TexMapArray getTexMapArray() { <@func evalTexMapArrayTexcoord0(texMapArray, inTexcoord0, outTexcoord0)@> { - <$outTexcoord0$> = (<$texMapArray$>._texcoordTransforms[0] * vec4(<$inTexcoord0$>.st, 0.0, 1.0)).st; + <$outTexcoord0$> = (<$texMapArray$>._texcoordTransforms0 * vec4(<$inTexcoord0$>.st, 0.0, 1.0)).st; } <@endfunc@> <@func evalTexMapArrayTexcoord1(texMapArray, inTexcoord1, outTexcoord1)@> { - <$outTexcoord1$> = (<$texMapArray$>._texcoordTransforms[1] * vec4(<$inTexcoord1$>.st, 0.0, 1.0)).st; + <$outTexcoord1$> = (<$texMapArray$>._texcoordTransforms1 * vec4(<$inTexcoord1$>.st, 0.0, 1.0)).st; } <@endfunc@> diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6922e95d39..45c77a606d 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1098,7 +1098,9 @@ void Blender::run() { int index = blendshape.indices.at(j); meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; meshNormals[index] += blendshape.normals.at(j) * normalCoefficient; - meshTangents[index] += blendshape.tangents.at(j) * normalCoefficient; + if (blendshape.tangents.size() > j) { + meshTangents[index] += blendshape.tangents.at(j) * normalCoefficient; + } } } } @@ -1300,7 +1302,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo normalsAndTangents.clear(); normalsAndTangents.resize(normals.size()+tangents.size()); - assert(normalsAndTangents.size() == 2 * vertexCount); +// assert(normalsAndTangents.size() == 2 * vertexCount); // Interleave normals and tangents #if 0 diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp new file mode 100644 index 0000000000..a824a2221b --- /dev/null +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -0,0 +1,240 @@ +// +// Created by Bradley Austin Davis on 2018/01/09 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "RenderCommonTask.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "LightingModel.h" +#include "StencilMaskPass.h" +#include "DebugDeferredBuffer.h" +#include "DeferredFramebuffer.h" +#include "DeferredLightingEffect.h" +#include "SurfaceGeometryPass.h" +#include "FramebufferCache.h" +#include "TextureCache.h" +#include "ZoneRenderer.h" +#include "FadeEffect.h" +#include "RenderUtilsLogging.h" + +#include "AmbientOcclusionEffect.h" +#include "AntialiasingEffect.h" +#include "ToneMappingEffect.h" +#include "SubsurfaceScattering.h" +#include "DrawHaze.h" +#include "BloomEffect.h" +#include "HighlightEffect.h" + +#include + +using namespace render; +extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false); + +void BeginGPURangeTimer::run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) { + timer = _gpuTimer; + gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) { + _gpuTimer->begin(batch); + }); +} + +void EndGPURangeTimer::run(const render::RenderContextPointer& renderContext, const gpu::RangeTimerPointer& timer) { + gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) { + timer->end(batch); + }); + + auto config = std::static_pointer_cast(renderContext->jobConfig); + config->setGPUBatchRunTime(timer->getGPUAverage(), timer->getBatchAverage()); +} + +DrawOverlay3D::DrawOverlay3D(bool opaque) : + _shapePlumber(std::make_shared()), + _opaquePass(opaque) { + initOverlay3DPipelines(*_shapePlumber); +} + +void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + + auto config = std::static_pointer_cast(renderContext->jobConfig); + + const auto& inItems = inputs.get0(); + const auto& lightingModel = inputs.get1(); + + config->setNumDrawn((int)inItems.size()); + emit config->numDrawnChanged(); + + if (!inItems.empty()) { + RenderArgs* args = renderContext->args; + + // Clear the framebuffer without stereo + // Needs to be distinct from the other batch because using the clear call + // while stereo is enabled triggers a warning + if (_opaquePass) { + gpu::doInBatch(args->_context, [&](gpu::Batch& batch){ + batch.enableStereo(false); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, false); + }); + } + + // Render the items + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + // Setup lighting model for all items; + batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); + + renderShapes(renderContext, _shapePlumber, inItems, _maxDrawn); + args->_batch = nullptr; + }); + } +} + +void CompositeHUD::run(const RenderContextPointer& renderContext) { + assert(renderContext->args); + assert(renderContext->args->_context); + + // We do not want to render HUD elements in secondary camera + if (renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) { + return; + } + + // Grab the HUD texture +#if !defined(DISABLE_QML) + gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) { + if (renderContext->args->_hudOperator) { + renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); + } + }); +#endif +} + +void Blit::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) { + assert(renderContext->args); + assert(renderContext->args->_context); + + RenderArgs* renderArgs = renderContext->args; + auto blitFbo = renderArgs->_blitFramebuffer; + + if (!blitFbo) { + qCWarning(renderutils) << "Blit::run - no blit frame buffer."; + return; + } + + // Determine size from viewport + int width = renderArgs->_viewport.z; + int height = renderArgs->_viewport.w; + + // Blit primary to blit FBO + auto primaryFbo = srcFramebuffer; + + gpu::doInBatch(renderArgs->_context, [&](gpu::Batch& batch) { + batch.setFramebuffer(blitFbo); + + if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + if (renderArgs->isStereo()) { + gpu::Vec4i srcRectLeft; + srcRectLeft.z = width / 2; + srcRectLeft.w = height; + + gpu::Vec4i srcRectRight; + srcRectRight.x = width / 2; + srcRectRight.z = width; + srcRectRight.w = height; + + gpu::Vec4i destRectLeft; + destRectLeft.x = srcRectLeft.z; + destRectLeft.z = srcRectLeft.x; + destRectLeft.y = srcRectLeft.y; + destRectLeft.w = srcRectLeft.w; + + gpu::Vec4i destRectRight; + destRectRight.x = srcRectRight.z; + destRectRight.z = srcRectRight.x; + destRectRight.y = srcRectRight.y; + destRectRight.w = srcRectRight.w; + + // Blit left to right and right to left in stereo + batch.blit(primaryFbo, srcRectRight, blitFbo, destRectLeft); + batch.blit(primaryFbo, srcRectLeft, blitFbo, destRectRight); + } else { + gpu::Vec4i srcRect; + srcRect.z = width; + srcRect.w = height; + + gpu::Vec4i destRect; + destRect.x = width; + destRect.y = 0; + destRect.z = 0; + destRect.w = height; + + batch.blit(primaryFbo, srcRect, blitFbo, destRect); + } + } else { + gpu::Vec4i rect; + rect.z = width; + rect.w = height; + + batch.blit(primaryFbo, rect, blitFbo, rect); + } + }); +} + +void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Output& output) { + assert(renderContext->args); + assert(renderContext->args->_context); + + RenderArgs* args = renderContext->args; + + // Return view frustum + auto& viewFrustum = output[VIEW_FRUSTUM].edit(); + if (!viewFrustum) { + viewFrustum = std::make_shared(args->getViewFrustum()); + } else { + *viewFrustum = args->getViewFrustum(); + } + + // Return shadow frustum + auto lightStage = args->_scene->getStage(LightStage::getName()); + for (auto i = 0; i < SHADOW_CASCADE_FRUSTUM_COUNT; i++) { + auto& shadowFrustum = output[SHADOW_CASCADE0_FRUSTUM+i].edit(); + if (lightStage) { + auto globalShadow = lightStage->getCurrentKeyShadow(); + + if (globalShadow && i<(int)globalShadow->getCascadeCount()) { + auto& cascade = globalShadow->getCascade(i); + shadowFrustum = cascade.getFrustum(); + } else { + shadowFrustum.reset(); + } + } else { + shadowFrustum.reset(); + } + } +} diff --git a/libraries/render-utils/src/RenderCommonTask.h b/libraries/render-utils/src/RenderCommonTask.h new file mode 100644 index 0000000000..767b6893cd --- /dev/null +++ b/libraries/render-utils/src/RenderCommonTask.h @@ -0,0 +1,116 @@ +// +// Created by Bradley Austin Davis on 2018/01/09 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderCommonTask_h +#define hifi_RenderCommonTask_h + +#include +#include +#include "LightingModel.h" + +class BeginGPURangeTimer { +public: + using JobModel = render::Job::ModelO; + + BeginGPURangeTimer(const std::string& name) : _gpuTimer(std::make_shared(name)) {} + + void run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer); + +protected: + gpu::RangeTimerPointer _gpuTimer; +}; + +using GPURangeTimerConfig = render::GPUJobConfig; + +class EndGPURangeTimer { +public: + using Config = GPURangeTimerConfig; + using JobModel = render::Job::ModelI; + + EndGPURangeTimer() {} + + void configure(const Config& config) {} + void run(const render::RenderContextPointer& renderContext, const gpu::RangeTimerPointer& timer); + +protected: +}; + +class DrawOverlay3DConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged) + Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) +public: + int getNumDrawn() { return numDrawn; } + void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); } + + int maxDrawn{ -1 }; + +signals: + void numDrawnChanged(); + void dirty(); + +protected: + int numDrawn{ 0 }; +}; + +class DrawOverlay3D { +public: + using Inputs = render::VaryingSet2 ; + + using Config = DrawOverlay3DConfig; + using JobModel = render::Job::ModelI; + + DrawOverlay3D(bool opaque); + + void configure(const Config& config) { _maxDrawn = config.maxDrawn; } + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); + +protected: + render::ShapePlumberPointer _shapePlumber; + int _maxDrawn; // initialized by Config + bool _opaquePass{ true }; +}; + +class CompositeHUD { +public: + using JobModel = render::Job::Model; + + CompositeHUD() {} + void run(const render::RenderContextPointer& renderContext); +}; + +class Blit { +public: + using JobModel = render::Job::ModelI; + + void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer); +}; + +class ExtractFrustums { +public: + + enum Frustum { + SHADOW_CASCADE0_FRUSTUM = 0, + SHADOW_CASCADE1_FRUSTUM, + SHADOW_CASCADE2_FRUSTUM, + SHADOW_CASCADE3_FRUSTUM, + + SHADOW_CASCADE_FRUSTUM_COUNT, + + VIEW_FRUSTUM = SHADOW_CASCADE_FRUSTUM_COUNT, + + FRUSTUM_COUNT + }; + + using Output = render::VaryingArray; + using JobModel = render::Job::ModelO; + + void run(const render::RenderContextPointer& renderContext, Output& output); +}; + +#endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 979683e338..f262307944 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -12,6 +12,8 @@ #include "RenderDeferredTask.h" +#include + #include #include #include @@ -25,6 +27,7 @@ #include #include +#include "RenderCommonTask.h" #include "LightingModel.h" #include "StencilMaskPass.h" #include "DebugDeferredBuffer.h" @@ -167,7 +170,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawHazeDeferred", drawHazeInputs); // Render transparent objects forward in LightingBuffer - const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).asVarying(); + const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel, lightClusters).asVarying(); task.addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber); // Light Cluster Grid Debuging job @@ -289,23 +292,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("Blit", primaryFramebuffer); } -void BeginGPURangeTimer::run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) { - timer = _gpuTimer; - gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) { - _gpuTimer->begin(batch); - }); -} - -void EndGPURangeTimer::run(const render::RenderContextPointer& renderContext, const gpu::RangeTimerPointer& timer) { - gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) { - timer->end(batch); - }); - - auto config = std::static_pointer_cast(renderContext->jobConfig); - config->setGPUBatchRunTime(timer->getGPUAverage(), timer->getBatchAverage()); -} - - void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -314,6 +300,8 @@ void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& const auto& inItems = inputs.get0(); const auto& lightingModel = inputs.get1(); + const auto& lightClusters = inputs.get2(); + auto deferredLightingEffect = DependencyManager::get(); RenderArgs* args = renderContext->args; @@ -335,7 +323,13 @@ void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& // Setup lighting model for all items; batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); - // Setup haze iff current zone has haze + deferredLightingEffect->setupLocalLightsBatch(batch, + render::ShapePipeline::Slot::LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, + render::ShapePipeline::Slot::LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, + render::ShapePipeline::Slot::LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, + lightClusters); + + // Setup haze if current zone has haze auto hazeStage = args->_scene->getStage(); if (hazeStage && hazeStage->_currentFrame._hazes.size() > 0) { graphics::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front()); @@ -357,6 +351,11 @@ void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& args->_batch = nullptr; args->_globalShapeKey = 0; + + deferredLightingEffect->unsetLocalLightsBatch(batch, + render::ShapePipeline::Slot::LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, + render::ShapePipeline::Slot::LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, + render::ShapePipeline::Slot::LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT); }); config->setNumDrawn((int)inItems.size()); @@ -412,177 +411,3 @@ void DrawStateSortDeferred::run(const RenderContextPointer& renderContext, const config->setNumDrawn((int)inItems.size()); } -DrawOverlay3D::DrawOverlay3D(bool opaque) : - _shapePlumber(std::make_shared()), - _opaquePass(opaque) { - initOverlay3DPipelines(*_shapePlumber); -} - -void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) { - assert(renderContext->args); - assert(renderContext->args->hasViewFrustum()); - - auto config = std::static_pointer_cast(renderContext->jobConfig); - - const auto& inItems = inputs.get0(); - const auto& lightingModel = inputs.get1(); - - config->setNumDrawn((int)inItems.size()); - emit config->numDrawnChanged(); - - if (!inItems.empty()) { - RenderArgs* args = renderContext->args; - - // Clear the framebuffer without stereo - // Needs to be distinct from the other batch because using the clear call - // while stereo is enabled triggers a warning - if (_opaquePass) { - gpu::doInBatch(args->_context, [&](gpu::Batch& batch){ - batch.enableStereo(false); - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, false); - }); - } - - // Render the items - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - - // Setup lighting model for all items; - batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); - - renderShapes(renderContext, _shapePlumber, inItems, _maxDrawn); - args->_batch = nullptr; - }); - } -} - -void CompositeHUD::run(const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_context); - - // We do not want to render HUD elements in secondary camera - if (renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) { - return; - } - - // Grab the HUD texture - gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) { - if (renderContext->args->_hudOperator) { - renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); - } - }); -} - -void Blit::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) { - assert(renderContext->args); - assert(renderContext->args->_context); - - RenderArgs* renderArgs = renderContext->args; - auto blitFbo = renderArgs->_blitFramebuffer; - - if (!blitFbo) { - qCWarning(renderutils) << "Blit::run - no blit frame buffer."; - return; - } - - // Determine size from viewport - int width = renderArgs->_viewport.z; - int height = renderArgs->_viewport.w; - - // Blit primary to blit FBO - auto primaryFbo = srcFramebuffer; - - gpu::doInBatch(renderArgs->_context, [&](gpu::Batch& batch) { - batch.setFramebuffer(blitFbo); - - if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { - if (renderArgs->isStereo()) { - gpu::Vec4i srcRectLeft; - srcRectLeft.z = width / 2; - srcRectLeft.w = height; - - gpu::Vec4i srcRectRight; - srcRectRight.x = width / 2; - srcRectRight.z = width; - srcRectRight.w = height; - - gpu::Vec4i destRectLeft; - destRectLeft.x = srcRectLeft.z; - destRectLeft.z = srcRectLeft.x; - destRectLeft.y = srcRectLeft.y; - destRectLeft.w = srcRectLeft.w; - - gpu::Vec4i destRectRight; - destRectRight.x = srcRectRight.z; - destRectRight.z = srcRectRight.x; - destRectRight.y = srcRectRight.y; - destRectRight.w = srcRectRight.w; - - // Blit left to right and right to left in stereo - batch.blit(primaryFbo, srcRectRight, blitFbo, destRectLeft); - batch.blit(primaryFbo, srcRectLeft, blitFbo, destRectRight); - } else { - gpu::Vec4i srcRect; - srcRect.z = width; - srcRect.w = height; - - gpu::Vec4i destRect; - destRect.x = width; - destRect.y = 0; - destRect.z = 0; - destRect.w = height; - - batch.blit(primaryFbo, srcRect, blitFbo, destRect); - } - } else { - gpu::Vec4i rect; - rect.z = width; - rect.w = height; - - batch.blit(primaryFbo, rect, blitFbo, rect); - } - }); -} - -void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Output& output) { - assert(renderContext->args); - assert(renderContext->args->_context); - - RenderArgs* args = renderContext->args; - - // Return view frustum - auto& viewFrustum = output[VIEW_FRUSTUM].edit(); - if (!viewFrustum) { - viewFrustum = std::make_shared(args->getViewFrustum()); - } else { - *viewFrustum = args->getViewFrustum(); - } - - // Return shadow frustum - auto lightStage = args->_scene->getStage(LightStage::getName()); - for (auto i = 0; i < SHADOW_CASCADE_FRUSTUM_COUNT; i++) { - auto& shadowFrustum = output[SHADOW_CASCADE0_FRUSTUM+i].edit(); - if (lightStage) { - auto globalShadow = lightStage->getCurrentKeyShadow(); - - if (globalShadow && i<(int)globalShadow->getCascadeCount()) { - auto& cascade = globalShadow->getCascade(i); - shadowFrustum = cascade.getFrustum(); - } else { - shadowFrustum.reset(); - } - } else { - shadowFrustum.reset(); - } - } -} diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index e41b7edc77..1b57c88768 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -15,43 +15,19 @@ #include #include #include "LightingModel.h" +#include "LightClusters.h" -class BeginGPURangeTimer { -public: - using JobModel = render::Job::ModelO; - - BeginGPURangeTimer(const std::string& name) : _gpuTimer(std::make_shared(name)) {} - - void run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer); - -protected: - gpu::RangeTimerPointer _gpuTimer; -}; - -using GPURangeTimerConfig = render::GPUJobConfig; - -class EndGPURangeTimer { -public: - using Config = GPURangeTimerConfig; - using JobModel = render::Job::ModelI; - - EndGPURangeTimer() {} - - void configure(const Config& config) {} - void run(const render::RenderContextPointer& renderContext, const gpu::RangeTimerPointer& timer); - -protected: -}; - -class DrawConfig : public render::Job::Config { +class DrawDeferredConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY newStats) Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) public: - int getNumDrawn() { return _numDrawn; } - void setNumDrawn(int numDrawn) { _numDrawn = numDrawn; emit newStats(); } + void setNumDrawn(int numDrawn) { + _numDrawn = numDrawn; + emit newStats(); + } int maxDrawn{ -1 }; @@ -65,29 +41,32 @@ protected: class DrawDeferred { public: - using Inputs = render::VaryingSet2 ; - using Config = DrawConfig; + using Inputs = render::VaryingSet3 ; + using Config = DrawDeferredConfig; using JobModel = render::Job::ModelI; - DrawDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + DrawDeferred(render::ShapePlumberPointer shapePlumber) + : _shapePlumber{ shapePlumber } {} void configure(const Config& config) { _maxDrawn = config.maxDrawn; } void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); protected: render::ShapePlumberPointer _shapePlumber; - int _maxDrawn; // initialized by Config + int _maxDrawn; // initialized by Config }; class DrawStateSortConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged) - Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) - Q_PROPERTY(bool stateSort MEMBER stateSort NOTIFY dirty) + Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged) + Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) + Q_PROPERTY(bool stateSort MEMBER stateSort NOTIFY dirty) public: - int getNumDrawn() { return numDrawn; } - void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); } + void setNumDrawn(int num) { + numDrawn = num; + emit numDrawnChanged(); + } int maxDrawn{ -1 }; bool stateSort{ true }; @@ -102,101 +81,32 @@ protected: class DrawStateSortDeferred { public: - using Inputs = render::VaryingSet2 ; + using Inputs = render::VaryingSet2; using Config = DrawStateSortConfig; using JobModel = render::Job::ModelI; - DrawStateSortDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + DrawStateSortDeferred(render::ShapePlumberPointer shapePlumber) + : _shapePlumber{ shapePlumber } {} - void configure(const Config& config) { _maxDrawn = config.maxDrawn; _stateSort = config.stateSort; } + void configure(const Config& config) { + _maxDrawn = config.maxDrawn; + _stateSort = config.stateSort; + } void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); protected: render::ShapePlumberPointer _shapePlumber; - int _maxDrawn; // initialized by Config + int _maxDrawn; // initialized by Config bool _stateSort; }; -class DrawOverlay3DConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged) - Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) -public: - int getNumDrawn() { return numDrawn; } - void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); } - - int maxDrawn{ -1 }; - -signals: - void numDrawnChanged(); - void dirty(); - -protected: - int numDrawn{ 0 }; -}; - -class DrawOverlay3D { -public: - using Inputs = render::VaryingSet2 ; - - using Config = DrawOverlay3DConfig; - using JobModel = render::Job::ModelI; - - DrawOverlay3D(bool opaque); - - void configure(const Config& config) { _maxDrawn = config.maxDrawn; } - void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); - -protected: - render::ShapePlumberPointer _shapePlumber; - int _maxDrawn; // initialized by Config - bool _opaquePass { true }; -}; - -class CompositeHUD { -public: - using JobModel = render::Job::Model; - - CompositeHUD() {} - void run(const render::RenderContextPointer& renderContext); -}; - -class Blit { -public: - using JobModel = render::Job::ModelI; - - void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer); -}; - -class ExtractFrustums { -public: - - enum Frustum { - SHADOW_CASCADE0_FRUSTUM = 0, - SHADOW_CASCADE1_FRUSTUM, - SHADOW_CASCADE2_FRUSTUM, - SHADOW_CASCADE3_FRUSTUM, - - SHADOW_CASCADE_FRUSTUM_COUNT, - - VIEW_FRUSTUM = SHADOW_CASCADE_FRUSTUM_COUNT, - - FRUSTUM_COUNT - }; - - using Output = render::VaryingArray; - using JobModel = render::Job::ModelO; - - void run(const render::RenderContextPointer& renderContext, Output& output); -}; - class RenderDeferredTaskConfig : public render::Task::Config { Q_OBJECT - Q_PROPERTY(float fadeScale MEMBER fadeScale NOTIFY dirty) - Q_PROPERTY(float fadeDuration MEMBER fadeDuration NOTIFY dirty) - Q_PROPERTY(bool debugFade MEMBER debugFade NOTIFY dirty) - Q_PROPERTY(float debugFadePercent MEMBER debugFadePercent NOTIFY dirty) + Q_PROPERTY(float fadeScale MEMBER fadeScale NOTIFY dirty) + Q_PROPERTY(float fadeDuration MEMBER fadeDuration NOTIFY dirty) + Q_PROPERTY(bool debugFade MEMBER debugFade NOTIFY dirty) + Q_PROPERTY(float debugFadePercent MEMBER debugFadePercent NOTIFY dirty) public: float fadeScale{ 0.5f }; float fadeDuration{ 3.0f }; @@ -205,7 +115,6 @@ public: signals: void dirty(); - }; class RenderDeferredTask { @@ -220,9 +129,11 @@ public: void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); private: - - static const render::Varying addSelectItemJobs(JobModel& task, const char* selectionName, - const render::Varying& metas, const render::Varying& opaques, const render::Varying& transparents); + static const render::Varying addSelectItemJobs(JobModel& task, + const char* selectionName, + const render::Varying& metas, + const render::Varying& opaques, + const render::Varying& transparents); }; -#endif // hifi_RenderDeferredTask_h +#endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index c83251c605..c201e8a106 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -11,59 +11,95 @@ // #include "RenderForwardTask.h" -#include "RenderDeferredTask.h" #include #include #include #include -#include "StencilMaskPass.h" +#include +#include +#include "StencilMaskPass.h" +#include "ZoneRenderer.h" +#include "FadeEffect.h" +#include "BackgroundStage.h" #include "FramebufferCache.h" #include "TextureCache.h" - -#include +#include "RenderCommonTask.h" +#include "LightStage.h" #include "nop_frag.h" using namespace render; -extern void initForwardPipelines(ShapePlumber& plumber); +extern void initForwardPipelines(ShapePlumber& plumber, + const render::ShapePipeline::BatchSetter& batchSetter, + const render::ShapePipeline::ItemSetter& itemSetter); void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { auto items = input.get(); + auto fadeEffect = DependencyManager::get(); // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); - initForwardPipelines(*shapePlumber); + initForwardPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); // Extract opaques / transparents / lights / metas / overlays / background const auto& opaques = items.get0()[RenderFetchCullSortTask::OPAQUE_SHAPE]; -// const auto& transparents = items.get0()[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; -// const auto& lights = items.get0()[RenderFetchCullSortTask::LIGHT]; -// const auto& metas = items.get0()[RenderFetchCullSortTask::META]; -// const auto& overlayOpaques = items.get0()[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE]; -// const auto& overlayTransparents = items.get0()[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE]; - const auto& background = items.get0()[RenderFetchCullSortTask::BACKGROUND]; -// const auto& spatialSelection = items[1]; + const auto& transparents = items.get0()[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; + // const auto& lights = items.get0()[RenderFetchCullSortTask::LIGHT]; + const auto& metas = items.get0()[RenderFetchCullSortTask::META]; + // const auto& overlayOpaques = items.get0()[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE]; + // const auto& overlayTransparents = items.get0()[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE]; + //const auto& background = items.get0()[RenderFetchCullSortTask::BACKGROUND]; + // const auto& spatialSelection = items[1]; + fadeEffect->build(task, opaques); + + // Prepare objects shared by several jobs + const auto lightingModel = task.addJob("LightingModel"); + + // Filter zones from the general metas bucket + const auto zones = task.addJob("ZoneRenderer", metas); + + // GPU jobs: Start preparing the main framebuffer const auto framebuffer = task.addJob("PrepareFramebuffer"); - task.addJob("DrawOpaques", opaques, shapePlumber); - task.addJob("Stencil"); - task.addJob("DrawBackground", background); + task.addJob("PrepareForward", lightingModel); - // Bounds do not draw on stencil buffer, so they must come last - task.addJob("DrawBounds", opaques); + // draw a stencil mask in hidden regions of the framebuffer. + task.addJob("PrepareStencil", framebuffer); + + // Draw opaques forward + const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel).asVarying(); + task.addJob("DrawOpaques", opaqueInputs, shapePlumber); + + // Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job + task.addJob("DrawBackgroundDeferred", lightingModel); + + // Draw transparent objects forward + const auto transparentInputs = DrawForward::Inputs(transparents, lightingModel).asVarying(); + task.addJob("DrawTransparents", transparentInputs, shapePlumber); + + { // Debug the bounds of the rendered items, still look at the zbuffer + + task.addJob("DrawMetaBounds", metas); + task.addJob("DrawBounds", opaques); + task.addJob("DrawTransparentBounds", transparents); + + task.addJob("DrawZones", zones); + } + + // Layered Overlays + + // Composite the HUD and HUD overlays + task.addJob("HUD"); // Blit! task.addJob("Blit", framebuffer); } -void PrepareFramebuffer::run(const RenderContextPointer& renderContext, - gpu::FramebufferPointer& framebuffer) { - auto framebufferCache = DependencyManager::get(); - auto framebufferSize = framebufferCache->getFrameBufferSize(); - glm::uvec2 frameSize(framebufferSize.width(), framebufferSize.height()); +void PrepareFramebuffer::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& framebuffer) { + glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w); // Resizing framebuffers instead of re-building them seems to cause issues with threaded rendering if (_framebuffer && _framebuffer->getSize() != frameSize) { @@ -75,11 +111,13 @@ void PrepareFramebuffer::run(const RenderContextPointer& renderContext, auto colorFormat = gpu::Element::COLOR_SRGBA_32; auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - auto colorTexture = gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); + auto colorTexture = + gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); _framebuffer->setRenderBuffer(0, colorTexture); - auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - auto depthTexture = gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); + auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format + auto depthTexture = + gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); _framebuffer->setDepthStencilBuffer(depthTexture, depthFormat); } @@ -90,23 +128,86 @@ void PrepareFramebuffer::run(const RenderContextPointer& renderContext, batch.setStateScissorRect(args->_viewport); batch.setFramebuffer(_framebuffer); - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | - gpu::Framebuffer::BUFFER_DEPTH | + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH | gpu::Framebuffer::BUFFER_STENCIL, - vec4(vec3(0), 1), 1.0, 0, true); + vec4(vec3(0, 1.0, 0.0), 0), 1.0, 0, true); }); framebuffer = _framebuffer; } -void Draw::run(const RenderContextPointer& renderContext, - const Inputs& items) { +enum ForwardShader_MapSlot { + DEFERRED_BUFFER_COLOR_UNIT = 0, + DEFERRED_BUFFER_NORMAL_UNIT = 1, + DEFERRED_BUFFER_EMISSIVE_UNIT = 2, + DEFERRED_BUFFER_DEPTH_UNIT = 3, + DEFERRED_BUFFER_OBSCURANCE_UNIT = 4, + SHADOW_MAP_UNIT = 5, + SKYBOX_MAP_UNIT = SHADOW_MAP_UNIT + 4, + DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, + DEFERRED_BUFFER_CURVATURE_UNIT, + DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT, + SCATTERING_LUT_UNIT, + SCATTERING_SPECULAR_UNIT, +}; +enum ForwardShader_BufferSlot { + DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT = 0, + CAMERA_CORRECTION_BUFFER_SLOT, + SCATTERING_PARAMETERS_BUFFER_SLOT, + LIGHTING_MODEL_BUFFER_SLOT = render::ShapePipeline::Slot::LIGHTING_MODEL, + LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT, + LIGHT_AMBIENT_SLOT = render::ShapePipeline::Slot::LIGHT_AMBIENT_BUFFER, + HAZE_MODEL_BUFFER_SLOT = render::ShapePipeline::Slot::HAZE_MODEL, + LIGHT_INDEX_GPU_SLOT, + LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, + LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, + LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, +}; + +void PrepareForward::run(const RenderContextPointer& renderContext, const Inputs& inputs) { RenderArgs* args = renderContext->args; + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + graphics::LightPointer keySunLight; + auto lightStage = args->_scene->getStage(); + if (lightStage) { + keySunLight = lightStage->getCurrentKeyLight(); + } + + graphics::LightPointer keyAmbiLight; + if (lightStage) { + keyAmbiLight = lightStage->getCurrentAmbientLight(); + } + + if (keySunLight) { + if (LIGHT_GPU_SLOT >= 0) { + batch.setUniformBuffer(LIGHT_GPU_SLOT, keySunLight->getLightSchemaBuffer()); + } + } + + if (keyAmbiLight) { + if (LIGHT_AMBIENT_SLOT >= 0) { + batch.setUniformBuffer(LIGHT_AMBIENT_SLOT, keyAmbiLight->getAmbientSchemaBuffer()); + } + + if (keyAmbiLight->getAmbientMap() && (SKYBOX_MAP_UNIT >= 0)) { + batch.setResourceTexture(SKYBOX_MAP_UNIT, keyAmbiLight->getAmbientMap()); + } + } + }); +} + +void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& inputs) { + RenderArgs* args = renderContext->args; + + const auto& inItems = inputs.get0(); + const auto& lightingModel = inputs.get1(); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; + // Setup projection glm::mat4 projMat; Transform viewMat; @@ -116,66 +217,23 @@ void Draw::run(const RenderContextPointer& renderContext, batch.setViewTransform(viewMat); batch.setModelTransform(Transform()); + // Setup lighting model for all items; + batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); + + // From the lighting model define a global shapeKey ORED with individiual keys + ShapeKey::Builder keyBuilder; + if (lightingModel->isWireframeEnabled()) { + keyBuilder.withWireframe(); + } + ShapeKey globalKey = keyBuilder.build(); + args->_globalShapeKey = globalKey._flags.to_ulong(); + // Render items - renderStateSortShapes(renderContext, _shapePlumber, items, -1); + renderStateSortShapes(renderContext, _shapePlumber, inItems, -1, globalKey); + + args->_batch = nullptr; + args->_globalShapeKey = 0; }); - args->_batch = nullptr; -} - -const gpu::PipelinePointer Stencil::getPipeline() { - if (!_stencilPipeline) { - auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(nop_frag)); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); - gpu::Shader::makeProgram(*program); - - auto state = std::make_shared(); - state->setDepthTest(true, false, gpu::LESS_EQUAL); - PrepareStencil::drawBackground(*state); - - _stencilPipeline = gpu::Pipeline::create(program, state); - } - return _stencilPipeline; -} - -void Stencil::run(const RenderContextPointer& renderContext) { - RenderArgs* args = renderContext->args; - - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - - batch.enableStereo(false); - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - batch.setPipeline(getPipeline()); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); - args->_batch = nullptr; -} - -void DrawBackground::run(const RenderContextPointer& renderContext, - const Inputs& background) { - RenderArgs* args = renderContext->args; - - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - - batch.enableSkybox(true); - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - // Setup projection - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - - renderItems(renderContext, background); - }); - args->_batch = nullptr; } diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 63d087b3c5..22e75d4bdc 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -38,37 +38,28 @@ private: gpu::FramebufferPointer _framebuffer; }; -class Draw { +class PrepareForward { public: - using Inputs = render::ItemBounds; - using JobModel = render::Job::ModelI; + using Inputs = LightingModelPointer; + using JobModel = render::Job::ModelI; - Draw(const render::ShapePlumberPointer& shapePlumber) : _shapePlumber(shapePlumber) {} void run(const render::RenderContextPointer& renderContext, - const Inputs& items); + const Inputs& inputs); + +private: +}; + +class DrawForward{ +public: + using Inputs = render::VaryingSet2; + using JobModel = render::Job::ModelI; + + DrawForward(const render::ShapePlumberPointer& shapePlumber) : _shapePlumber(shapePlumber) {} + void run(const render::RenderContextPointer& renderContext, + const Inputs& inputs); private: render::ShapePlumberPointer _shapePlumber; }; -class Stencil { -public: - using JobModel = render::Job::Model; - - void run(const render::RenderContextPointer& renderContext); - -private: - const gpu::PipelinePointer getPipeline(); - gpu::PipelinePointer _stencilPipeline; -}; - -class DrawBackground { -public: - using Inputs = render::ItemBounds; - using JobModel = render::Job::ModelI; - - void run(const render::RenderContextPointer& renderContext, - const Inputs& background); -}; - #endif // hifi_RenderForwardTask_h diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 2eb063e9e8..1c828c3d06 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -29,6 +29,8 @@ #include "model_lightmap_fade_vert.h" #include "model_lightmap_normal_map_fade_vert.h" +#include "model_translucent_vert.h" +#include "model_translucent_fade_vert.h" #include "skin_model_fade_vert.h" #include "skin_model_normal_map_fade_vert.h" @@ -106,7 +108,7 @@ using namespace std::placeholders; void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest = false); void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); -void initForwardPipelines(ShapePlumber& plumber); +void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state); void addPlumberPipeline(ShapePlumber& plumber, @@ -188,6 +190,8 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); auto modelLightmapVertex = gpu::Shader::createVertex(std::string(model_lightmap_vert)); auto modelLightmapNormalMapVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)); + auto modelTranslucentVertex = gpu::Shader::createVertex(std::string(model_translucent_vert)); + auto modelTranslucentFadeVertex = gpu::Shader::createVertex(std::string(model_translucent_fade_vert)); auto modelShadowVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); @@ -196,6 +200,8 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip auto modelLightmapNormalMapFadeVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_fade_vert)); auto skinModelFadeVertex = gpu::Shader::createVertex(std::string(skin_model_fade_vert)); auto skinModelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_fade_vert)); + auto skinModelTranslucentVertex = skinModelFadeVertex; // We use the same because it ouputs world position per vertex + auto skinModelNormalMapTranslucentVertex = skinModelNormalMapFadeVertex; // We use the same because it ouputs world position per vertex auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_fade_vert)); auto modelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(model_normal_map_fade_vert)); @@ -289,7 +295,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip // Translucents addPipeline( Key::Builder().withMaterial().withTranslucent(), - modelVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withTranslucent(), simpleVertex, simpleTranslucentPixel, nullptr, nullptr); @@ -301,21 +307,21 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip simpleVertex, simpleTranslucentUnlitPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents(), - modelNormalMapVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withSpecular(), - modelVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTranslucent().withTangents().withSpecular(), - modelNormalMapVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( // FIXME: Ignore lightmap for translucents meshpart Key::Builder().withMaterial().withTranslucent().withLightmap(), - modelVertex, modelTranslucentPixel, nullptr, nullptr); + modelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); // Same thing but with Fade on addPipeline( Key::Builder().withMaterial().withTranslucent().withFade(), - modelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); + modelTranslucentFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter); addPipeline( Key::Builder().withTranslucent().withFade(), simpleFadeVertex, simpleTranslucentFadePixel, batchSetter, itemSetter); @@ -396,16 +402,16 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip // Skinned and Translucent addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent(), - skinModelVertex, modelTranslucentPixel, nullptr, nullptr); + skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents(), - skinModelNormalMapVertex, modelTranslucentPixel, nullptr, nullptr); + skinModelNormalMapTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withSpecular(), - skinModelVertex, modelTranslucentPixel, nullptr, nullptr); + skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withSpecular(), - skinModelNormalMapVertex, modelTranslucentPixel, nullptr, nullptr); + skinModelNormalMapTranslucentVertex, modelTranslucentPixel, nullptr, nullptr); // Same thing but with Fade on addPipeline( Key::Builder().withMaterial().withSkinned().withTranslucent().withFade(), @@ -436,12 +442,13 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip skinModelShadowFadeVertex, modelShadowFadePixel, batchSetter, itemSetter); } -void initForwardPipelines(render::ShapePlumber& plumber) { +void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter) { // Vertex shaders auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); + auto skinModelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_fade_vert)); // Pixel shaders auto modelPixel = gpu::Shader::createPixel(std::string(forward_model_frag)); @@ -449,38 +456,43 @@ void initForwardPipelines(render::ShapePlumber& plumber) { auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_map_frag)); auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_specular_map_frag)); auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_specular_map_frag)); + auto modelNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_map_fade_frag)); using Key = render::ShapeKey; - auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, nullptr, nullptr); + auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, _4, _5); // Opaques addPipeline( Key::Builder().withMaterial(), - modelVertex, modelPixel); + modelVertex, modelPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withUnlit(), - modelVertex, modelUnlitPixel); + modelVertex, modelUnlitPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTangents(), - modelNormalMapVertex, modelNormalMapPixel); + modelNormalMapVertex, modelNormalMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSpecular(), - modelVertex, modelSpecularMapPixel); + modelVertex, modelSpecularMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTangents().withSpecular(), - modelNormalMapVertex, modelNormalSpecularMapPixel); + modelNormalMapVertex, modelNormalSpecularMapPixel, nullptr, nullptr); // Skinned addPipeline( Key::Builder().withMaterial().withSkinned(), - skinModelVertex, modelPixel); + skinModelVertex, modelPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTangents(), - skinModelNormalMapVertex, modelNormalMapPixel); + skinModelNormalMapVertex, modelNormalMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withSpecular(), - skinModelVertex, modelSpecularMapPixel); + skinModelVertex, modelSpecularMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTangents().withSpecular(), - skinModelNormalMapVertex, modelNormalSpecularMapPixel); + skinModelNormalMapVertex, modelNormalSpecularMapPixel, nullptr, nullptr); + addPipeline( + Key::Builder().withMaterial().withSkinned().withTangents().withFade(), + skinModelNormalMapFadeVertex, modelNormalMapFadePixel, batchSetter, itemSetter, nullptr, nullptr); + } void addPlumberPipeline(ShapePlumber& plumber, @@ -565,9 +577,9 @@ void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderAr batchSetter(pipeline, batch, args); // Set the light - if (pipeline.locations->lightBufferUnit >= 0) { + if (pipeline.locations->keyLightBufferUnit >= 0) { DependencyManager::get()->setupKeyLightBatch(args, batch, - pipeline.locations->lightBufferUnit, + pipeline.locations->keyLightBufferUnit, pipeline.locations->lightAmbientBufferUnit, pipeline.locations->lightAmbientMapUnit); } diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 49d0df3d2c..a7edfb14a6 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -39,12 +39,12 @@ mat4 dualQuatToMat4(vec4 real, vec4 dual) { twoRealXZ - twoRealYW, 0.0); vec4 col1 = vec4(twoRealXY - twoRealZW, - 1 - twoRealXSq - twoRealZSq, + 1.0 - twoRealXSq - twoRealZSq, twoRealYZ + twoRealXW, 0.0); vec4 col2 = vec4(twoRealXZ + twoRealYW, twoRealYZ - twoRealXW, - 1 - twoRealXSq - twoRealYSq, + 1.0 - twoRealXSq - twoRealYSq, 0.0); vec4 col3 = vec4(2.0 * (-dual.w * real.x + dual.x * real.w - dual.y * real.z + dual.z * real.y), 2.0 * (-dual.w * real.y + dual.x * real.z + dual.y * real.w - dual.z * real.x), @@ -72,7 +72,7 @@ void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPositio // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; - if (dot(real, polarityReference) < 0) { + if (dot(real, polarityReference) < 0.0) { dqClusterWeight = -clusterWeight; } @@ -110,7 +110,7 @@ void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inP // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; - if (dot(real, polarityReference) < 0) { + if (dot(real, polarityReference) < 0.0) { dqClusterWeight = -clusterWeight; } @@ -149,7 +149,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. float dqClusterWeight = clusterWeight; - if (dot(real, polarityReference) < 0) { + if (dot(real, polarityReference) < 0.0) { dqClusterWeight = -clusterWeight; } diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index 1786898e57..2183f95565 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -463,7 +463,7 @@ gpu::PipelinePointer DebugSubsurfaceScattering::getScatteringPipeline() { gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), ScatteringTask_FrameTransformSlot)); slotBindings.insert(gpu::Shader::Binding(std::string("scatteringParamsBuffer"), ScatteringTask_ParamSlot)); - slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), ScatteringTask_LightSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("keyLightBuffer"), ScatteringTask_LightSlot)); slotBindings.insert(gpu::Shader::Binding(std::string("scatteringLUT"), ScatteringTask_ScatteringTableSlot)); slotBindings.insert(gpu::Shader::Binding(std::string("curvatureMap"), ScatteringTask_CurvatureMapSlot)); diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index c39e6068b7..11e5b8d2f5 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -83,7 +83,7 @@ const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), ZONE_DEFERRED_TRANSFORM_BUFFER)); - slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), ZONE_KEYLIGHT_BUFFER)); + slotBindings.insert(gpu::Shader::Binding(std::string("keyLightBuffer"), ZONE_KEYLIGHT_BUFFER)); gpu::Shader::makeProgram(*program, slotBindings); diff --git a/libraries/render-utils/src/forward_model.slf b/libraries/render-utils/src/forward_model.slf index 41ff4afc81..65556e2939 100644 --- a/libraries/render-utils/src/forward_model.slf +++ b/libraries/render-utils/src/forward_model.slf @@ -68,7 +68,7 @@ void main(void) { vec3 fragNormal; <$transformEyeToWorldDir(cam, _normal, fragNormal)$> - /* vec4 color = vec4(evalSkyboxGlobalColor( + vec4 color = vec4(evalSkyboxGlobalColor( cam._viewInverse, 1.0, 1.0, @@ -81,8 +81,6 @@ void main(void) { opacity); color.rgb += emissive * isEmissiveEnabled(); - */ - - _fragColor = vec4(albedo, opacity); - +// _fragColor = vec4(albedo, opacity); + _fragColor = color; } diff --git a/libraries/render-utils/src/forward_model_normal_map.slf b/libraries/render-utils/src/forward_model_normal_map.slf index e5fcab4945..d22cc667e1 100644 --- a/libraries/render-utils/src/forward_model_normal_map.slf +++ b/libraries/render-utils/src/forward_model_normal_map.slf @@ -12,10 +12,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include ForwardBufferWrite.slh@> + !> + +<@include ForwardGlobalLight.slh@> +<$declareEvalSkyboxGlobalColor()$> <@include graphics/Material.slh@> +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION, SCATTERING)$> @@ -26,6 +32,8 @@ in vec3 _normal; in vec3 _tangent; in vec3 _color; +out vec4 _fragColor; + void main(void) { Material mat = getMaterial(); int matKey = getMaterialKey(mat); @@ -40,6 +48,15 @@ void main(void) { <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; albedo *= _color; + float metallic = getMaterialMetallic(mat); + vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value + if (metallic <= 0.5) { + metallic = 0.0; + } else { + fresnel = albedo; + metallic = 1.0; + } + float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; @@ -52,13 +69,24 @@ void main(void) { float scattering = getMaterialScattering(mat); <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; - packForwardFragment( - viewNormal, - opacity, + vec3 fragPosition = _position.xyz; + + TransformCamera cam = getTransformCamera(); + + vec4 color = vec4(evalSkyboxGlobalColor( + cam._viewInverse, + 1.0, + 1.0, + fragPosition, + viewNormal, albedo, - roughness, - getMaterialMetallic(mat), - emissive, - occlusionTex, - scattering); + fresnel, + metallic, + roughness), + opacity); + + color.rgb += emissive * isEmissiveEnabled(); + +// _fragColor = vec4(albedo, opacity); + _fragColor = color; } diff --git a/libraries/render-utils/src/forward_model_normal_specular_map.slf b/libraries/render-utils/src/forward_model_normal_specular_map.slf index 01a4251e8d..68e5b755f8 100644 --- a/libraries/render-utils/src/forward_model_normal_specular_map.slf +++ b/libraries/render-utils/src/forward_model_normal_specular_map.slf @@ -12,10 +12,17 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include ForwardBufferWrite.slh@> + + !> + +<@include ForwardGlobalLight.slh@> +<$declareEvalSkyboxGlobalColor()$> <@include graphics/Material.slh@> +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$> @@ -26,6 +33,8 @@ in vec3 _normal; in vec3 _tangent; in vec3 _color; +out vec4 _fragColor; + void main(void) { Material mat = getMaterial(); int matKey = getMaterialKey(mat); @@ -51,16 +60,32 @@ void main(void) { float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value + if (metallic <= 0.5) { + metallic = 0.0; + } + else { + fresnel = albedo; + metallic = 1.0; + } + vec3 fragPosition = _position.xyz; - float scattering = getMaterialScattering(mat); + TransformCamera cam = getTransformCamera(); - packForwardFragment( - normalize(viewNormal.xyz), - opacity, + vec4 color = vec4(evalSkyboxGlobalColor( + cam._viewInverse, + 1.0, + 1.0, + fragPosition, + viewNormal, albedo, - roughness, + fresnel, metallic, - emissive, - occlusionTex, - scattering); + roughness), + opacity); + + color.rgb += emissive * isEmissiveEnabled(); + + // _fragColor = vec4(albedo, opacity); + _fragColor = color; } diff --git a/libraries/render-utils/src/forward_model_specular_map.slf b/libraries/render-utils/src/forward_model_specular_map.slf index 77d34d4309..057ec2cf53 100644 --- a/libraries/render-utils/src/forward_model_specular_map.slf +++ b/libraries/render-utils/src/forward_model_specular_map.slf @@ -12,10 +12,17 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include ForwardBufferWrite.slh@> + + !> + +<@include ForwardGlobalLight.slh@> +<$declareEvalSkyboxGlobalColor()$> <@include graphics/Material.slh@> +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$> @@ -25,12 +32,13 @@ in vec2 _texCoord1; in vec3 _normal; in vec3 _color; +out vec4 _fragColor; void main(void) { Material mat = getMaterial(); int matKey = getMaterialKey(mat); <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex)$> - <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + !> float opacity = 1.0; <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; @@ -48,7 +56,14 @@ void main(void) { float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; - + vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value + if (metallic <= 0.5) { + metallic = 0.0; + } + else { + fresnel = albedo; + metallic = 1.0; + }/* float scattering = getMaterialScattering(mat); packForwardFragment( @@ -60,4 +75,26 @@ void main(void) { emissive, occlusionTex, scattering); + */ + vec3 fragPosition = _position.xyz; + + TransformCamera cam = getTransformCamera(); + vec3 fragNormal; + <$transformEyeToWorldDir(cam, _normal, fragNormal)$> + + vec4 color = vec4(evalSkyboxGlobalColor( + cam._viewInverse, + 1.0, + 1.0, + fragPosition, + fragNormal, + albedo, + fresnel, + metallic, + roughness), + opacity); + color.rgb += emissive * isEmissiveEnabled(); + + // _fragColor = vec4(albedo, opacity); + _fragColor = color; } diff --git a/libraries/render-utils/src/local_lights_drawOutline.slf b/libraries/render-utils/src/local_lights_drawOutline.slf index 56ce1e61a5..38dd383459 100644 --- a/libraries/render-utils/src/local_lights_drawOutline.slf +++ b/libraries/render-utils/src/local_lights_drawOutline.slf @@ -54,26 +54,8 @@ void main(void) { mat4 invViewMat = getViewInverse(); vec4 fragPos = invViewMat * fragPosition; - // From frag world pos find the cluster - vec4 clusterEyePos = frustumGrid_worldToEye(fragPos); - ivec3 clusterPos = frustumGrid_eyeToClusterPos(clusterEyePos.xyz); - - ivec3 cluster = clusterGrid_getCluster(frustumGrid_clusterToIndex(clusterPos)); - int numLights = cluster.x + cluster.y; - if (numLights <= 0) { - discard; - } - int lightClusterOffset = cluster.z; - - ivec3 dims = frustumGrid.dims.xyz; - if (clusterPos.x < 0 || clusterPos.x >= dims.x) { - discard; - } - - if (clusterPos.y < 0 || clusterPos.y >= dims.y) { - discard; - } - if (clusterPos.z < 0 || clusterPos.z > dims.z) { + <$fetchClusterInfo(fragPos)$>; + if (!hasLocalLights(numLights, clusterPos, dims)) { discard; } @@ -82,6 +64,7 @@ void main(void) { vec3 fragEyeDir = normalize(fragEyeVector.xyz); int numLightTouching = 0; + int lightClusterOffset = cluster.z; for (int i = 0; i < cluster.x; i++) { // Need the light now int theLightIndex = clusterGrid_getClusterLightId(i, lightClusterOffset); diff --git a/libraries/render-utils/src/local_lights_shading.slf b/libraries/render-utils/src/local_lights_shading.slf index c3208de726..8ae8f3fb5b 100644 --- a/libraries/render-utils/src/local_lights_shading.slf +++ b/libraries/render-utils/src/local_lights_shading.slf @@ -17,18 +17,7 @@ <$declareDeferredCurvature()$> -// Everything about light -<@include graphics/Light.slh@> -<$declareLightBuffer(256)$> -<@include LightingModel.slh@> - - -<@include LightPoint.slh@> -<$declareLightingPoint(supportScattering)$> -<@include LightSpot.slh@> -<$declareLightingSpot(supportScattering)$> - -<@include LightClusterGrid.slh@> +<@include LightLocal.slh@> in vec2 _texCoord0; out vec4 _fragColor; @@ -49,28 +38,10 @@ void main(void) { // Frag pos in world mat4 invViewMat = getViewInverse(); - vec4 fragPos = invViewMat * fragPosition; + vec4 fragWorldPos = invViewMat * fragPosition; - // From frag world pos find the cluster - vec4 clusterEyePos = frustumGrid_worldToEye(fragPos); - ivec3 clusterPos = frustumGrid_eyeToClusterPos(clusterEyePos.xyz); - - ivec3 cluster = clusterGrid_getCluster(frustumGrid_clusterToIndex(clusterPos)); - int numLights = cluster.x + cluster.y; - if (numLights <= 0) { - discard; - } - int lightClusterOffset = cluster.z; - - ivec3 dims = frustumGrid.dims.xyz; - if (clusterPos.x < 0 || clusterPos.x >= dims.x) { - discard; - } - - if (clusterPos.y < 0 || clusterPos.y >= dims.y) { - discard; - } - if (clusterPos.z < 0 || clusterPos.z > dims.z) { + <$fetchClusterInfo(fragWorldPos)$>; + if (!hasLocalLights(numLights, clusterPos, dims)) { discard; } @@ -84,117 +55,11 @@ void main(void) { // Frag to eye vec vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0); vec3 fragEyeDir = normalize(fragEyeVector.xyz); - SurfaceData surface = initSurfaceData(frag.roughness, frag.normal, fragEyeDir); - bool withScattering = (frag.scattering * isScatteringEnabled() > 0.0); - int numLightTouching = 0; - for (int i = 0; i < cluster.x; i++) { - // Need the light now - int theLightIndex = clusterGrid_getClusterLightId(i, lightClusterOffset); - Light light = getLight(theLightIndex); - - // Clip againgst the light volume and Make the Light vector going from fragment to light center in world space - vec4 fragLightVecLen2; - vec4 fragLightDirLen; - - if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragPos.xyz, fragLightVecLen2)) { - continue; - } - - // Allright we re in the light sphere volume - fragLightDirLen.w = length(fragLightVecLen2.xyz); - fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w; - if (dot(frag.normal, fragLightDirLen.xyz) < 0.0) { - continue; - } - - numLightTouching++; - - vec3 diffuse = vec3(1.0); - vec3 specular = vec3(0.1); - - // Allright we re valid in the volume - float fragLightDistance = fragLightDirLen.w; - vec3 fragLightDir = fragLightDirLen.xyz; - - updateSurfaceDataWithLight(surface, fragLightDir); - - // Eval attenuation - float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); - vec3 lightEnergy = radialAttenuation * getLightIrradiance(light); - - // Eval shading - if (withScattering) { - evalFragShadingScattering(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo - ,frag.scattering, midNormalCurvature, lowNormalCurvature ); - } else { - evalFragShadingGloss(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo); - } - - diffuse *= lightEnergy * isDiffuseEnabled(); - specular *= lightEnergy * isSpecularEnabled(); - - _fragColor.rgb += diffuse; - _fragColor.rgb += specular; - } - - for (int i = cluster.x; i < numLights; i++) { - // Need the light now - int theLightIndex = clusterGrid_getClusterLightId(i, lightClusterOffset); - Light light = getLight(theLightIndex); - - // Clip againgst the light volume and Make the Light vector going from fragment to light center in world space - vec4 fragLightVecLen2; - vec4 fragLightDirLen; - float cosSpotAngle; - - if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragPos.xyz, fragLightVecLen2)) { - continue; - } - - // Allright we re in the light sphere volume - fragLightDirLen.w = length(fragLightVecLen2.xyz); - fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w; - if (dot(frag.normal, fragLightDirLen.xyz) < 0.0) { - continue; - } - - // Check spot - if (!lightVolume_clipFragToLightVolumeSpotSide(light.volume, fragLightDirLen, cosSpotAngle)) { - continue; - } - - numLightTouching++; - - vec3 diffuse = vec3(1.0); - vec3 specular = vec3(0.1); - - // Allright we re valid in the volume - float fragLightDistance = fragLightDirLen.w; - vec3 fragLightDir = fragLightDirLen.xyz; - - updateSurfaceDataWithLight(surface, fragLightDir); - - // Eval attenuation - float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); - float angularAttenuation = lightIrradiance_evalLightSpotAttenuation(light.irradiance, cosSpotAngle); - vec3 lightEnergy = radialAttenuation * angularAttenuation * getLightIrradiance(light); - - // Eval shading - if (withScattering) { - evalFragShadingScattering(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo - ,frag.scattering, midNormalCurvature, lowNormalCurvature ); - } else { - evalFragShadingGloss(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo); - } - - diffuse *= lightEnergy * isDiffuseEnabled(); - specular *= lightEnergy * isSpecularEnabled(); - - _fragColor.rgb += diffuse; - _fragColor.rgb += specular; - } + _fragColor = evalLocalLighting(cluster, numLights, fragWorldPos.xyz, surface, + frag.metallic, frag.fresnel, frag.albedo, frag.scattering, + midNormalCurvature, lowNormalCurvature, 1.0); } diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 0924389dba..761e702595 100644 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -18,6 +18,8 @@ <$declareEvalGlobalLightingAlphaBlended()$> +<@include LightLocal.slh@> + <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> @@ -27,6 +29,7 @@ in vec2 _texCoord0; in vec2 _texCoord1; in vec4 _position; +in vec4 _worldPosition; in vec3 _normal; in vec3 _color; in float _alpha; @@ -56,20 +59,32 @@ void main(void) { <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; vec3 fragPosition = _position.xyz; + // Lighting is done in world space vec3 fragNormal = normalize(_normal); TransformCamera cam = getTransformCamera(); + vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + + vec4 localLighting = vec4(0.0); + + <$fetchClusterInfo(_worldPosition)$>; + if (hasLocalLights(numLights, clusterPos, dims)) { + localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface, + metallic, fresnel, albedo, 0.0, + vec4(0), vec4(0), opacity); + } _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( cam._viewInverse, 1.0, occlusionTex, fragPosition, - fragNormal, albedo, fresnel, metallic, emissive, - roughness, opacity), + surface, opacity, localLighting.rgb), opacity); } diff --git a/libraries/render-utils/src/model_translucent.slv b/libraries/render-utils/src/model_translucent.slv new file mode 100644 index 0000000000..305aba06c3 --- /dev/null +++ b/libraries/render-utils/src/model_translucent.slv @@ -0,0 +1,44 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// model_translucent.slv +// vertex shader +// +// Created by Olivier Prat on 15/01/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTexMapArrayBuffer()$> + +out float _alpha; +out vec2 _texCoord0; +out vec2 _texCoord1; +out vec4 _position; +out vec4 _worldPosition; +out vec3 _normal; +out vec3 _color; + +void main(void) { + _color = colorToLinearRGB(inColor.xyz); + _alpha = inColor.w; + + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> + <$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$> + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> + <$transformModelToWorldPos(obj, inPosition, _worldPosition)$> + <$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$> +} diff --git a/libraries/render-utils/src/model_translucent_fade.slf b/libraries/render-utils/src/model_translucent_fade.slf index 8ac442476f..a2d271653c 100644 --- a/libraries/render-utils/src/model_translucent_fade.slf +++ b/libraries/render-utils/src/model_translucent_fade.slf @@ -18,6 +18,8 @@ <$declareEvalGlobalLightingAlphaBlended()$> +<@include LightLocal.slh@> + <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> @@ -66,20 +68,32 @@ void main(void) { <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; vec3 fragPosition = _position.xyz; + // Lighting is done in world space vec3 fragNormal = normalize(_normal); TransformCamera cam = getTransformCamera(); + vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + + vec4 localLighting = vec4(0.0); + + <$fetchClusterInfo(_worldPosition)$>; + if (hasLocalLights(numLights, clusterPos, dims)) { + localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface, + metallic, fresnel, albedo, 0.0, + vec4(0), vec4(0), opacity); + } _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( cam._viewInverse, 1.0, occlusionTex, fragPosition, - fragNormal, albedo, fresnel, metallic, emissive+fadeEmissive, - roughness, opacity), + surface, opacity, localLighting.rgb), opacity); } diff --git a/libraries/render-utils/src/model_translucent_fade.slv b/libraries/render-utils/src/model_translucent_fade.slv new file mode 100644 index 0000000000..3fb9ad2cb4 --- /dev/null +++ b/libraries/render-utils/src/model_translucent_fade.slv @@ -0,0 +1,44 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// model_translucent_fade.slv +// vertex shader +// +// Created by Olivier Prat on 15/01/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTexMapArrayBuffer()$> + +out float _alpha; +out vec2 _texCoord0; +out vec2 _texCoord1; +out vec4 _position; +out vec4 _worldPosition; +out vec3 _normal; +out vec3 _color; + +void main(void) { + _color = colorToLinearRGB(inColor.xyz); + _alpha = inColor.w; + + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> + <$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$> + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> + <$transformModelToWorldPos(obj, inPosition, _worldPosition)$> + <$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$> +} diff --git a/libraries/render-utils/src/overlay3D.slf b/libraries/render-utils/src/overlay3D.slf index dcded917b4..b6df71ccc3 100644 --- a/libraries/render-utils/src/overlay3D.slf +++ b/libraries/render-utils/src/overlay3D.slf @@ -27,7 +27,7 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 albedo, float metallic, vec3 fresnel, float roughness, float opacity) { // Need the light now - Light light = getLight(); + Light light = getKeyLight(); vec3 lightDirection = getLightDirection(light); vec3 lightIrradiance = getLightIrradiance(light); diff --git a/libraries/render-utils/src/overlay3D_translucent.slf b/libraries/render-utils/src/overlay3D_translucent.slf index 64035ac984..c247364a8c 100644 --- a/libraries/render-utils/src/overlay3D_translucent.slf +++ b/libraries/render-utils/src/overlay3D_translucent.slf @@ -27,7 +27,7 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 albedo, float metallic, vec3 fresnel, float roughness, float opacity) { // Need the light now - Light light = getLight(); + Light light = getKeyLight(); vec3 lightDirection = getLightDirection(light); vec3 lightIrradiance = getLightIrradiance(light); diff --git a/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf b/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf index ad6918d727..e66d881e11 100644 --- a/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf +++ b/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf @@ -42,7 +42,7 @@ vec3 evalScatteringBRDF(vec2 texcoord) { vec3 fragNormal = vec3((normal)); // Get light - Light light = getLight(); + Light light = getKeyLight(); vec3 fresnel = vec3(0.028); // Default Di-electric fresnel value for skin float metallic = 0.0; @@ -65,7 +65,7 @@ vec3 drawScatteringTableUV(vec2 cursor, vec2 texcoord) { float curvature = unpackCurvature(diffusedCurvature.w); // Get light - Light light = getLight(); + Light light = getKeyLight(); vec3 fresnel = vec3(0.028); // Default Di-electric fresnel value for skin vec3 fragLightDir = -normalize(getLightDirection(light)); diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 8449c58c7c..b6868a2cd4 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -369,11 +369,15 @@ void Font::drawString(gpu::Batch& batch, float x, float y, const QString& str, c batch.setPipeline(((*color).a < 1.0f || layered) ? _transparentPipeline : _pipeline); batch.setResourceTexture(_fontLoc, _texture); - batch._glUniform1i(_outlineLoc, (effectType == OUTLINE_EFFECT)); + if (_outlineLoc >= 0) { + batch._glUniform1i(_outlineLoc, (effectType == OUTLINE_EFFECT)); + } // need the gamma corrected color here glm::vec4 lrgba = ColorUtils::sRGBToLinearVec4(*color); - batch._glUniform4fv(_colorLoc, 1, (const float*)&lrgba); + if (_colorLoc >= 0) { + batch._glUniform4fv(_colorLoc, 1, (const float*)&lrgba); + } batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); diff --git a/libraries/render-utils/src/zone_drawKeyLight.slf b/libraries/render-utils/src/zone_drawKeyLight.slf index ea11c775b4..5f0f0265d3 100644 --- a/libraries/render-utils/src/zone_drawKeyLight.slf +++ b/libraries/render-utils/src/zone_drawKeyLight.slf @@ -25,7 +25,7 @@ void main(void) { <$evalGlobeWidget()$> - Light light = getLight(); + Light light = getKeyLight(); vec3 lightDirection = normalize(getLightDirection(light)); vec3 lightIrradiance = getLightIrradiance(light); vec3 color = vec3(0.0); diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 4cf1b306ab..fb58c21dde 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -70,6 +70,8 @@ void ShapePlumber::addPipeline(const Key& key, const gpu::ShaderPointer& program void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& program, const gpu::StatePointer& state, BatchSetter batchSetter, ItemSetter itemSetter) { + ShapeKey key{ filter._flags }; + gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("lightingModelBuffer"), Slot::BUFFER::LIGHTING_MODEL)); slotBindings.insert(gpu::Shader::Binding(std::string("skinClusterBuffer"), Slot::BUFFER::SKINNING)); @@ -82,6 +84,7 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), Slot::MAP::EMISSIVE_LIGHTMAP)); slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), Slot::MAP::OCCLUSION)); slotBindings.insert(gpu::Shader::Binding(std::string("scatteringMap"), Slot::MAP::SCATTERING)); + slotBindings.insert(gpu::Shader::Binding(std::string("keyLightBuffer"), Slot::BUFFER::KEY_LIGHT)); slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), Slot::BUFFER::LIGHT)); slotBindings.insert(gpu::Shader::Binding(std::string("lightAmbientBuffer"), Slot::BUFFER::LIGHT_AMBIENT_BUFFER)); slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), Slot::MAP::LIGHT_AMBIENT)); @@ -89,6 +92,12 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), Slot::BUFFER::FADE_PARAMETERS)); slotBindings.insert(gpu::Shader::Binding(std::string("hazeBuffer"), Slot::BUFFER::HAZE_MODEL)); + if (key.isTranslucent()) { + slotBindings.insert(gpu::Shader::Binding(std::string("clusterGridBuffer"), Slot::BUFFER::LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("clusterContentBuffer"), Slot::BUFFER::LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("frustumGridBuffer"), Slot::BUFFER::LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT)); + } + gpu::Shader::makeProgram(*program, slotBindings); auto locations = std::make_shared(); @@ -103,14 +112,23 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p locations->skinClusterBufferUnit = program->getUniformBuffers().findLocation("skinClusterBuffer"); locations->materialBufferUnit = program->getUniformBuffers().findLocation("materialBuffer"); locations->texMapArrayBufferUnit = program->getUniformBuffers().findLocation("texMapArrayBuffer"); + locations->keyLightBufferUnit = program->getUniformBuffers().findLocation("keyLightBuffer"); locations->lightBufferUnit = program->getUniformBuffers().findLocation("lightBuffer"); locations->lightAmbientBufferUnit = program->getUniformBuffers().findLocation("lightAmbientBuffer"); locations->lightAmbientMapUnit = program->getTextures().findLocation("skyboxMap"); locations->fadeMaskTextureUnit = program->getTextures().findLocation("fadeMaskMap"); locations->fadeParameterBufferUnit = program->getUniformBuffers().findLocation("fadeParametersBuffer"); locations->hazeParameterBufferUnit = program->getUniformBuffers().findLocation("hazeParametersBuffer"); + if (key.isTranslucent()) { + locations->lightClusterGridBufferUnit = program->getUniformBuffers().findLocation("clusterGridBuffer"); + locations->lightClusterContentBufferUnit = program->getUniformBuffers().findLocation("clusterContentBuffer"); + locations->lightClusterFrustumBufferUnit = program->getUniformBuffers().findLocation("frustumGridBuffer"); + } else { + locations->lightClusterGridBufferUnit = -1; + locations->lightClusterContentBufferUnit = -1; + locations->lightClusterFrustumBufferUnit = -1; + } - ShapeKey key{filter._flags}; auto gpuPipeline = gpu::Pipeline::create(program, state); auto shapePipeline = std::make_shared(gpuPipeline, locations, batchSetter, itemSetter); addPipelineHelper(filter, key, 0, shapePipeline); @@ -131,7 +149,7 @@ const ShapePipelinePointer ShapePlumber::pickPipeline(RenderArgs* args, const Ke auto factoryIt = ShapePipeline::_globalCustomFactoryMap.find(key.getCustom()); if ((factoryIt != ShapePipeline::_globalCustomFactoryMap.end()) && (factoryIt)->second) { // found a factory for the custom key, can now generate a shape pipeline for this case: - addPipelineHelper(Filter(key), key, 0, (factoryIt)->second(*this, key)); + addPipelineHelper(Filter(key), key, 0, (factoryIt)->second(*this, key, *(args->_batch))); return pickPipeline(args, key); } else { diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index be77e2f95e..1dd9f5da49 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -235,10 +235,15 @@ public: MATERIAL, TEXMAPARRAY, LIGHTING_MODEL, + KEY_LIGHT, LIGHT, LIGHT_AMBIENT_BUFFER, HAZE_MODEL, FADE_PARAMETERS, + LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, + LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, + LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, + }; enum MAP { @@ -266,12 +271,16 @@ public: int skinClusterBufferUnit; int materialBufferUnit; int texMapArrayBufferUnit; + int keyLightBufferUnit; int lightBufferUnit; int lightAmbientBufferUnit; int lightAmbientMapUnit; int fadeMaskTextureUnit; int fadeParameterBufferUnit; int hazeParameterBufferUnit; + int lightClusterGridBufferUnit; + int lightClusterContentBufferUnit; + int lightClusterFrustumBufferUnit; }; using LocationsPointer = std::shared_ptr; @@ -301,7 +310,7 @@ protected: ItemSetter _itemSetter; public: using CustomKey = uint8_t; - using CustomFactory = std::function (const ShapePlumber& plumber, const ShapeKey& key)>; + using CustomFactory = std::function (const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch)>; using CustomFactoryMap = std::map; static CustomFactoryMap _globalCustomFactoryMap; diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index 69c2cc0799..166a21b773 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -19,6 +19,7 @@ #include #include #include +#include class ScriptEngine; diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.cpp b/libraries/script-engine/src/ConsoleScriptingInterface.cpp index f3ceee63f7..b4ef98938d 100644 --- a/libraries/script-engine/src/ConsoleScriptingInterface.cpp +++ b/libraries/script-engine/src/ConsoleScriptingInterface.cpp @@ -15,6 +15,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include "ConsoleScriptingInterface.h" #include "ScriptEngine.h" diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c7034adf35..9d073453ae 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1390,7 +1390,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const { } QUrl ScriptEngine::resourcesPath() const { - return QUrl::fromLocalFile(PathUtils::resourcesPath()); + return QUrl(PathUtils::resourcesUrl()); } void ScriptEngine::print(const QString& message) { diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 6cfa678652..78cb05fa0d 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -62,8 +62,7 @@ void ScriptEngines::onErrorLoadingScript(const QString& url) { } ScriptEngines::ScriptEngines(ScriptEngine::Context context) - : _context(context), - _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION) + : _context(context) { _scriptsModelFilter.setSourceModel(&_scriptsModel); _scriptsModelFilter.sort(0, Qt::AscendingOrder); @@ -429,13 +428,8 @@ bool ScriptEngines::stopScript(const QString& rawScriptURL, bool restart) { return stoppedScript; } -QString ScriptEngines::getScriptsLocation() const { - return _scriptsLocationHandle.get(); -} - -void ScriptEngines::setScriptsLocation(const QString& scriptsLocation) { - _scriptsLocationHandle.set(scriptsLocation); - _scriptsModel.updateScriptsLocation(scriptsLocation); +void ScriptEngines::reloadLocalFiles() { + _scriptsModel.reloadLocalFiles(); } void ScriptEngines::reloadAllScripts() { diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 0f807b08cf..5a4b8f2f47 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -45,9 +45,9 @@ public: QString getDebugScriptUrl() { return _debugScriptUrl; }; void setDebugScriptUrl(const QString& url) { _debugScriptUrl = url; }; - QString getScriptsLocation() const; void loadDefaultScripts(); - void setScriptsLocation(const QString& scriptsLocation); + void reloadLocalFiles(); + QStringList getRunningScripts(); ScriptEnginePointer getScriptEngine(const QUrl& scriptHash); @@ -115,7 +115,6 @@ protected: QSet _allKnownScriptEngines; QMutex _allScriptsMutex; std::list _scriptInitializers; - mutable Setting::Handle _scriptsLocationHandle; ScriptsModel _scriptsModel; ScriptsModelFilter _scriptsModelFilter; std::atomic _isStopped { false }; diff --git a/libraries/script-engine/src/ScriptsModel.cpp b/libraries/script-engine/src/ScriptsModel.cpp index 9b7a367773..5f30033bf9 100644 --- a/libraries/script-engine/src/ScriptsModel.cpp +++ b/libraries/script-engine/src/ScriptsModel.cpp @@ -100,6 +100,7 @@ QVariant ScriptsModel::data(const QModelIndex& index, int role) const { return QVariant(); } if (node->getType() == TREE_NODE_TYPE_SCRIPT) { + TreeNodeScript* script = static_cast(node); if (role == Qt::DisplayRole) { return QVariant(script->getName() + (script->getOrigin() == SCRIPT_ORIGIN_LOCAL ? " (local)" : "")); @@ -133,7 +134,6 @@ void ScriptsModel::updateScriptsLocation(const QString& newPath) { _fsWatcher.addPath(_localDirectory.absolutePath()); } } - reloadLocalFiles(); } @@ -305,6 +305,7 @@ void ScriptsModel::rebuildTree() { _treeNodes.removeAt(i); } } + QHash folders; for (int i = 0; i < _treeNodes.size(); i++) { TreeNodeBase* node = _treeNodes.at(i); diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index f9b835df5c..3d61765988 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -7,5 +7,9 @@ if (WIN32) target_link_libraries(${TARGET_NAME} Wbemuuid.lib) endif() +if (ANDROID) + target_link_libraries(${TARGET_NAME} android) +endif() + target_zlib() target_nsight() \ No newline at end of file diff --git a/libraries/shared/src/BitVectorHelpers.h b/libraries/shared/src/BitVectorHelpers.h new file mode 100644 index 0000000000..9f5af0c380 --- /dev/null +++ b/libraries/shared/src/BitVectorHelpers.h @@ -0,0 +1,64 @@ +// +// BitVectorHelpers.h +// libraries/shared/src +// +// Created by Anthony Thibault on 1/19/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_BitVectorHelpers_h +#define hifi_BitVectorHelpers_h + +int calcBitVectorSize(int numBits) { + return ((numBits - 1) >> 3) + 1; +} + +// func should be of type bool func(int index) +template +int writeBitVector(uint8_t* destinationBuffer, int numBits, const F& func) { + int totalBytes = calcBitVectorSize(numBits); + uint8_t* cursor = destinationBuffer; + uint8_t byte = 0; + uint8_t bit = 0; + + for (int i = 0; i < numBits; i++) { + if (func(i)) { + byte |= (1 << bit); + } + if (++bit == BITS_IN_BYTE) { + *cursor++ = byte; + byte = 0; + bit = 0; + } + } + // write the last byte, if necessary + if (bit != 0) { + *cursor++ = byte; + } + + assert((int)(cursor - destinationBuffer) == totalBytes); + return totalBytes; +} + +// func should be of type 'void func(int index, bool value)' +template +int readBitVector(const uint8_t* sourceBuffer, int numBits, const F& func) { + int totalBytes = calcBitVectorSize(numBits); + const uint8_t* cursor = sourceBuffer; + uint8_t bit = 0; + + for (int i = 0; i < numBits; i++) { + bool value = (bool)(*cursor & (1 << bit)); + func(i, value); + if (++bit == BITS_IN_BYTE) { + cursor++; + bit = 0; + } + } + return totalBytes; +} + +#endif diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 973998b927..4f761a4aac 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -253,6 +253,7 @@ glm::vec2 getFacingDir2D(const glm::mat4& m); inline bool isNaN(const glm::vec3& value) { return isNaN(value.x) || isNaN(value.y) || isNaN(value.z); } inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z); } +inline bool isNaN(const glm::mat3& value) { return isNaN(value * glm::vec3(1.0f)); } glm::mat4 orthoInverse(const glm::mat4& m); diff --git a/libraries/shared/src/JointData.h b/libraries/shared/src/JointData.h index a05b5c649a..f4c8b89e7a 100644 --- a/libraries/shared/src/JointData.h +++ b/libraries/shared/src/JointData.h @@ -5,14 +5,27 @@ #include #include -// Used by the avatar mixer to describe a single joint -// These are relative to their parent and translations are in meters -class JointData { +class EntityJointData { public: glm::quat rotation; + glm::vec3 translation; bool rotationSet = false; - glm::vec3 translation; // meters bool translationSet = false; }; +// Used by the avatar mixer to describe a single joint +// Translations relative to their parent and are in meters. +// Rotations are absolute (i.e. not relative to parent) and are in rig space. +class JointData { +public: + glm::quat rotation; + glm::vec3 translation; + + // This indicates that the rotation or translation is the same as the defaultPose for the avatar. + // if true, it also means that the rotation or translation value in this structure is not valid and + // should be replaced by the avatar's actual default pose value. + bool rotationIsDefaultPose = true; + bool translationIsDefaultPose = true; +}; + #endif diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index b33b330fc0..35d7554f39 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -11,16 +11,25 @@ #include "PathUtils.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include #include // std::once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(Q_OS_OSX) +#include +#endif + + #include "shared/GlobalAppProperties.h" #include "SharedUtil.h" @@ -28,14 +37,14 @@ // Example: ... QString TEMP_DIR_FORMAT { "%1-%2-%3" }; -const QString& PathUtils::resourcesPath() { -#ifdef Q_OS_MAC - static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; +#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) +#if defined(Q_OS_OSX) +static bool USE_SOURCE_TREE_RESOURCES = true; #else - static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/"; +static const QString USE_SOURCE_TREE_RESOURCES_FLAG("HIFI_USE_SOURCE_TREE_RESOURCES"); +static bool USE_SOURCE_TREE_RESOURCES = QProcessEnvironment::systemEnvironment().contains(USE_SOURCE_TREE_RESOURCES_FLAG); +#endif #endif - return staticResourcePath; -} #ifdef DEV_BUILD const QString& PathUtils::projectRootPath() { @@ -49,16 +58,71 @@ const QString& PathUtils::projectRootPath() { } #endif -const QString& PathUtils::qmlBasePath() { -#ifdef DEV_BUILD - static const QString staticResourcePath = QUrl::fromLocalFile(projectRootPath() + "/interface/resources/qml/").toString(); +const QString& PathUtils::resourcesPath() { + static QString staticResourcePath; + static std::once_flag once; + std::call_once(once, [&]{ + +#if defined(Q_OS_OSX) + // FIXME fix the OSX installer to install the resources.rcc instead of the + // individual resource files + // FIXME the first call to fetch the resources location seems to return + // nothing for QCoreApplication::applicationDirPath() + char buffer[8192]; + uint32_t bufferSize = sizeof(buffer); + _NSGetExecutablePath(buffer, &bufferSize); + staticResourcePath = QDir::cleanPath(QFileInfo(buffer).dir().absoluteFilePath("../Resources")) + "/"; #else - static const QString staticResourcePath = "qrc:///qml/"; + staticResourcePath = ":/"; #endif + +#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) + if (USE_SOURCE_TREE_RESOURCES) { + // For dev builds, optionally load content from the Git source tree + staticResourcePath = projectRootPath() + "/interface/resources/"; + } +#endif + qDebug() << "Resource path resolved to " << staticResourcePath; + }); return staticResourcePath; } +const QString& PathUtils::resourcesUrl() { + static QString staticResourcePath; + static std::once_flag once; + std::call_once(once, [&]{ + +#if defined(Q_OS_OSX) + staticResourcePath = QUrl::fromLocalFile(resourcesPath()).toString(); +#else + staticResourcePath = "qrc:///"; +#endif + +#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) + if (USE_SOURCE_TREE_RESOURCES) { + // For dev builds, optionally load content from the Git source tree + staticResourcePath = QUrl::fromLocalFile(projectRootPath() + "/interface/resources/").toString(); + } +#endif + qDebug() << "Resource url resolved to " << staticResourcePath; + }); + return staticResourcePath; +} + +QUrl PathUtils::resourcesUrl(const QString& relativeUrl) { + return QUrl(resourcesUrl() + relativeUrl); +} + +const QString& PathUtils::qmlBaseUrl() { + static const QString staticResourcePath = resourcesUrl() + "qml/"; + return staticResourcePath; +} + +QUrl PathUtils::qmlUrl(const QString& relativeUrl) { + return QUrl(qmlBaseUrl() + relativeUrl); +} + QString PathUtils::getAppDataPath() { return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/"; } @@ -71,7 +135,11 @@ QString PathUtils::getAppLocalDataPath() { } // otherwise return standard path +#ifdef Q_OS_ANDROID + return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/"; +#else return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/"; +#endif } QString PathUtils::getAppDataFilePath(const QString& filename) { diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 2b4fe35d97..ec71ccee6a 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -33,8 +33,11 @@ class PathUtils : public QObject, public Dependency { Q_PROPERTY(QString resources READ resourcesPath CONSTANT) Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation CONSTANT) public: + static const QString& resourcesUrl(); + static QUrl resourcesUrl(const QString& relative); static const QString& resourcesPath(); - static const QString& qmlBasePath(); + static const QString& qmlBaseUrl(); + static QUrl qmlUrl(const QString& relative); #ifdef DEV_BUILD static const QString& projectRootPath(); #endif @@ -54,7 +57,6 @@ public: // note: this is FS-case-sensitive version of parentURL.isParentOf(childURL) static bool isDescendantOf(const QUrl& childURL, const QUrl& parentURL); static QUrl defaultScriptsLocation(const QString& newDefault = ""); - }; QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions); diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 20a5a76b73..324cee3417 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -464,6 +464,36 @@ glm::vec3 SpatiallyNestable::localToWorldDimensions(const glm::vec3& dimensions, return dimensions; } +void SpatiallyNestable::setWorldTransform(const glm::vec3& position, const glm::quat& orientation) { + // guard against introducing NaN into the transform + if (isNaN(orientation) || isNaN(position)) { + return; + } + + bool changed = false; + bool success = true; + Transform parentTransform = getParentTransform(success); + _transformLock.withWriteLock([&] { + Transform myWorldTransform; + Transform::mult(myWorldTransform, parentTransform, _transform); + if (myWorldTransform.getRotation() != orientation) { + changed = true; + myWorldTransform.setRotation(orientation); + } + if (myWorldTransform.getTranslation() != position) { + changed = true; + myWorldTransform.setTranslation(position); + } + if (changed) { + Transform::inverseMult(_transform, parentTransform, myWorldTransform); + _translationChanged = usecTimestampNow(); + } + }); + if (success && changed) { + locationChanged(false); + } +} + glm::vec3 SpatiallyNestable::getWorldPosition(bool& success) const { return getTransform(success).getTranslation(); } diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 2a315e9230..090ca4c266 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -87,6 +87,7 @@ public: virtual Transform getParentTransform(bool& success, int depth = 0) const; + void setWorldTransform(const glm::vec3& position, const glm::quat& orientation); virtual glm::vec3 getWorldPosition(bool& success) const; virtual glm::vec3 getWorldPosition() const; virtual void setWorldPosition(const glm::vec3& position, bool& success, bool tellPhysics = true); diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp index dba0af7b16..7b88321e99 100644 --- a/libraries/shared/src/shared/FileUtils.cpp +++ b/libraries/shared/src/shared/FileUtils.cpp @@ -12,6 +12,8 @@ #include "FileUtils.h" +#include + #include #include #include @@ -21,8 +23,21 @@ #include #include + #include "../SharedLogging.h" +const QStringList& FileUtils::getFileSelectors() { + static std::once_flag once; + static QStringList extraSelectors; + std::call_once(once, [] { +#if defined(USE_GLES) + extraSelectors << "gles"; +#endif + }); + return extraSelectors; + +} + QString FileUtils::readFile(const QString& filename) { QFile file(filename); @@ -84,7 +99,11 @@ void FileUtils::locateFile(QString filePath) { QString FileUtils::standardPath(QString subfolder) { // standard path // Mac: ~/Library/Application Support/Interface +#ifdef Q_OS_ANDROID + QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); +#else QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); +#endif if (!subfolder.startsWith("/")) { subfolder.prepend("/"); } diff --git a/libraries/shared/src/shared/FileUtils.h b/libraries/shared/src/shared/FileUtils.h index d68fcd8a44..9a5e2d5aba 100644 --- a/libraries/shared/src/shared/FileUtils.h +++ b/libraries/shared/src/shared/FileUtils.h @@ -13,10 +13,11 @@ #define hifi_FileUtils_h #include - +#include class FileUtils { public: + static const QStringList& getFileSelectors(); static void locateFile(QString fileName); static QString standardPath(QString subfolder); static QString readFile(const QString& filename); diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 7e9f86b049..683903a4e8 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -55,7 +55,7 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u if (!file.resize(size)) { throw std::runtime_error("Unable to resize file"); } - { + if (data) { auto mapped = file.map(0, size); if (!mapped) { throw std::runtime_error("Unable to map file"); @@ -79,12 +79,14 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } if (opened) { - _mapped = _file.map(0, _file.size()); - if (_mapped) { - _valid = true; - } else { - qCWarning(storagelogging) << "Failed to map file " << filename; - } + _size = _file.size(); + _mapped = _file.map(0, _size); + if (!_mapped) { + qCDebug(storagelogging) << "Failed to map file, falling back to memory storage " << filename; + _fallback = _file.readAll(); + _mapped = (uint8_t*)_fallback.data(); + } + _valid = true; } else { qCWarning(storagelogging) << "Failed to open file " << filename; } @@ -92,7 +94,9 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { FileStorage::~FileStorage() { if (_mapped) { - _file.unmap(_mapped); + if (_fallback.isEmpty()) { + _file.unmap(_mapped); + } _mapped = nullptr; } if (_file.isOpen()) { diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index d7946738cf..0e5032bb62 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -61,10 +61,12 @@ namespace storage { const uint8_t* data() const override { return _mapped; } uint8_t* mutableData() override { return _hasWriteAccess ? _mapped : nullptr; } - size_t size() const override { return _file.size(); } + size_t size() const override { return _size; } operator bool() const override { return _valid; } private: - + // For compressed QRC files we can't map the file object, so we need to read it into memory + QByteArray _fallback; + size_t _size { 0 }; bool _valid { false }; bool _hasWriteAccess { false }; QFile _file; diff --git a/libraries/ui/src/DesktopPreviewProvider.cpp b/libraries/ui/src/DesktopPreviewProvider.cpp new file mode 100644 index 0000000000..31aa7a22e2 --- /dev/null +++ b/libraries/ui/src/DesktopPreviewProvider.cpp @@ -0,0 +1,46 @@ +#include "DesktopPreviewProvider.h" +#include +#include +#include +#include + +DesktopPreviewProvider::DesktopPreviewProvider() { +} + +constexpr const char* DesktopPreviewProvider::imagePaths[]; + +QSharedPointer DesktopPreviewProvider::getInstance() { + static QSharedPointer instance = DependencyManager::get(); + return instance; +} + +QImage DesktopPreviewProvider::getPreviewDisabledImage(bool vsyncEnabled) const { + + auto imageIndex = vsyncEnabled ? VSYNC : m_previewDisabledReason; + assert(imageIndex >= 0 && imageIndex <= VSYNC); + + return !m_previewDisabled[imageIndex].isNull() ? m_previewDisabled[imageIndex] : loadPreviewImage(m_previewDisabled[imageIndex], PathUtils::resourcesPath() + imagePaths[imageIndex]); +} + +void DesktopPreviewProvider::setPreviewDisabledReason(PreviewDisabledReasons reason) { + if (reason == VSYNC) { + qDebug() << "Preview disabled reason can't be forced to " << QMetaEnum::fromType().valueToKey(reason); + return; // Not settable via this interface, as VSYNC is controlled by HMD plugin.. + } + + m_previewDisabledReason = reason; +} + +void DesktopPreviewProvider::setPreviewDisabledReason(const QString& reasonString) { + PreviewDisabledReasons reason = USER; + bool ok = false; + + reason = (PreviewDisabledReasons) QMetaEnum::fromType().keyToValue(reasonString.toLatin1().data(), &ok); + if (ok) { + setPreviewDisabledReason(reason); + } +} + +QImage& DesktopPreviewProvider::loadPreviewImage(QImage& image, const QString& path) const { + return image = QImage(path).mirrored().convertToFormat(QImage::Format_RGBA8888); +} diff --git a/libraries/ui/src/DesktopPreviewProvider.h b/libraries/ui/src/DesktopPreviewProvider.h new file mode 100644 index 0000000000..449c3723e1 --- /dev/null +++ b/libraries/ui/src/DesktopPreviewProvider.h @@ -0,0 +1,47 @@ +// +// Created by Alexander Ivash on 2018/01/08 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include + +class DesktopPreviewProvider : public QObject, public Dependency { + SINGLETON_DEPENDENCY + Q_OBJECT + + DesktopPreviewProvider(); + DesktopPreviewProvider(const DesktopPreviewProvider& other) = delete; + + constexpr static const char* imagePaths[] = { + "images/preview-disabled.png", // USER + "images/preview-privacy.png", // SECURE_SCREEN + "images/preview.png", // VSYNC + }; + +public: + enum PreviewDisabledReasons { + USER = 0, + SECURE_SCREEN, + VSYNC // Not settable via this interface, as VSYNC is controlled by HMD plugin.. + }; + Q_ENUM(PreviewDisabledReasons) + + static QSharedPointer getInstance(); + + QImage getPreviewDisabledImage(bool vsyncEnabled) const; + void setPreviewDisabledReason(PreviewDisabledReasons reason); + +public slots: + void setPreviewDisabledReason(const QString& reason); + +private: + QImage& loadPreviewImage(QImage& image, const QString& path) const; + + PreviewDisabledReasons m_previewDisabledReason = { USER }; + + mutable QImage m_previewDisabled[3]; +}; diff --git a/libraries/ui/src/InfoView.cpp b/libraries/ui/src/InfoView.cpp index cb80e3f6db..650d43831c 100644 --- a/libraries/ui/src/InfoView.cpp +++ b/libraries/ui/src/InfoView.cpp @@ -46,7 +46,7 @@ void InfoView::show(const QString& path, bool firstOrChangedOnly, QString urlQue registerType(); QUrl url; if (QDir(path).isRelative()) { - url = QUrl::fromLocalFile(PathUtils::resourcesPath() + path); + url = PathUtils::resourcesUrl(path); } else { url = QUrl::fromLocalFile(path); } diff --git a/libraries/ui/src/OffscreenQmlElement.h b/libraries/ui/src/OffscreenQmlElement.h index 4e07fcccd9..aa4b8cf1a9 100644 --- a/libraries/ui/src/OffscreenQmlElement.h +++ b/libraries/ui/src/OffscreenQmlElement.h @@ -13,6 +13,9 @@ #define hifi_OffscreenQmlElement_h #include + +#include + class QQmlContext; #define HIFI_QML_DECL \ @@ -40,7 +43,7 @@ public: \ private: #define HIFI_QML_DEF(x) \ - const QUrl x::QML = QUrl(#x ".qml"); \ + const QUrl x::QML = PathUtils::qmlUrl(#x ".qml"); \ const QString x::NAME = #x; \ \ void x::registerType() { \ diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 902f91f9b8..db3df34dc5 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -6,14 +6,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "OffscreenQmlSurface.h" -#include "ImageProvider.h" -// Has to come before Qt GL includes -#include +#include #include #include +#include + #include #include #include @@ -29,7 +29,6 @@ #include #include -#include #include #include #include @@ -46,8 +45,10 @@ #include #include #include +#include #include +#include "ImageProvider.h" #include "types/FileTypeProfile.h" #include "types/HFWebEngineProfile.h" #include "types/SoundEffect.h" @@ -70,7 +71,7 @@ public: withWriteLock([&] { for (auto url : urls) { if (url.isRelative()) { - url = QUrl(PathUtils::qmlBasePath() + url.toString()); + url = PathUtils::qmlUrl(url.toString()); } _callbacks[url].push_back(callback); } @@ -282,7 +283,7 @@ private: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); -#if !defined(Q_OS_ANDROID) +#if !defined(USE_GLES) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.2f); #endif glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); @@ -443,7 +444,7 @@ void initializeQmlEngine(QQmlEngine* engine, QQuickWindow* window) { auto rootContext = engine->rootContext(); rootContext->setContextProperty("GL", ::getGLContextData()); rootContext->setContextProperty("urlHandler", new UrlHandler()); - rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath())); + rootContext->setContextProperty("resourceDirectoryUrl", QUrl(PathUtils::resourcesUrl())); rootContext->setContextProperty("pathToFonts", "../../"); rootContext->setContextProperty("ApplicationInterface", qApp); auto javaScriptToInject = getEventBridgeJavascript(); @@ -519,6 +520,7 @@ QOpenGLContext* OffscreenQmlSurface::getSharedContext() { void OffscreenQmlSurface::cleanup() { _isCleaned = true; +#if !defined(DISABLE_QML) _canvas->makeCurrent(); _renderControl->invalidate(); @@ -536,9 +538,11 @@ void OffscreenQmlSurface::cleanup() { offscreenTextures.releaseSize(_size); _canvas->doneCurrent(); +#endif } void OffscreenQmlSurface::render() { +#if !defined(DISABLE_QML) if (nsightActive()) { return; } @@ -578,6 +582,7 @@ void OffscreenQmlSurface::render() { _quickWindow->resetOpenGLState(); _lastRenderTime = usecTimestampNow(); _canvas->doneCurrent(); +#endif } bool OffscreenQmlSurface::fetchTexture(TextureAndFence& textureAndFence) { @@ -622,7 +627,9 @@ OffscreenQmlSurface::~OffscreenQmlSurface() { cleanup(); auto engine = _qmlContext->engine(); +#if !defined(DISABLE_QML) _canvas->deleteLater(); +#endif _rootItem->deleteLater(); _quickWindow->deleteLater(); releaseEngine(engine); @@ -647,6 +654,7 @@ void OffscreenQmlSurface::disconnectAudioOutputTimer() { void OffscreenQmlSurface::create() { qCDebug(uiLogging) << "Building QML surface"; +#if !defined(DISABLE_QML) _renderControl = new QMyQuickRenderControl(); connect(_renderControl, &QQuickRenderControl::renderRequested, this, [this] { _render = true; }); connect(_renderControl, &QQuickRenderControl::sceneChanged, this, [this] { _render = _polish = true; }); @@ -663,26 +671,30 @@ void OffscreenQmlSurface::create() { _quickWindow->setClearBeforeRendering(false); _renderControl->_renderWindow = _proxyWindow; - _canvas = new OffscreenGLCanvas(); if (!_canvas->create(getSharedContext())) { qFatal("Failed to create OffscreenGLCanvas"); return; }; - connect(_quickWindow, &QQuickWindow::focusObjectChanged, this, &OffscreenQmlSurface::onFocusObjectChanged); - // acquireEngine interrogates the GL context, so we need to have the context current here if (!_canvas->makeCurrent()) { qFatal("Failed to make context current for QML Renderer"); return; } +#else + _quickWindow = new QQuickWindow(); +#endif + + + connect(_quickWindow, &QQuickWindow::focusObjectChanged, this, &OffscreenQmlSurface::onFocusObjectChanged); + // Create a QML engine. auto qmlEngine = acquireEngine(_quickWindow); _qmlContext = new QQmlContext(qmlEngine->rootContext()); - _qmlContext->setBaseUrl(QUrl{ PathUtils::qmlBasePath() }); + _qmlContext->setBaseUrl(QUrl{ PathUtils::qmlBaseUrl() }); _qmlContext->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); _qmlContext->setContextProperty("eventBridge", this); _qmlContext->setContextProperty("webEntity", this); @@ -691,7 +703,10 @@ void OffscreenQmlSurface::create() { // FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper // Find a way to flag older scripts using this mechanism and wanr that this is deprecated _qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, _qmlContext)); + +#if !defined(DISABLE_QML) _renderControl->initialize(_canvas->getContext()); +#endif #if !defined(Q_OS_ANDROID) // Connect with the audio client and listen for audio device changes @@ -791,6 +806,7 @@ void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { return; } +#if !defined(DISABLE_QML) qCDebug(uiLogging) << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height(); gl::withSavedContext([&] { _canvas->makeCurrent(); @@ -827,6 +843,7 @@ void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { _canvas->doneCurrent(); }); +#endif } QQuickItem* OffscreenQmlSurface::getRootItem() { @@ -1009,7 +1026,9 @@ void OffscreenQmlSurface::updateQuick() { if (_polish) { PROFILE_RANGE(render_qml, "OffscreenQML polish") +#if !defined(DISABLE_QML) _renderControl->polishItems(); +#endif _polish = false; } @@ -1097,6 +1116,27 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even } break; } +#if defined(Q_OS_ANDROID) + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: { + QTouchEvent *originalEvent = static_cast(event); + QTouchEvent *fakeEvent = new QTouchEvent(*originalEvent); + auto newTouchPoints = fakeEvent->touchPoints(); + for (size_t i = 0; i < newTouchPoints.size(); ++i) { + const auto &originalPoint = originalEvent->touchPoints()[i]; + auto &newPoint = newTouchPoints[i]; + newPoint.setPos(originalPoint.pos()); + } + fakeEvent->setTouchPoints(newTouchPoints); + if (QCoreApplication::sendEvent(_quickWindow, fakeEvent)) { + qInfo() << __FUNCTION__ << "sent fake touch event:" << fakeEvent->type() + << "_quickWindow handled it... accepted:" << fakeEvent->isAccepted(); + return false; //event->isAccepted(); + } + break; + } +#endif default: break; } @@ -1279,9 +1319,11 @@ bool OffscreenQmlSurface::isPaused() const { void OffscreenQmlSurface::setProxyWindow(QWindow* window) { _proxyWindow = window; +#if !defined(DISABLE_QML) if (_renderControl) { _renderControl->_renderWindow = window; } +#endif } QObject* OffscreenQmlSurface::getEventHandler() { diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 4251bb6a9d..7aeac8d7c3 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -168,10 +168,13 @@ public slots: private: QQuickWindow* _quickWindow { nullptr }; - QMyQuickRenderControl* _renderControl{ nullptr }; QQmlContext* _qmlContext { nullptr }; QQuickItem* _rootItem { nullptr }; + +#if !defined(DISABLE_QML) + QMyQuickRenderControl* _renderControl{ nullptr }; OffscreenGLCanvas* _canvas { nullptr }; +#endif QTimer _updateTimer; uint32_t _fbo { 0 }; diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index c69ec1ce84..0191baca5e 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -31,6 +31,8 @@ const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; const QString TabletScriptingInterface::QML = "hifi/tablet/TabletRoot.qml"; +const QString BUTTON_SORT_ORDER_KEY = "sortOrder"; +const int DEFAULT_BUTTON_SORT_ORDER = 100; static QString getUsername() { QString username = "Unknown user"; @@ -74,11 +76,21 @@ QVariant TabletButtonListModel::data(const QModelIndex& index, int role) const { } TabletButtonProxy* TabletButtonListModel::addButton(const QVariant& properties) { - auto tabletButtonProxy = QSharedPointer(new TabletButtonProxy(properties.toMap())); + QVariantMap newProperties = properties.toMap(); + if (newProperties.find(BUTTON_SORT_ORDER_KEY) == newProperties.end()) { + newProperties[BUTTON_SORT_ORDER_KEY] = DEFAULT_BUTTON_SORT_ORDER; + } + int index = computeNewButtonIndex(newProperties); + auto button = QSharedPointer(new TabletButtonProxy(newProperties)); beginResetModel(); - _buttons.push_back(tabletButtonProxy); + int numButtons = (int)_buttons.size(); + if (index < numButtons) { + _buttons.insert(_buttons.begin() + index, button); + } else { + _buttons.push_back(button); + } endResetModel(); - return tabletButtonProxy.data(); + return button.data(); } void TabletButtonListModel::removeButton(TabletButtonProxy* button) { @@ -92,6 +104,20 @@ void TabletButtonListModel::removeButton(TabletButtonProxy* button) { endResetModel(); } +int TabletButtonListModel::computeNewButtonIndex(const QVariantMap& newButtonProperties) { + int numButtons = (int)_buttons.size(); + int newButtonSortOrder = newButtonProperties[BUTTON_SORT_ORDER_KEY].toInt(); + if (newButtonSortOrder == DEFAULT_BUTTON_SORT_ORDER) return numButtons; + for (int i = 0; i < numButtons; i++) { + QVariantMap tabletButtonProperties = _buttons[i]->getProperties(); + int tabletButtonSortOrder = tabletButtonProperties[BUTTON_SORT_ORDER_KEY].toInt(); + if (newButtonSortOrder <= tabletButtonSortOrder) { + return i; + } + } + return numButtons; +} + TabletButtonsProxyModel::TabletButtonsProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } @@ -172,9 +198,8 @@ void TabletScriptingInterface::preloadSounds() { //preload audio events const QStringList &audioSettings = tabletSoundsButtonClick.get(); for (int i = 0; i < TabletAudioEvents::Last; i++) { - QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + audioSettings.at(i)); SharedSoundPointer sound = DependencyManager::get()-> - getSound(QUrl::fromLocalFile(inf.absoluteFilePath())); + getSound(PathUtils::resourcesUrl(audioSettings.at(i))); _audioEvents.insert(static_cast(i), sound); } } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 56e3ae257b..34827117f0 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -115,6 +115,7 @@ protected: friend class TabletProxy; TabletButtonProxy* addButton(const QVariant& properties); void removeButton(TabletButtonProxy* button); + int computeNewButtonIndex(const QVariantMap& newButtonProperties); using List = std::list>; static QHash _roles; static Qt::ItemFlags _flags; diff --git a/libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.h b/libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.h index e38549937e..8be2974782 100644 --- a/libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.h +++ b/libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.h @@ -11,14 +11,20 @@ #ifndef hifi_HFTabletWebEngineRequestInterceptor_h #define hifi_HFTabletWebEngineRequestInterceptor_h +#if !defined(Q_OS_ANDROID) +#include #include -class HFTabletWebEngineRequestInterceptor : public QWebEngineUrlRequestInterceptor { +class HFTabletWebEngineRequestInterceptor + : public QWebEngineUrlRequestInterceptor +{ public: - HFTabletWebEngineRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {}; - + HFTabletWebEngineRequestInterceptor(QObject* parent) + : QWebEngineUrlRequestInterceptor(parent) + {}; virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override; }; +#endif #endif // hifi_HFWebEngineRequestInterceptor_h diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 55eb9c7b85..59986d103e 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -6,7 +6,7 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -if (WIN32) +if (WIN32 AND (NOT USE_GLES)) # we're using static GLEW, so define GLEW_STATIC add_definitions(-DGLEW_STATIC) @@ -19,9 +19,10 @@ if (WIN32) set(TARGET_NAME oculus) setup_hifi_plugin(Multimedia) link_hifi_libraries( - shared gl gpu gpu-gl controllers ui + shared gl gpu controllers ui plugins ui-plugins display-plugins input-plugins audio-client networking render-utils + ${PLATFORM_GL_BACKEND} ) include_hifi_library_headers(octree) diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 7e7fcc0db3..d03419d025 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "OculusHelpers.h" @@ -92,9 +93,7 @@ glm::mat4 OculusBaseDisplayPlugin::getCullingProjection(const glm::mat4& basePro // DLL based display plugins MUST initialize GLEW inside the DLL code. void OculusBaseDisplayPlugin::customizeContext() { - glewExperimental = true; - GLenum err = glewInit(); - glGetError(); // clear the potential error from glewExperimental + gl::initModuleGl(); Parent::customizeContext(); } diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt index 12d0236cc2..00e90fb6d7 100644 --- a/plugins/oculusLegacy/CMakeLists.txt +++ b/plugins/oculusLegacy/CMakeLists.txt @@ -13,7 +13,7 @@ if (APPLE) set(TARGET_NAME oculusLegacy) setup_hifi_plugin() - link_hifi_libraries(shared gl gpu gpu-gl plugins ui ui-plugins display-plugins input-plugins midi) + link_hifi_libraries(shared gl gpu plugins ui ui-plugins display-plugins input-plugins midi ${PLATFORM_GL_BACKEND}) include_hifi_library_headers(octree) diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 804ff7d62e..e6b555443f 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -9,6 +9,8 @@ #include +#include + #include #include #include @@ -192,14 +194,9 @@ void OculusLegacyDisplayPlugin::internalDeactivate() { _container->makeRenderingContextCurrent(); } -// DLL based display plugins MUST initialize GLEW inside the DLL code. +// DLL based display plugins MUST initialize GL inside the DLL code. void OculusLegacyDisplayPlugin::customizeContext() { - static std::once_flag once; - std::call_once(once, []{ - glewExperimental = true; - glewInit(); - glGetError(); - }); + gl::initModuleGl(); _hmdWindow->requestActivate(); QThread::msleep(1000); Parent::customizeContext(); diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 9ae9b25b65..3581009284 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -6,14 +6,14 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -if (WIN32) +if (WIN32 AND (NOT USE_GLES)) # we're using static GLEW, so define GLEW_STATIC add_definitions(-DGLEW_STATIC) set(TARGET_NAME openvr) setup_hifi_plugin(OpenGL Script Qml Widgets Multimedia) link_hifi_libraries(shared gl networking controllers ui plugins display-plugins ui-plugins input-plugins script-engine - audio-client render-utils graphics gpu gpu-gl render model-networking fbx ktx image procedural) + audio-client render-utils graphics gpu render model-networking fbx ktx image procedural ${PLATFORM_GL_BACKEND}) include_hifi_library_headers(octree) diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 96ad19f46b..4403eaeff7 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -175,12 +175,12 @@ public: while (!_queue.empty()) { auto& front = _queue.front(); - auto result = glClientWaitSync(front.fence, 0, 0); + auto result = glClientWaitSync((GLsync)front.fence, 0, 0); if (GL_TIMEOUT_EXPIRED == result || GL_WAIT_FAILED == result) { break; } else if (GL_CONDITION_SATISFIED == result || GL_ALREADY_SIGNALED == result) { - glDeleteSync(front.fence); + glDeleteSync((GLsync)front.fence); } else { assert(false); } @@ -510,14 +510,8 @@ void OpenVrDisplayPlugin::internalDeactivate() { } void OpenVrDisplayPlugin::customizeContext() { - // Display plugins in DLLs must initialize glew locally - static std::once_flag once; - std::call_once(once, [] { - glewExperimental = true; - GLenum err = glewInit(); - glGetError(); // clear the potential error from glewExperimental - }); - + // Display plugins in DLLs must initialize GL locally + gl::initModuleGl(); Parent::customizeContext(); if (_threadedSubmit) { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 6aea6ef575..f681852cd5 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -27,9 +27,9 @@ struct CompositeInfo { using Array = std::array; gpu::TexturePointer texture; - GLuint textureID { 0 }; + uint32_t textureID { 0 }; glm::mat4 pose; - GLsync fence{ 0 }; + void* fence{ 0 }; }; class OpenVrDisplayPlugin : public HmdDisplayPlugin { diff --git a/scripts/+android/defaultScripts.js b/scripts/+android/defaultScripts.js new file mode 100644 index 0000000000..a115e498da --- /dev/null +++ b/scripts/+android/defaultScripts.js @@ -0,0 +1,110 @@ +"use strict"; +/* jslint vars: true, plusplus: true */ + +// +// defaultScripts.js +// examples +// +// 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 +// + +var DEFAULT_SCRIPTS_COMBINED = [ + "system/progress.js"/*, + "system/away.js", + "system/controllers/controllerDisplayManager.js", + "system/controllers/handControllerGrabAndroid.js", + "system/controllers/handControllerPointerAndroid.js", + "system/controllers/squeezeHands.js", + "system/controllers/grab.js", + "system/controllers/teleport.js", + "system/controllers/toggleAdvancedMovementForHandControllers.js", + "system/dialTone.js", + "system/firstPersonHMD.js", + "system/bubble.js", + "system/android.js", + "developer/debugging/debugAndroidMouse.js"*/ +]; + +var DEFAULT_SCRIPTS_SEPARATE = [ ]; + +// add a menu item for debugging +var MENU_CATEGORY = "Developer"; +var MENU_ITEM = "Debug defaultScripts.js"; + +var SETTINGS_KEY = '_debugDefaultScriptsIsChecked'; +var previousSetting = Settings.getValue(SETTINGS_KEY); + +if (previousSetting === '' || previousSetting === false || previousSetting === 'false') { + previousSetting = false; +} + +if (previousSetting === true || previousSetting === 'true') { + previousSetting = true; +} + +if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_ITEM)) { + Menu.addMenuItem({ + menuName: MENU_CATEGORY, + menuItemName: MENU_ITEM, + isCheckable: true, + isChecked: previousSetting, + grouping: "Advanced" + }); +} + +function loadSeparateDefaults() { + for (var i in DEFAULT_SCRIPTS_SEPARATE) { + Script.load(DEFAULT_SCRIPTS_SEPARATE[i]); + } +} + +function runDefaultsTogether() { + for (var i in DEFAULT_SCRIPTS_COMBINED) { + Script.include(DEFAULT_SCRIPTS_COMBINED[i]); + } + loadSeparateDefaults(); +} + +function runDefaultsSeparately() { + for (var i in DEFAULT_SCRIPTS_COMBINED) { + Script.load(DEFAULT_SCRIPTS_COMBINED[i]); + } + loadSeparateDefaults(); +} + +// start all scripts +if (Menu.isOptionChecked(MENU_ITEM)) { + // we're debugging individual default scripts + // so we load each into its own ScriptEngine instance + runDefaultsSeparately(); +} else { + // include all default scripts into this ScriptEngine + runDefaultsTogether(); +} + +function menuItemEvent(menuItem) { + if (menuItem === MENU_ITEM) { + var isChecked = Menu.isOptionChecked(MENU_ITEM); + if (isChecked === true) { + Settings.setValue(SETTINGS_KEY, true); + } else if (isChecked === false) { + Settings.setValue(SETTINGS_KEY, false); + } + Menu.triggerOption("Reload All Scripts"); + } +} + +function removeMenuItem() { + if (!Menu.isOptionChecked(MENU_ITEM)) { + Menu.removeMenuItem(MENU_CATEGORY, MENU_ITEM); + } +} + +Script.scriptEnding.connect(function() { + removeMenuItem(); +}); + +Menu.menuItemEvent.connect(menuItemEvent); diff --git a/scripts/developer/tests/dynamics/dynamicsTests.js b/scripts/developer/tests/dynamics/dynamicsTests.js index c0b001eab3..e9262c9308 100644 --- a/scripts/developer/tests/dynamics/dynamicsTests.js +++ b/scripts/developer/tests/dynamics/dynamicsTests.js @@ -22,8 +22,7 @@ var button = tablet.addButton({ icon: Script.resolvePath("dynamicsTests.svg"), - text: "Dynamics", - sortOrder: 15 + text: "Dynamics" }); diff --git a/scripts/developer/utilities/render/debugHighlight.js b/scripts/developer/utilities/render/debugHighlight.js index e70565cec2..c2173f6e2a 100644 --- a/scripts/developer/utilities/render/debugHighlight.js +++ b/scripts/developer/utilities/render/debugHighlight.js @@ -32,8 +32,7 @@ var button = tablet.addButton({ text: TABLET_BUTTON_NAME, icon: ICON_URL, - activeIcon: ACTIVE_ICON_URL, - sortOrder: 1 + activeIcon: ACTIVE_ICON_URL }); var hasEventBridge = false; diff --git a/scripts/developer/utilities/render/lod.js b/scripts/developer/utilities/render/lod.js index dc0b99edc2..307e509d39 100644 --- a/scripts/developer/utilities/render/lod.js +++ b/scripts/developer/utilities/render/lod.js @@ -30,8 +30,7 @@ var button = tablet.addButton({ text: TABLET_BUTTON_NAME, icon: ICON_URL, - activeIcon: ACTIVE_ICON_URL, - sortOrder: 1 + activeIcon: ACTIVE_ICON_URL }); var hasEventBridge = false; diff --git a/scripts/developer/utilities/render/luci.js b/scripts/developer/utilities/render/luci.js index 1e2ac1261f..6482c884ff 100644 --- a/scripts/developer/utilities/render/luci.js +++ b/scripts/developer/utilities/render/luci.js @@ -31,8 +31,7 @@ var button = tablet.addButton({ text: TABLET_BUTTON_NAME, icon: ICON_URL, - activeIcon: ACTIVE_ICON_URL, - sortOrder: 1 + activeIcon: ACTIVE_ICON_URL }); var hasEventBridge = false; diff --git a/scripts/developer/utilities/tools/currentAPI.js b/scripts/developer/utilities/tools/currentAPI.js index 175b84b8d9..6cab6a5710 100644 --- a/scripts/developer/utilities/tools/currentAPI.js +++ b/scripts/developer/utilities/tools/currentAPI.js @@ -28,8 +28,8 @@ var window = new OverlayWindow({ title: 'API Debugger', source: qml, - width: 1200, - height: 500 + width: 500, + height: 700 }); window.closed.connect(function () { diff --git a/scripts/system/bubble.js b/scripts/system/bubble.js index 4ea684ff06..994bde49eb 100644 --- a/scripts/system/bubble.js +++ b/scripts/system/bubble.js @@ -16,10 +16,10 @@ var button; // Used for animating and disappearing the bubble var bubbleOverlayTimestamp; + // Used for rate limiting the bubble sound + var lastBubbleSoundTimestamp = 0; // Used for flashing the HUD button upon activation var bubbleButtonFlashState = false; - // Used for flashing the HUD button upon activation - var bubbleButtonTimestamp; // Affects bubble height var BUBBLE_HEIGHT_SCALE = 0.15; // The bubble model itself @@ -36,9 +36,11 @@ var bubbleActivateSound = SoundCache.getSound(Script.resolvePath("assets/sounds/bubble.wav")); // Is the update() function connected? var updateConnected = false; + var bubbleFlashTimer = false; var BUBBLE_VISIBLE_DURATION_MS = 3000; var BUBBLE_RAISE_ANIMATION_DURATION_MS = 750; + var BUBBLE_SOUND_RATE_LIMIT_MS = 15000; // Hides the bubble model overlay and resets the button flash state function hideOverlays() { @@ -50,11 +52,15 @@ // Make the bubble overlay visible, set its position, and play the sound function createOverlays() { - Audio.playSound(bubbleActivateSound, { - position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z }, - localOnly: true, - volume: 0.2 - }); + var nowTimestamp = Date.now(); + if (nowTimestamp - lastBubbleSoundTimestamp >= BUBBLE_SOUND_RATE_LIMIT_MS) { + Audio.playSound(bubbleActivateSound, { + position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z }, + localOnly: true, + volume: 0.2 + }); + lastBubbleSoundTimestamp = nowTimestamp; + } hideOverlays(); if (updateConnected === true) { updateConnected = false; @@ -80,10 +86,17 @@ }, visible: true }); - bubbleOverlayTimestamp = Date.now(); - bubbleButtonTimestamp = bubbleOverlayTimestamp; + bubbleOverlayTimestamp = nowTimestamp; Script.update.connect(update); updateConnected = true; + + // Flash button + if (!bubbleFlashTimer) { + bubbleFlashTimer = Script.setInterval(function () { + writeButtonProperties(bubbleButtonFlashState); + bubbleButtonFlashState = !bubbleButtonFlashState; + }, 500); + } } // Called from the C++ scripting interface to show the bubble overlay @@ -103,12 +116,6 @@ var delay = (timestamp - bubbleOverlayTimestamp); var overlayAlpha = 1.0 - (delay / BUBBLE_VISIBLE_DURATION_MS); if (overlayAlpha > 0) { - // Flash button - if ((timestamp - bubbleButtonTimestamp) >= BUBBLE_VISIBLE_DURATION_MS) { - writeButtonProperties(bubbleButtonFlashState); - bubbleButtonTimestamp = timestamp; - bubbleButtonFlashState = !bubbleButtonFlashState; - } if (delay < BUBBLE_RAISE_ANIMATION_DURATION_MS) { Overlays.editOverlay(bubbleOverlay, { @@ -157,8 +164,11 @@ Script.update.disconnect(update); updateConnected = false; } - var bubbleActive = Users.getIgnoreRadiusEnabled(); - writeButtonProperties(bubbleActive); + if (bubbleFlashTimer) { + Script.clearTimeout(bubbleFlashTimer); + bubbleFlashTimer = false; + } + writeButtonProperties(Users.getIgnoreRadiusEnabled()); } } @@ -166,6 +176,10 @@ // NOTE: the c++ calls this with just the first param -- we added a second // just for not logging the initial state of the bubble when we startup. function onBubbleToggled(enabled, doNotLog) { + if (bubbleFlashTimer) { + Script.clearTimeout(bubbleFlashTimer); + bubbleFlashTimer = false; + } writeButtonProperties(enabled); if (doNotLog !== true) { UserActivityLogger.bubbleToggled(enabled); @@ -200,6 +214,10 @@ // Cleanup the tablet button and overlays when script is stopped Script.scriptEnding.connect(function () { button.clicked.disconnect(Users.toggleIgnoreRadius); + if (bubbleFlashTimer) { + Script.clearTimeout(bubbleFlashTimer); + bubbleFlashTimer = false; + } if (tablet) { tablet.removeButton(button); } diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 0cb414e23c..b0a2e114a3 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -945,8 +945,7 @@ tabletButton = tablet.addButton({ icon: tabletButtonIcon, activeIcon: tabletButtonActiveIcon, - text: tabletButtonName, - sortOrder: 0 + text: tabletButtonName }); Messages.subscribe(channelName); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 0826325a57..ad864622ed 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -562,9 +562,11 @@ break; case 'disableHmdPreview': isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview"); + DesktopPreviewProvider.setPreviewDisabledReason("SECURE_SCREEN"); Menu.setIsOptionChecked("Disable Preview", true); break; case 'maybeEnableHmdPreview': + DesktopPreviewProvider.setPreviewDisabledReason("USER"); Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled); break; case 'passphraseReset': @@ -591,10 +593,16 @@ getConnectionData(false); break; case 'enable_ChooseRecipientNearbyMode': - Script.update.connect(updateOverlays); + if (!isUpdateOverlaysWired) { + Script.update.connect(updateOverlays); + isUpdateOverlaysWired = true; + } break; case 'disable_ChooseRecipientNearbyMode': - Script.update.disconnect(updateOverlays); + if (isUpdateOverlaysWired) { + Script.update.disconnect(updateOverlays); + isUpdateOverlaysWired = false; + } removeOverlays(); break; default: @@ -635,7 +643,11 @@ // -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string // value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML. function onTabletScreenChanged(type, url) { - onWalletScreen = (type === "QML" && url === WALLET_QML_SOURCE); + var onWalletScreenNow = (type === "QML" && url === WALLET_QML_SOURCE); + if (!onWalletScreenNow && onWalletScreen) { + DesktopPreviewProvider.setPreviewDisabledReason("USER"); + } + onWalletScreen = onWalletScreenNow; wireEventBridge(onWalletScreen); // Change button to active when window is first openend, false otherwise. if (button) { @@ -675,14 +687,18 @@ } } var isWired = false; + var isUpdateOverlaysWired = false; function off() { if (isWired) { // It is not ok to disconnect these twice, hence guard. Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Script.update.disconnect(updateOverlays); Controller.mousePressEvent.disconnect(handleMouseEvent); Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); isWired = false; } + if (isUpdateOverlaysWired) { + Script.update.disconnect(updateOverlays); + isUpdateOverlaysWired = false; + } triggerMapping.disable(); // It's ok if we disable twice. triggerPressMapping.disable(); // see above removeOverlays(); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 88195d7024..32bf7316a9 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -370,20 +370,23 @@ Script.include("/~/system/libraries/Xform.js"); }; this.isReady = function (controllerData) { - if (this.notPointingAtEntity(controllerData)) { - return makeRunningValues(false, [], []); - } + if (HMD.active) { + if (this.notPointingAtEntity(controllerData)) { + return makeRunningValues(false, [], []); + } - this.distanceHolding = false; - this.distanceRotating = false; + this.distanceHolding = false; + this.distanceRotating = false; - if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { - this.prepareDistanceRotatingData(controllerData); - return makeRunningValues(true, [], []); - } else { - this.destroyContextOverlay(); - return makeRunningValues(false, [], []); + if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + this.prepareDistanceRotatingData(controllerData); + return makeRunningValues(true, [], []); + } else { + this.destroyContextOverlay(); + return makeRunningValues(false, [], []); + } } + return makeRunningValues(false, [], []); }; this.run = function (controllerData) { diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js index a7a186b07a..afc9256875 100644 --- a/scripts/system/controllers/controllerModules/hudOverlayPointer.js +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -89,7 +89,7 @@ this.isReady = function (controllerData) { var otherModuleRunning = this.getOtherModule().running; - if (!otherModuleRunning) { + if (!otherModuleRunning && HMD.active) { if (this.processLaser(controllerData)) { this.running = true; return ControllerDispatcherUtils.makeRunningValues(true, [], []); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 87cd3e0faf..92ccdf6565 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -448,7 +448,7 @@ var toolBar = (function () { }); addButton("importEntitiesButton", "assets-01.svg", function() { - Window.openFileChanged.connect(onFileOpenChanged); + Window.browseChanged.connect(onFileOpenChanged); Window.browseAsync("Select Model to Import", "", "*.json"); }); @@ -1497,7 +1497,7 @@ function onFileOpenChanged(filename) { // disconnect the event, otherwise the requests will stack up try { // Not all calls to onFileOpenChanged() connect an event. - Window.openFileChanged.disconnect(onFileOpenChanged); + Window.browseChanged.disconnect(onFileOpenChanged); } catch (e) { // Ignore. } @@ -1549,7 +1549,7 @@ function handeMenuEvent(menuItem) { } } else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") { if (menuItem === "Import Entities") { - Window.openFileChanged.connect(onFileOpenChanged); + Window.browseChanged.connect(onFileOpenChanged); Window.browseAsync("Select Model to Import", "", "*.json"); } else { Window.promptTextChanged.connect(onPromptTextChanged); diff --git a/scripts/system/emote.js b/scripts/system/emote.js index f1f739c126..139870fd63 100644 --- a/scripts/system/emote.js +++ b/scripts/system/emote.js @@ -43,7 +43,7 @@ var activeTimer = false; // used to cancel active timer if a user plays an amima var activeEmote = false; // to keep track of the currently playing emote button = tablet.addButton({ - //icon: "icons/tablet-icons/emote.svg", // TODO - we need graphics for this + icon: "icons/tablet-icons/EmoteAppIcon.svg", text: EMOTE_LABEL, sortOrder: EMOTE_APP_SORT_ORDER }); diff --git a/scripts/system/generalSettings.js b/scripts/system/generalSettings.js index 082528ffc5..d3848da7d0 100644 --- a/scripts/system/generalSettings.js +++ b/scripts/system/generalSettings.js @@ -37,8 +37,7 @@ tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); button = tablet.addButton({ icon: "icons/tablet-icons/goto-i.svg", - text: buttonName, - sortOrder: 8 + text: buttonName }); } diff --git a/scripts/system/goto.js b/scripts/system/goto.js index d364bf579e..5cc5bad844 100644 --- a/scripts/system/goto.js +++ b/scripts/system/goto.js @@ -41,8 +41,7 @@ if (Settings.getValue("HUDUIEnabled")) { button = tablet.addButton({ icon: "icons/tablet-icons/goto-i.svg", activeIcon: "icons/tablet-icons/goto-a.svg", - text: buttonName, - sortOrder: 8 + text: buttonName }); } diff --git a/scripts/system/html/EmoteApp.html b/scripts/system/html/EmoteApp.html index 30ef3e17a1..0a423b9b6c 100644 --- a/scripts/system/html/EmoteApp.html +++ b/scripts/system/html/EmoteApp.html @@ -44,11 +44,11 @@ input[type=button] { font-family: 'Raleway'; font-weight: bold; - font-size: 13px; + font-size: 20px; text-transform: uppercase; vertical-align: top; - height: 28px; - min-width: 120px; + height: 105px; + min-width: 190px; padding: 0px 18px; margin-right: 6px; border-radius: 5px; @@ -98,14 +98,14 @@

Click an emotion to Emote:

-

-

-

-

-

-

-

-

+

+

+

+

+

+

+

+

diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 11c083dacc..728760c1e7 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -539,8 +539,14 @@ return startingUp; } - function onDomainConnectionRefused(reason) { - createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED); + function onDomainConnectionRefused(reason, reasonCode) { + // the "login error" reason means that the DS couldn't decrypt the username signature + // since this eventually resolves itself for good actors we don't need to show a notification for it + var LOGIN_ERROR_REASON_CODE = 2; + + if (reasonCode != LOGIN_ERROR_REASON_CODE) { + createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED); + } } function onEditError(msg) { diff --git a/scripts/system/run.js b/scripts/system/run.js index 054cca6d9c..c34271b18d 100644 --- a/scripts/system/run.js +++ b/scripts/system/run.js @@ -10,8 +10,7 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ icon: Script.resolvePath("assets/images/run.svg"), - text: "RUN", - sortOrder: 15 + text: "RUN" }); function onClicked() { diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 2a0e827932..43dae9625a 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -136,7 +136,7 @@ 'include_actions=' + actions, 'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'), 'require_online=true', - 'protocol=' + encodeURIComponent(location.protocolVersion()), + 'protocol=' + encodeURIComponent(Window.protocolSignature()), 'per_page=' + count ]; var url = Account.metaverseServerURL + '/api/v1/user_stories?' + options.join('&'); diff --git a/scripts/system/tablet-users.js b/scripts/system/tablet-users.js index 6f37cd55eb..6181173818 100644 --- a/scripts/system/tablet-users.js +++ b/scripts/system/tablet-users.js @@ -37,8 +37,7 @@ var button = tablet.addButton({ icon: "icons/tablet-icons/users-i.svg", activeIcon: "icons/tablet-icons/users-a.svg", - text: "USERS", - sortOrder: 11 + text: "USERS" }); var onUsersScreen = false; diff --git a/tests/controllers/CMakeLists.txt b/tests/controllers/CMakeLists.txt index c3d25cfe2e..b5e866ccce 100644 --- a/tests/controllers/CMakeLists.txt +++ b/tests/controllers/CMakeLists.txt @@ -1,4 +1,5 @@ - +# FIXME Disabling test on OSX because of unexpected link error +if (NOT APPLE) set(TARGET_NAME controllers-test) # This is not a testcase -- just set it up as a regular hifi project @@ -23,3 +24,4 @@ if (WIN32) endif() package_libraries_for_deployment() +endif() \ No newline at end of file diff --git a/tests/gl/CMakeLists.txt b/tests/gl/CMakeLists.txt new file mode 100644 index 0000000000..40bb64be1c --- /dev/null +++ b/tests/gl/CMakeLists.txt @@ -0,0 +1,9 @@ +set(TARGET_NAME gl-test) +# This is not a testcase -- just set it up as a regular hifi project +setup_hifi_project(Quick Gui OpenGL) +setup_memory_debugger() +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") +link_hifi_libraries(shared gl) +package_libraries_for_deployment() +target_nsight() +target_opengl() diff --git a/tests/gl/src/main.cpp b/tests/gl/src/main.cpp new file mode 100644 index 0000000000..7435c8c2bf --- /dev/null +++ b/tests/gl/src/main.cpp @@ -0,0 +1,41 @@ +// +// Created by Bradley Austin Davis on 2018/01/11 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include + + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); + + GLWindow* window = new GLWindow(); + window->create(); + window->show(); + bool contextCreated = false; + QTimer* timer = new QTimer(); + QObject::connect(timer, &QTimer::timeout, [&] { + if (!contextCreated) { + window->createContext(); + contextCreated = true; + } + if (!window->makeCurrent()) { + throw std::runtime_error("Failed"); + } + glClearColor(1.0f, 0.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + window->swapBuffers(); + window->doneCurrent(); + }); + + timer->setInterval(15); + timer->setSingleShot(false); + timer->start(); + app.exec(); +} diff --git a/tests/gpu-test/CMakeLists.txt b/tests/gpu-test/CMakeLists.txt index 9772320812..2092279159 100644 --- a/tests/gpu-test/CMakeLists.txt +++ b/tests/gpu-test/CMakeLists.txt @@ -4,7 +4,13 @@ AUTOSCRIBE_SHADER_LIB(gpu graphics render-utils) setup_hifi_project(Quick Gui OpenGL Script Widgets) setup_memory_debugger() set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") -link_hifi_libraries(networking gl gpu gpu-gl procedural shared fbx graphics model-networking animation script-engine render render-utils octree image ktx) +link_hifi_libraries( + shared networking gl + ktx gpu procedural octree image + graphics model-networking fbx animation + script-engine render render-utils + ${PLATFORM_GL_BACKEND} +) if (WIN32) add_dependency_external_projects(wasapi) diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 9739533fd2..5314f7a45b 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -12,7 +12,15 @@ 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 networking graphics fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural midi ui) +link_hifi_libraries( + shared networking animation + ktx image octree gl gpu + render render-utils + graphics fbx model-networking + entities entities-renderer audio avatars script-engine + physics procedural midi ui + ${PLATFORM_GL_BACKEND} +) if (WIN32) target_link_libraries(${TARGET_NAME} Winmm.lib) diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index db0fbea9ef..2bf0c74067 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -226,9 +226,7 @@ public: _context.makeCurrent(); window->setSurfaceType(QSurface::OpenGLSurface); _context.makeCurrent(_context.qglContext(), window); -#ifdef Q_OS_WIN - wglSwapIntervalEXT(0); -#endif + gl::setSwapInterval(0); // GPU library init gpu::Context::init(); _gpuContext = std::make_shared(); diff --git a/tests/render-texture-load/CMakeLists.txt b/tests/render-texture-load/CMakeLists.txt index 302ddd0f97..dbfb7d33db 100644 --- a/tests/render-texture-load/CMakeLists.txt +++ b/tests/render-texture-load/CMakeLists.txt @@ -12,7 +12,15 @@ 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 graphics model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics ktx image) +link_hifi_libraries( + shared networking octree + gl gpu render ktx image animation + graphics fbx model-networking + render-utils + entities entities-renderer audio avatars + script-engine physics + ${PLATFORM_GL_BACKEND} +) package_libraries_for_deployment() diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp index 67b80d9ba8..066b39fc40 100644 --- a/tests/render-texture-load/src/main.cpp +++ b/tests/render-texture-load/src/main.cpp @@ -11,6 +11,8 @@ #include #include +#include + #include #include #include @@ -147,9 +149,7 @@ public: } _context.makeCurrent(); - glewExperimental = true; - glewInit(); - glGetError(); + gl::initModuleGl(); //wglSwapIntervalEXT(0); _frameTimes.resize(FRAME_TIME_BUFFER_SIZE, 0); @@ -441,6 +441,7 @@ protected: } void reportMemory() { +#if !defined(USE_GLES) static GLint lastMemory = 0; GLint availableMem; glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &availableMem); @@ -449,6 +450,7 @@ protected: qDebug() << "Delta " << availableMem - lastMemory; } lastMemory = availableMem; +#endif } void derezTexture() { diff --git a/tests/render-utils/CMakeLists.txt b/tests/render-utils/CMakeLists.txt index 4e67aef3be..e3b1523a96 100644 --- a/tests/render-utils/CMakeLists.txt +++ b/tests/render-utils/CMakeLists.txt @@ -8,7 +8,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") setup_memory_debugger() # link in the shared libraries -link_hifi_libraries(render-utils gl gpu gpu-gl shared) +link_hifi_libraries(render-utils gl gpu shared ${PLATFORM_GL_BACKEND}) target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT}) if (WIN32) diff --git a/tests/shaders/CMakeLists.txt b/tests/shaders/CMakeLists.txt index a6796b8601..76d2aa100c 100644 --- a/tests/shaders/CMakeLists.txt +++ b/tests/shaders/CMakeLists.txt @@ -8,9 +8,12 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") setup_memory_debugger() # link in the shared libraries -link_hifi_libraries(shared octree gl gpu gpu-gl graphics render fbx networking entities - script-engine physics - render-utils entities-renderer) +link_hifi_libraries( + shared octree gl gpu graphics render fbx networking entities + script-engine physics + render-utils entities-renderer + ${PLATFORM_GL_BACKEND} +) include_directories("${PROJECT_BINARY_DIR}/../../libraries/gpu/") include_directories("${PROJECT_BINARY_DIR}/../../libraries/render-utils/") diff --git a/tests/shared/src/BitVectorHelperTests.cpp b/tests/shared/src/BitVectorHelperTests.cpp new file mode 100644 index 0000000000..070e90eec7 --- /dev/null +++ b/tests/shared/src/BitVectorHelperTests.cpp @@ -0,0 +1,80 @@ +// +// BitVectorHelperTests.cpp +// tests/shared/src +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "BitVectorHelperTests.h" + +#include + +#include "../QTestExtensions.h" +#include +#include +#include +#include + +QTEST_MAIN(BitVectorHelperTests) + +const int BITS_IN_BYTE = 8; + +void BitVectorHelperTests::sizeTest() { + std::vector sizes = {0, 6, 7, 8, 30, 31, 32, 33, 87, 88, 89, 90, 90, 91, 92, 93}; + for (auto& size : sizes) { + const int oldWay = (int)ceil((float)size / (float)BITS_IN_BYTE); + const int newWay = (int)calcBitVectorSize(size); + QCOMPARE(oldWay, newWay); + } +} + +static void readWriteHelper(const std::vector& src) { + + int numBits = (int)src.size(); + int numBytes = calcBitVectorSize(numBits); + uint8_t* bytes = new uint8_t[numBytes]; + memset(bytes, numBytes, sizeof(uint8_t)); + int numBytesWritten = writeBitVector(bytes, numBits, [&](int i) { + return src[i]; + }); + QCOMPARE(numBytesWritten, numBytes); + + std::vector dst; + int numBytesRead = readBitVector(bytes, numBits, [&](int i, bool value) { + dst.push_back(value); + }); + QCOMPARE(numBytesRead, numBytes); + + QCOMPARE(numBits, (int)src.size()); + QCOMPARE(numBits, (int)dst.size()); + for (int i = 0; i < numBits; i++) { + bool a = src[i]; + bool b = dst[i]; + QCOMPARE(a, b); + } +} + +void BitVectorHelperTests::readWriteTest() { + std::vector sizes = {0, 6, 7, 8, 30, 31, 32, 33, 87, 88, 89, 90, 90, 91, 92, 93}; + + for (auto& size : sizes) { + std::vector allTrue(size, true); + std::vector allFalse(size, false); + std::vector evenSet; + evenSet.reserve(size); + std::vector oddSet; + oddSet.reserve(size); + for (int i = 0; i < size; i++) { + bool isOdd = (i & 0x1) > 0; + evenSet.push_back(!isOdd); + oddSet.push_back(isOdd); + } + readWriteHelper(allTrue); + readWriteHelper(allFalse); + readWriteHelper(evenSet); + readWriteHelper(oddSet); + } +} diff --git a/tests/shared/src/BitVectorHelperTests.h b/tests/shared/src/BitVectorHelperTests.h new file mode 100644 index 0000000000..1f52ba1ac9 --- /dev/null +++ b/tests/shared/src/BitVectorHelperTests.h @@ -0,0 +1,23 @@ +// +// BitVectorHelperTests.h +// tests/shared/src +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_BitVectorHelperTests_h +#define hifi_BitVectorHelperTests_h + +#include + +class BitVectorHelperTests : public QObject { + Q_OBJECT +private slots: + void sizeTest(); + void readWriteTest(); +}; + +#endif // hifi_BitVectorHelperTests_h diff --git a/tools/auto-tester/src/Test.cpp b/tools/auto-tester/src/Test.cpp index 6c637ab404..816eac7fbd 100644 --- a/tools/auto-tester/src/Test.cpp +++ b/tools/auto-tester/src/Test.cpp @@ -306,8 +306,8 @@ void Test::evaluateTestsRecursively(bool interactiveMode, QProgressBar* progress zipAndDeleteTestResultsFolder(); } -void Test::importTest(QTextStream& textStream, const QString& testPathname, int testNumber) { - textStream << "var test" << testNumber << " = Script.require(\"" << "file:///" << testPathname + "\");" << endl; +void Test::importTest(QTextStream& textStream, const QString& testPathname) { + textStream << "Script.include(\"" << "file:///" << testPathname + "?raw=true\");" << endl; } // Creates a single script in a user-selected folder. @@ -330,12 +330,14 @@ void Test::createRecursiveScript() { } QTextStream textStream(&allTestsFilename); - textStream << "// This is an automatically generated file, created by auto-tester" << endl; + textStream << "// This is an automatically generated file, created by auto-tester" << endl << endl; + + textStream << "var autoTester = Script.require(\"https://github.com/highfidelity/hifi_tests/blob/master/tests/utils/autoTester.js?raw=true\");" << endl; + textStream << "autoTester.enableRecursive();" << endl << endl; // The main will call each test after the previous test is completed // This is implemented with an interval timer that periodically tests if a // running test has increment a testNumber variable that it received as an input. - int testNumber = 1; QVector testPathnames; // First test if top-level folder has a test.js file @@ -343,8 +345,7 @@ void Test::createRecursiveScript() { QFileInfo fileInfo(testPathname); if (fileInfo.exists()) { // Current folder contains a test - importTest(textStream, testPathname, testNumber); - ++testNumber; + importTest(textStream, testPathname); testPathnames << testPathname; } @@ -363,8 +364,7 @@ void Test::createRecursiveScript() { QFileInfo fileInfo(testPathname); if (fileInfo.exists()) { // Current folder contains a test - importTest(textStream, testPathname, testNumber); - ++testNumber; + importTest(textStream, testPathname); testPathnames << testPathname; } @@ -377,79 +377,9 @@ void Test::createRecursiveScript() { } textStream << endl; - - // Define flags for each test - for (int i = 1; i <= testPathnames.length(); ++i) { - textStream << "var test" << i << "HasNotStarted = true;" << endl; - } - - // Leave a blank line in the main - textStream << endl; - - const int TEST_PERIOD = 1000; // in milliseconds - const QString tab = " "; - - textStream << "// Check every second if the current test is complete and the next test can be run" << endl; - textStream << "var testTimer = Script.setInterval(" << endl; - textStream << tab << "function() {" << endl; - - const QString testFunction = "test"; - for (int i = 1; i <= testPathnames.length(); ++i) { - // First test starts immediately, all other tests wait for the previous test to complete. - // The script produced will look as follows: - // if (test1HasNotStarted) { - // test1HasNotStarted = false; - // test1.test("auto"); - // print("******started test 1******"); - // } - // | - // | - // if (test5.complete && test6HasNotStarted) { - // test6HasNotStarted = false; - // test7.test(); - // print("******started test 6******"); - // } - // | - // | - // if (test12.complete) { - // print("******stopping******"); - // Script.stop(); - // } - // - if (i == 1) { - textStream << tab << tab << "if (test1HasNotStarted) {" << endl; - } else { - textStream << tab << tab << "if (test" << i - 1 << ".complete && test" << i << "HasNotStarted) {" << endl; - } - textStream << tab << tab << tab << "test" << i << "HasNotStarted = false;" << endl; - textStream << tab << tab << tab << "test" << i << "." << testFunction << "(\"auto\");" << endl; - textStream << tab << tab << tab << "print(\"******started test " << i << "******\");" << endl; - - textStream << tab << tab << "}" << endl << endl; - - } - - // Add extra step to stop the script - textStream << tab << tab << "if (test" << testPathnames.length() << ".complete) {" << endl; - textStream << tab << tab << tab << "print(\"******stopping******\");" << endl; - textStream << tab << tab << tab << "Script.stop();" << endl; - textStream << tab << tab << "}" << endl << endl; - - textStream << tab << "}," << endl; - textStream << endl; - textStream << tab << TEST_PERIOD << endl; - textStream << ");" << endl << endl; - - textStream << "// Stop the timer and clear the module cache" << endl; - textStream << "Script.scriptEnding.connect(" << endl; - textStream << tab << "function() {" << endl; - textStream << tab << tab << "Script.clearInterval(testTimer);" << endl; - textStream << tab << tab << "Script.require.cache = {};" << endl; - textStream << tab << "}" << endl; - textStream << ");" << endl; + textStream << "autoTester.runRecursive();" << endl; allTestsFilename.close(); - messageBox.information(0, "Success", "Script has been created"); } diff --git a/tools/auto-tester/src/Test.h b/tools/auto-tester/src/Test.h index aa1346fa2a..3177df4d47 100644 --- a/tools/auto-tester/src/Test.h +++ b/tools/auto-tester/src/Test.h @@ -36,7 +36,7 @@ public: bool isInSnapshotFilenameFormat(QString filename); bool isInExpectedImageFilenameFormat(QString filename); - void importTest(QTextStream& textStream, const QString& testPathname, int testNumber); + void importTest(QTextStream& textStream, const QString& testPathname); void appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage); diff --git a/unpublishedScripts/marketplace/camera-move/app-camera-move.js b/unpublishedScripts/marketplace/camera-move/app-camera-move.js index f58dd3d3bd..f9361c6091 100644 --- a/unpublishedScripts/marketplace/camera-move/app-camera-move.js +++ b/unpublishedScripts/marketplace/camera-move/app-camera-move.js @@ -133,7 +133,7 @@ var DEBUG_INFO = { Reticle: { supportsScale: 'scale' in Reticle, }, - protocolVersion: location.protocolVersion, + protocolVersion: Window.protocolSignature(), }; var globalState = { diff --git a/unpublishedScripts/marketplace/camera-move/modules/custom-settings-app/CustomSettingsApp.js b/unpublishedScripts/marketplace/camera-move/modules/custom-settings-app/CustomSettingsApp.js index c76adea940..95dba5c558 100644 --- a/unpublishedScripts/marketplace/camera-move/modules/custom-settings-app/CustomSettingsApp.js +++ b/unpublishedScripts/marketplace/camera-move/modules/custom-settings-app/CustomSettingsApp.js @@ -52,7 +52,7 @@ function CustomSettingsApp(options) { this.extraParams = Object.assign(options.extraParams || {}, { customSettingsVersion: CustomSettingsApp.version+'', - protocolVersion: location.protocolVersion && location.protocolVersion() + protocolVersion: Window.protocolSignature && Window.protocolSignature() }); var params = { diff --git a/unpublishedScripts/marketplace/clap/clapApp.js b/unpublishedScripts/marketplace/clap/clapApp.js index b2d8ce55db..7118b3623c 100644 --- a/unpublishedScripts/marketplace/clap/clapApp.js +++ b/unpublishedScripts/marketplace/clap/clapApp.js @@ -31,8 +31,7 @@ var activeButton = tablet.addButton({ icon: whiteIcon, activeIcon: blackIcon, text: APP_NAME, - isActive: isActive, - sortOrder: 11 + isActive: isActive }); if (isActive) { diff --git a/unpublishedScripts/marketplace/skyboxChanger/skyboxchanger.js b/unpublishedScripts/marketplace/skyboxChanger/skyboxchanger.js index 7bc65722cd..0bd23552e4 100644 --- a/unpublishedScripts/marketplace/skyboxChanger/skyboxchanger.js +++ b/unpublishedScripts/marketplace/skyboxChanger/skyboxchanger.js @@ -32,8 +32,7 @@ var button = tablet.addButton({ icon: ICONS.icon, activeIcon: ICONS.activeIcon, - text: TABLET_BUTTON_NAME, - sortOrder: 1 + text: TABLET_BUTTON_NAME }); var hasEventBridge = false;