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/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/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/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/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/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/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/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/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/resources/styles/global.qss b/interface/resources/styles/global.qss index 2554f3b2c9..778e5759b3 100644 --- a/interface/resources/styles/global.qss +++ b/interface/resources/styles/global.qss @@ -41,14 +41,14 @@ QSpinBox, QDoubleSpinBox { QDoubleSpinBox::up-arrow, QSpinBox::up-arrow { - background-image: url(styles/up.svg); + background-image: url(:/styles/up.svg); background-repeat: no-repeat; background-position: center center; } QDoubleSpinBox::down-arrow, QSpinBox::down-arrow { - background-image: url(styles/down.svg); + background-image: url(:/styles/down.svg); background-repeat: no-repeat; background-position: center center; } @@ -88,7 +88,7 @@ QSlider { QSlider::groove:horizontal { border: none; - background-image: url(styles/slider-bg.svg); + background-image: url(:/styles/slider-bg.svg); background-repeat: no-repeat; background-position: center center; } @@ -96,7 +96,7 @@ QSlider::groove:horizontal { QSlider::handle:horizontal { width: 18px; height: 18px; - background-image: url(styles/slider-handle.svg); + background-image: url(:/styles/slider-handle.svg); background-repeat: no-repeat; background-position: center center; } @@ -107,7 +107,7 @@ QPushButton#closeButton { border-width: 1px; border-radius: 0; background-color: #fff; - background-image: url(styles/close.svg); + background-image: url(:/styles/close.svg); background-repeat: no-repeat; background-position: center center; } diff --git a/interface/resources/styles/import_dialog.qss b/interface/resources/styles/import_dialog.qss index 8fe04ae1b7..3c2dbdcce9 100644 --- a/interface/resources/styles/import_dialog.qss +++ b/interface/resources/styles/import_dialog.qss @@ -63,17 +63,17 @@ QPushButton#cancelButton { } #backButton { - background-image: url(icons/backButton.svg); + background-image: url(:/icons/backButton.svg); border-radius: 0px; } #forwardButton { - background-image: url(icons/forwardButton.svg); + background-image: url(:/icons/forwardButton.svg); border-radius: 0px; } #toParentButton { - background-image: url(icons/toParentButton.svg); + background-image: url(:/icons/toParentButton.svg); border-radius: 0px; } diff --git a/interface/resources/styles/log_dialog.qss b/interface/resources/styles/log_dialog.qss index 33473d2903..e0ec17549d 100644 --- a/interface/resources/styles/log_dialog.qss +++ b/interface/resources/styles/log_dialog.qss @@ -22,7 +22,7 @@ QLineEdit { } QPushButton#searchButton { - background: url(styles/search.svg); + background: url(:/styles/search.svg); background-repeat: none; background-position: left center; background-origin: content; @@ -55,7 +55,7 @@ QPushButton#searchPrevButton { QPushButton#revealLogButton { font-family: Helvetica, Arial, sans-serif; - background: url(styles/txt-file.svg); + background: url(:/styles/txt-file.svg); background-repeat: none; background-position: left center; background-origin: content; @@ -86,11 +86,11 @@ QCheckBox { } QCheckBox::indicator:unchecked { - image: url(styles/unchecked.svg); + image: url(:/styles/unchecked.svg); } QCheckBox::indicator:checked { - image: url(styles/checked.svg); + image: url(:/styles/checked.svg); } QComboBox { @@ -110,6 +110,6 @@ QComboBox::drop-down { } QComboBox::down-arrow { - image: url(styles/filter.png); + image: url(:/styles/filter.png); border-width: 0px; } \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ffe2f781db..9379c85fdd 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 @@ -211,8 +215,6 @@ #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 @@ -227,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, @@ -241,7 +249,6 @@ enum ApplicationEvent { Idle, }; - class RenderEventHandler : public QObject { using Parent = QObject; Q_OBJECT @@ -305,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; @@ -334,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; @@ -537,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")); } @@ -600,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 @@ -643,11 +698,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { 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(); @@ -736,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 }; @@ -783,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); @@ -1003,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){ @@ -1089,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"; @@ -1382,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 @@ -1737,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; @@ -1776,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); @@ -1789,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; @@ -2188,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(); @@ -2209,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()); @@ -2320,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); @@ -2660,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::resourcesUrl() + INFO_HELP_PATH + "?" + queryString.toString()); + DependencyManager::get()->openTablet(); //InfoView::show(INFO_HELP_PATH, false, queryString.toString()); } @@ -3916,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__); @@ -4286,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(); @@ -5767,7 +5838,9 @@ 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()); 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 e93b897013..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); 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/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/BaseLogDialog.cpp b/interface/src/ui/BaseLogDialog.cpp index 6830de6e35..969f9895de 100644 --- a/interface/src/ui/BaseLogDialog.cpp +++ b/interface/src/ui/BaseLogDialog.cpp @@ -39,7 +39,6 @@ BaseLogDialog::BaseLogDialog(QWidget* parent) : QDialog(parent, Qt::Window) { QFile styleSheet(PathUtils::resourcesPath() + "styles/log_dialog.qss"); if (styleSheet.open(QIODevice::ReadOnly)) { - QDir::setCurrent(PathUtils::resourcesPath()); setStyleSheet(styleSheet.readAll()); } 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/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index d690880f99..d4138941ae 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -168,7 +168,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); _contextOverlay->setIgnoreRayIntersection(false); _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL(PathUtils::resourcesPath() + "images/inspect-icon.png"); + _contextOverlay->setURL(PathUtils::resourcesUrl() + "images/inspect-icon.png"); _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } 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/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 9924cc137e..c2aadb8723 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -114,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; } @@ -308,7 +308,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent 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()); 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/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/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/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/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/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 be8330f198..ddcbf8229c 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -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/Engine.cpp b/libraries/render/src/render/Engine.cpp index 5f67d40d17..463b45451b 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -45,8 +45,8 @@ void Engine::load() { auto config = getConfiguration(); const QString configFile= "config/render.json"; - QUrl path(PathUtils::resourcesPath() + configFile); - QFile file(path.toString()); + QString path(PathUtils::resourcesPath() + configFile); + QFile file(path); if (!file.exists()) { qWarning() << "Engine configuration file" << path << "does not exist"; } else if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { 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 index 71826036eb..9f5af0c380 100644 --- a/libraries/shared/src/BitVectorHelpers.h +++ b/libraries/shared/src/BitVectorHelpers.h @@ -12,14 +12,14 @@ #ifndef hifi_BitVectorHelpers_h #define hifi_BitVectorHelpers_h -size_t calcBitVectorSize(int numBits) { +int calcBitVectorSize(int numBits) { return ((numBits - 1) >> 3) + 1; } // func should be of type bool func(int index) template -size_t writeBitVector(uint8_t* destinationBuffer, int numBits, const F& func) { - size_t totalBytes = ((numBits - 1) >> 3) + 1; +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; @@ -34,13 +34,19 @@ size_t writeBitVector(uint8_t* destinationBuffer, int numBits, const F& func) { 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 -size_t readBitVector(const uint8_t* sourceBuffer, int numBits, const F& func) { - size_t totalBytes = ((numBits - 1) >> 3) + 1; +int readBitVector(const uint8_t* sourceBuffer, int numBits, const F& func) { + int totalBytes = calcBitVectorSize(numBits); const uint8_t* cursor = sourceBuffer; uint8_t bit = 0; diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index b33b330fc0..41db7281ac 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -11,16 +11,24 @@ #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 +36,21 @@ // 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/"; -#endif - return staticResourcePath; +static bool USE_SOURCE_TREE_RESOURCES() { + static bool result = false; + static std::once_flag once; + std::call_once(once, [&] { + const QString USE_SOURCE_TREE_RESOURCES_FLAG("HIFI_USE_SOURCE_TREE_RESOURCES"); + result = QProcessEnvironment::systemEnvironment().contains(USE_SOURCE_TREE_RESOURCES_FLAG); + }); + return result; } +#endif +#endif #ifdef DEV_BUILD const QString& PathUtils::projectRootPath() { @@ -49,16 +64,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 +141,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/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/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 0bc9676a2b..0191baca5e 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -198,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/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/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);