diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt
index 4411b7b1bb..a0af43b30a 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
- model 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 gpu-gles image fbx render-utils physics entities octree)
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..815a15e752 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -1,10 +1,9 @@
apply plugin: 'com.android.application'
-
android {
compileSdkVersion 26
defaultConfig {
- applicationId "com.highfidelity.iface"
+ applicationId "io.highfidelity.hifiinterface"
minSdkVersion 24
targetSdkVersion 26
versionCode 1
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/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..640ef78e06 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',
@@ -257,7 +261,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 +297,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)
}
@@ -438,8 +442,8 @@ 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()
}
@@ -450,10 +454,74 @@ 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'
}
+task generateAssetsFileList() {
+ doLast {
+ 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();
+ }
+}
+
+task copyInterfaceAssets() {
+ doLast {
+ def resourcesDir = new File("${appDir}/../../interface/resources")
+ def scriptsDir = new File("${appDir}/../../scripts")
+ def assetsDir = new File(appDir, 'src/main/assets')
+ copy {
+ from resourcesDir
+ into new File(assetsDir, "resources")
+ }
+ copy {
+ from scriptsDir
+ into new File(assetsDir, "scripts")
+ }
+ }
+}
+
// 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/cmake/macros/TargetOpenGL.cmake b/cmake/macros/TargetOpenGL.cmake
index 6ad92259bb..b9de4e6253 100644
--- a/cmake/macros/TargetOpenGL.cmake
+++ b/cmake/macros/TargetOpenGL.cmake
@@ -11,7 +11,10 @@ macro(TARGET_OPENGL)
find_library(OpenGL OpenGL)
target_link_libraries(${TARGET_NAME} ${OpenGL})
elseif(ANDROID)
- target_link_libraries(${TARGET_NAME} GLESv3 EGL)
+ find_library(EGL EGL)
+ find_library(OpenGLES3 GLESv3)
+ list(APPEND IGNORE_COPY_LIBS ${OpenGLES3} ${OpenGLES2} ${EGL})
+ target_link_libraries(${TARGET_NAME} ${OpenGLES3} ${EGL})
else()
find_package(OpenGL REQUIRED)
if (${OPENGL_INCLUDE_DIR})
diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt
index e6b3392aad..8d58d4a6e8 100644
--- a/interface/CMakeLists.txt
+++ b/interface/CMakeLists.txt
@@ -12,7 +12,7 @@ function(JOIN VALUES GLUE OUTPUT)
endfunction()
-if (NOT DEV_BUILD)
+if (ANDROID OR NOT DEV_BUILD)
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)
endif()
@@ -74,7 +74,7 @@ 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}")
-if (NOT DEV_BUILD)
+if (ANDROID OR NOT DEV_BUILD)
list(APPEND INTERFACE_SRCS ${INTERFACE_QML_QRC})
endif()
@@ -93,21 +93,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 ()
@@ -262,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)
@@ -278,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)
@@ -363,20 +354,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/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/src/Application.cpp b/interface/src/Application.cpp
index f3c41565f8..8ed6012c71 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -209,7 +209,10 @@
#include "commerce/QmlCommerce.h"
#include "webbrowser/WebBrowserSuggestionsEngine.h"
-
+#if defined(Q_OS_ANDROID)
+#include
+#include
+#endif
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
#if defined(Q_OS_WIN)
@@ -231,6 +234,30 @@ extern "C" {
}
#endif
+#if defined(Q_OS_ANDROID)
+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();
+}
+
+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();
+}
+
+
+}
+#endif
enum ApplicationEvent {
// Execute a lambda function
Lambda = QEvent::User + 1,
@@ -530,6 +557,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"));
}
@@ -636,11 +683,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();
@@ -2172,6 +2219,7 @@ void Application::initializeGL() {
_isGLInitialized = true;
}
+ gl::initModuleGl();
_glWidget->makeCurrent();
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->setObjectName("ChromiumShareContext");
@@ -2194,9 +2242,15 @@ void Application::initializeGL() {
// Set up the render engine
render::CullFunctor cullFunctor = LODManager::shouldRender;
static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
+#ifdef Q_OS_ANDROID
+ bool isDeferred = false;
+#else
bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
+#endif
_renderEngine->addJob("UpdateScene");
- _renderEngine->addJob("SecondaryCameraJob", cullFunctor);
+#ifndef Q_OS_ANDROID
+ _renderEngine->addJob("SecondaryCameraJob", cullFunctor, isDeferred);
+#endif
_renderEngine->addJob("RenderMainView", cullFunctor, isDeferred);
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);
@@ -2305,8 +2359,11 @@ 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
+#ifdef Q_OS_ANDROID
+ offscreenUi->createDesktop(PathUtils::qmlBasePath() + "hifi/Desktop.qml");
+#else
offscreenUi->createDesktop(QString("hifi/Desktop.qml"));
-
+#endif
// 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);
@@ -3883,12 +3940,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__);
@@ -4253,9 +4316,9 @@ void Application::init() {
// Make sure Login state is up to date
DependencyManager::get()->toggleLoginDialog();
-
+#ifndef Q_OS_ANDROID
DependencyManager::get()->init();
-
+#endif
DependencyManager::get()->init();
_timerStart.start();
@@ -5721,7 +5784,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());
@@ -5773,6 +5838,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get().data());
scriptEngine->registerGlobalObject("Users", DependencyManager::get().data());
+ scriptEngine->registerGlobalObject("App", this);
+ scriptEngine->registerFunction("App", "isAndroid", Application::isAndroid, 0);
+
scriptEngine->registerGlobalObject("LimitlessSpeechRecognition", DependencyManager::get().data());
scriptEngine->registerGlobalObject("GooglePoly", DependencyManager::get().data());
@@ -7356,6 +7424,18 @@ void Application::updateThreadPoolCount() const {
QThreadPool::globalInstance()->setMaxThreadCount(threadPoolSize);
}
+QScriptValue Application::isAndroid(QScriptContext* context, QScriptEngine* engine) {
+ return QScriptValue(engine, isAndroid());
+}
+
+bool Application::isAndroid() {
+#ifdef Q_OS_ANDROID
+ return true;
+#else
+ return false;
+#endif
+}
+
void Application::updateSystemTabletMode() {
qApp->setProperty(hifi::properties::HMD, isHMDMode());
if (isHMDMode()) {
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 479ce919a3..54b4757f69 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -284,6 +284,9 @@ public:
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
void saveNextPhysicsStats(QString filename);
+ static Q_INVOKABLE QScriptValue isAndroid(QScriptContext* context, QScriptEngine* engine);
+ static bool isAndroid();
+
signals:
void svoImportRequested(const QString& url);
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/main.cpp b/interface/src/main.cpp
index 5c07bebc23..b8b3f4ab97 100644
--- a/interface/src/main.cpp
+++ b/interface/src/main.cpp
@@ -105,6 +105,18 @@ int main(int argc, const char* argv[]) {
if (allowMultipleInstances) {
instanceMightBeRunning = false;
}
+
+#if defined(Q_OS_ANDROID)
+ std::vector assetDirs = {
+ "/resources",
+ "/scripts",
+ };
+ QDir dirInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
+ for (std::vector::iterator it = assetDirs.begin() ; it != assetDirs.end(); ++it) {
+ QString dir = *it;
+ PathUtils::copyDirDeep("assets:" + dir, QUrl::fromLocalFile(dirInfo.canonicalPath() + dir).toLocalFile());
+ }
+#endif
// this needs to be done here in main, as the mechanism for setting the
// scripts directory appears not to work. See the bug report
// https://highfidelity.fogbugz.com/f/cases/5759/Issues-changing-scripts-directory-in-ScriptsEngine
diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp
index 72b6f401e9..334c45e49e 100644
--- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp
+++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp
@@ -39,7 +39,8 @@ AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() {
void AssetMappingsScriptingInterface::setMapping(QString path, QString hash, QJSValue callback) {
auto assetClient = DependencyManager::get();
auto request = assetClient->createSetMappingRequest(path, hash);
-
+#if !defined(Q_OS_ANDROID)
+// TODO: just to make android compile
connect(request, &SetMappingRequest::finished, this, [this, callback](SetMappingRequest* request) mutable {
if (callback.isCallable()) {
QJSValueList args { request->getErrorString(), request->getPath() };
@@ -48,6 +49,7 @@ void AssetMappingsScriptingInterface::setMapping(QString path, QString hash, QJS
request->deleteLater();
});
+#endif
request->start();
}
@@ -55,7 +57,8 @@ void AssetMappingsScriptingInterface::setMapping(QString path, QString hash, QJS
void AssetMappingsScriptingInterface::getMapping(QString path, QJSValue callback) {
auto assetClient = DependencyManager::get();
auto request = assetClient->createGetMappingRequest(path);
-
+#if !defined(Q_OS_ANDROID)
+// TODO: just to make android compile
connect(request, &GetMappingRequest::finished, this, [this, callback](GetMappingRequest* request) mutable {
auto hash = request->getHash();
@@ -66,6 +69,7 @@ void AssetMappingsScriptingInterface::getMapping(QString path, QJSValue callback
request->deleteLater();
});
+#endif
request->start();
}
@@ -142,7 +146,8 @@ void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping,
void AssetMappingsScriptingInterface::deleteMappings(QStringList paths, QJSValue callback) {
auto assetClient = DependencyManager::get();
auto request = assetClient->createDeleteMappingsRequest(paths);
-
+#if !defined(Q_OS_ANDROID)
+// TODO: just to make android compile
connect(request, &DeleteMappingsRequest::finished, this, [this, callback](DeleteMappingsRequest* request) mutable {
if (callback.isCallable()) {
QJSValueList args { request->getErrorString() };
@@ -151,6 +156,7 @@ void AssetMappingsScriptingInterface::deleteMappings(QStringList paths, QJSValue
request->deleteLater();
});
+#endif
request->start();
}
@@ -158,7 +164,8 @@ void AssetMappingsScriptingInterface::deleteMappings(QStringList paths, QJSValue
void AssetMappingsScriptingInterface::getAllMappings(QJSValue callback) {
auto assetClient = DependencyManager::get();
auto request = assetClient->createGetAllMappingsRequest();
-
+#if !defined(Q_OS_ANDROID)
+// TODO: just to make android compile
connect(request, &GetAllMappingsRequest::finished, this, [this, callback](GetAllMappingsRequest* request) mutable {
auto mappings = request->getMappings();
auto map = callback.engine()->newObject();
@@ -174,6 +181,7 @@ void AssetMappingsScriptingInterface::getAllMappings(QJSValue callback) {
request->deleteLater();
});
+#endif
request->start();
}
@@ -181,7 +189,8 @@ void AssetMappingsScriptingInterface::getAllMappings(QJSValue callback) {
void AssetMappingsScriptingInterface::renameMapping(QString oldPath, QString newPath, QJSValue callback) {
auto assetClient = DependencyManager::get();
auto request = assetClient->createRenameMappingRequest(oldPath, newPath);
-
+#if !defined(Q_OS_ANDROID)
+// TODO: just to make android compile
connect(request, &RenameMappingRequest::finished, this, [this, callback](RenameMappingRequest* request) mutable {
if (callback.isCallable()) {
QJSValueList args{ request->getErrorString() };
@@ -190,14 +199,15 @@ void AssetMappingsScriptingInterface::renameMapping(QString oldPath, QString new
request->deleteLater();
});
-
+#endif
request->start();
}
void AssetMappingsScriptingInterface::setBakingEnabled(QStringList paths, bool enabled, QJSValue callback) {
auto assetClient = DependencyManager::get();
auto request = assetClient->createSetBakingEnabledRequest(paths, enabled);
-
+#if !defined(Q_OS_ANDROID)
+// TODO: just to make android compile
connect(request, &SetBakingEnabledRequest::finished, this, [this, callback](SetBakingEnabledRequest* request) mutable {
if (callback.isCallable()) {
QJSValueList args{ request->getErrorString() };
@@ -206,7 +216,7 @@ void AssetMappingsScriptingInterface::setBakingEnabled(QStringList paths, bool e
request->deleteLater();
});
-
+#endif
request->start();
}
diff --git a/interface/src/scripting/LimitlessConnection.h b/interface/src/scripting/LimitlessConnection.h
index ee049aff8e..6acf651c4e 100644
--- a/interface/src/scripting/LimitlessConnection.h
+++ b/interface/src/scripting/LimitlessConnection.h
@@ -15,7 +15,9 @@
#include
#include
#include
-
+#if defined(Q_OS_ANDROID)
+#include
+#endif
class LimitlessConnection : public QObject {
Q_OBJECT
public:
diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp
index 5b7c1896fe..7447e9f88b 100644
--- a/interface/src/ui/ResourceImageItem.cpp
+++ b/interface/src/ui/ResourceImageItem.cpp
@@ -16,7 +16,9 @@
#include
#include
-
+#ifdef Q_OS_ANDROID
+#include
+#endif
ResourceImageItem::ResourceImageItem() : QQuickFramebufferObject() {
auto textureCache = DependencyManager::get();
connect(textureCache.data(), SIGNAL(spectatorCameraFramebufferReset()), this, SLOT(update()));
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/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
index e646ba27f5..0facf5f2d0 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
@@ -891,10 +891,14 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne
GLuint fbo[2] {0, 0};
// need mipmaps for blitting texture
- glGenerateTextureMipmap(sourceTexture);
+#if !defined(Q_OS_ANDROID)
+ glGenerateTextureMipmap(sourceTexture);
+#endif
// create 2 fbos (one for initial texture, second for scaled one)
- glCreateFramebuffers(2, fbo);
+#if !defined(Q_OS_ANDROID)
+ glCreateFramebuffers(2, fbo);
+#endif
// setup source fbo
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
@@ -924,7 +928,9 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne
} else {
newY = (target->height() - newHeight) / 2;
}
+#if !defined(Q_OS_ANDROID)
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);
+#endif
// don't delete the textures!
glDeleteFramebuffers(2, fbo);
diff --git a/libraries/gl/src/gl/Config.cpp b/libraries/gl/src/gl/Config.cpp
index 1f29fe21b1..d862149a44 100644
--- a/libraries/gl/src/gl/Config.cpp
+++ b/libraries/gl/src/gl/Config.cpp
@@ -14,9 +14,9 @@
#include
#if defined(Q_OS_ANDROID)
-PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT = NULL;
-PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT = NULL;
-PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT = NULL;
+PFNGLQUERYCOUNTEREXTPROC __glQueryCounterEXT = NULL;
+PFNGLGETQUERYOBJECTUI64VEXTPROC __glGetQueryObjectui64vEXT = NULL;
+PFNGLFRAMEBUFFERTEXTUREEXTPROC __glFramebufferTextureEXT = NULL;
#endif
void gl::initModuleGl() {
diff --git a/libraries/gl/src/gl/Config.h b/libraries/gl/src/gl/Config.h
index cdf86675c6..6dc95cb1b1 100644
--- a/libraries/gl/src/gl/Config.h
+++ b/libraries/gl/src/gl/Config.h
@@ -57,9 +57,15 @@ 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;
+
+
+ #define glQueryCounterEXT __glQueryCounterEXT
+ #define glGetQueryObjectui64vEXT __glGetQueryObjectui64vEXT
+ #define glFramebufferTextureEXT __glFramebufferTextureEXT
+
+ extern PFNGLQUERYCOUNTEREXTPROC __glQueryCounterEXT;
+ extern PFNGLGETQUERYOBJECTUI64VEXTPROC __glGetQueryObjectui64vEXT;
+ extern PFNGLFRAMEBUFFERTEXTUREEXTPROC __glFramebufferTextureEXT;
}
#else // !defined(HIFI_GLES)
diff --git a/libraries/gpu-gles/CMakeLists.txt b/libraries/gpu-gles/CMakeLists.txt
index 55ec53b184..e9875411e3 100644
--- a/libraries/gpu-gles/CMakeLists.txt
+++ b/libraries/gpu-gles/CMakeLists.txt
@@ -1,5 +1,5 @@
set(TARGET_NAME gpu-gles)
-setup_hifi_library(OpenGL)
+setup_hifi_library(Concurrent)
link_hifi_libraries(shared gl gpu)
GroupSources("src")
diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp
index cb00d00b3e..7d00552663 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();
}
@@ -187,7 +183,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
}
}
- { // Sync all the buffers
+ { // Sync all the transform states
ANDROID_PROFILE(render, "syncCPUTransform", 0xffaaaaff, 1)
_transform._cameras.clear();
_transform._cameraOffsets.clear();
@@ -203,6 +199,14 @@ 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: {
@@ -257,7 +261,8 @@ void GLBackend::renderPassDraw(const Batch& batch) {
updateInput();
updateTransform(batch);
updatePipeline();
- {ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xff0000ff, 1)
+ {
+ ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xff0000ff, 1)
CommandCall call = _commandCalls[(*command)];
(this->*(call))(batch, *offset);
}
@@ -292,6 +297,11 @@ void GLBackend::render(const Batch& batch) {
renderPassTransfer(batch);
}
+#ifdef GPU_STEREO_DRAWCALL_INSTANCED
+ if (_stereo.isStereo()) {
+ glEnable(GL_CLIP_DISTANCE0);
+ }
+#endif
{
//PROFILE_RANGE(render_gpu_gl, _stereo._enable ? "Render Stereo" : "Render");
ANDROID_PROFILE(render, "RenderPassDraw", 0xff00ddff, 1)
@@ -333,6 +343,22 @@ 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();
@@ -391,7 +417,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();
@@ -709,9 +735,9 @@ void GLBackend::recycle() const {
}
}
-#ifndef THREADED_TEXTURE_TRANSFER
- gl::GLTexture::_textureTransferHelper->process();
-#endif
+ GLVariableAllocationSupport::manageMemory();
+ GLVariableAllocationSupport::_frameTexturesCreated = 0;
+
}
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..f26bdc4818 100644
--- a/libraries/gpu-gles/src/gpu/gl/GLBackend.h
+++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.h
@@ -53,248 +53,259 @@
#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);
+ void setupStereoSide(int side);
- typedef std::bitset BuffersState;
+ virtual void initInput() final;
+ virtual void killInput() final;
+ virtual void syncInputStateCache() final;
+ virtual void resetInputStage();
+ virtual void updateInput() = 0;
- BuffersState _invalidBuffers{ 0 };
- BuffersState _attribBindingBuffers{ 0 };
+ struct InputStageState {
+ bool _invalidFormat { true };
+ Stream::FormatPointer _format;
+ std::string _formatKey;
- Buffers _buffers;
- Offsets _bufferOffsets;
- Offsets _bufferStrides;
- std::vector _bufferVBOs;
+ typedef std::bitset ActivationCache;
+ ActivationCache _attributeActivation { 0 };
- glm::vec4 _colorAttribute{ 0.0f };
+ typedef std::bitset BuffersState;
- BufferPointer _indexBuffer;
- Offset _indexBufferOffset { 0 };
- Type _indexBufferType { UINT32 };
+ BuffersState _invalidBuffers{ 0 };
+ BuffersState _attribBindingBuffers{ 0 };
- BufferPointer _indirectBuffer;
- Offset _indirectBufferOffset{ 0 };
- Offset _indirectBufferStride{ 0 };
+ Buffers _buffers;
+ Offsets _bufferOffsets;
+ Offsets _bufferStrides;
+ std::vector _bufferVBOs;
- GLuint _defaultVAO { 0 };
+ glm::vec4 _colorAttribute{ 0.0f };
- 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;
+ BufferPointer _indexBuffer;
+ Offset _indexBufferOffset { 0 };
+ Type _indexBufferType { UINT32 };
+
+ BufferPointer _indirectBuffer;
+ Offset _indirectBufferOffset{ 0 };
+ Offset _indirectBufferStride{ 0 };
- 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();
+ GLuint _defaultVAO { 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;
- };
+ 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;
- struct TransformStageState {
+ 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 +315,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/GLBackendPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp
index c35966d440..73a597e89a 100644
--- a/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp
+++ b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp
@@ -197,6 +197,36 @@ void GLBackend::resetResourceStage() {
}
}
+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) {
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..9e3768cb86
--- /dev/null
+++ b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp
@@ -0,0 +1,530 @@
+//
+// 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< GLuint > 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_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},
+ */
+
+ //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},
+
+ 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},
+
+#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 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/GLBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp
index 4be7682a4f..13c5bd98af 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,7 +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);
+ GLTexture* object = syncGPUObject(resourceTexture);
qDebug() << "GLBackendTexture do_generateTextureMips syncGPUObject";
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..23926dc11d 100644
--- a/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp
+++ b/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp
@@ -38,7 +38,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 +103,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 +120,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 +152,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 +162,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..b04dd1da84 100644
--- a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp
+++ b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp
@@ -21,7 +21,7 @@ GLFramebuffer::~GLFramebuffer() {
}
}
-bool GLFramebuffer::checkStatus(GLenum target) const {
+bool GLFramebuffer::checkStatus() const {
bool result = false;
switch (_status) {
case GL_FRAMEBUFFER_COMPLETE:
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/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..0b3d84a09c 100644
--- a/libraries/gpu-gles/src/gpu/gl/GLShared.cpp
+++ b/libraries/gpu-gles/src/gpu/gl/GLShared.cpp
@@ -27,22 +27,22 @@ 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;
+ qCWarning(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error;
break;
}
return true;
@@ -53,7 +53,7 @@ bool checkGLErrorDebug(const char* name) {
#ifdef DEBUG
return checkGLError(name);
#else
- Q_UNUSED(name);
+ Q_UNUSED(name);
return false;
#endif
}
@@ -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;
@@ -354,504 +317,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..5b25e55c82 100644
--- a/libraries/gpu-gles/src/gpu/gl/GLShared.h
+++ b/libraries/gpu-gles/src/gpu/gl/GLShared.h
@@ -17,9 +17,9 @@
Q_DECLARE_LOGGING_CATEGORY(gpugllogging)
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl)
-namespace gpu { namespace gl {
+#define BUFFER_OFFSET(bytes) ((GLubyte*) nullptr + (bytes))
- static const GLint TRANSFORM_OBJECT_SLOT { 14 }; // SSBO binding slot
+namespace gpu { namespace gl {
// Create a fence and inject a GPU wait on the fence
void serverWait();
@@ -27,7 +27,6 @@ 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 +34,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,6 +108,7 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
GL_SHORT,
GL_UNSIGNED_SHORT,
GL_BYTE,
+ GL_UNSIGNED_BYTE,
GL_UNSIGNED_BYTE
};
@@ -156,6 +137,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..bff3998573 100644
--- a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp
+++ b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp
@@ -11,6 +11,9 @@
using namespace gpu;
using namespace gpu::gl;
+bool GLTexelFormat::isCompressed() const {
+ return false;
+}
GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
// qDebug() << "GLTexelFormat::evalGLTexelFormatInternal " << dstFormat.getDimension() << ", " << dstFormat.getSemantic() << ", " << dstFormat.getType();
@@ -18,6 +21,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
switch (dstFormat.getDimension()) {
case gpu::SCALAR: {
switch (dstFormat.getSemantic()) {
+ case gpu::RED:
case gpu::RGB:
case gpu::RGBA:
case gpu::SRGB:
@@ -74,7 +78,10 @@ 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()) {
@@ -114,6 +121,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
+ case gpu::XY:
result = GL_RG8;
break;
default:
@@ -178,12 +186,12 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
case gpu::NINT8:
result = GL_RGBA8_SNORM;
break;
- case gpu::COMPRESSED:
case gpu::NUINT2:
case gpu::NINT16:
case gpu::NUINT16:
case gpu::NUINT32:
case gpu::NINT32:
+ case gpu::COMPRESSED:
case gpu::NUM_TYPES: // quiet compiler
Q_UNREACHABLE();
}
@@ -201,7 +209,37 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
}
break;
}
+ // TODO: implement compression formats supported by android (ASTC, ETC2)
+ /*
+ case gpu::TILE4x4: {
+ switch (dstFormat.getSemantic()) {
+ case gpu::COMPRESSED_BC4_RED:
+ result = GL_COMPRESSED_RED_RGTC1;
+ break;
+ case gpu::COMPRESSED_BC1_SRGB:
+ result = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
+ break;
+ case gpu::COMPRESSED_BC1_SRGBA:
+ result = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
+ break;
+ case gpu::COMPRESSED_BC3_SRGBA:
+ result = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
+ break;
+ case gpu::COMPRESSED_BC5_XY:
+ result = GL_COMPRESSED_RG_RGTC2;
+ break;
+ case gpu::COMPRESSED_BC6_RGB:
+ result = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
+ break;
+ case gpu::COMPRESSED_BC7_SRGBA:
+ result = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
+ break;
+ default:
+ qCWarning(gpugllogging) << "Unknown combination of texel format";
+ }
+ break;
+ */
default:
qCDebug(gpugllogging) << "Unknown combination of texel format";
}
@@ -212,8 +250,6 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
}
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 };
@@ -237,6 +273,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
//CLIMAX_MERGE_END
case gpu::DEPTH:
+ texel.format = GL_DEPTH_COMPONENT;
texel.internalFormat = GL_DEPTH_COMPONENT32_OES;
break;
case gpu::DEPTH_STENCIL:
@@ -245,7 +282,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 +294,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;
@@ -288,7 +326,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
// break;
//CLIMAX_MERGE_END
default:
- qCDebug(gpugllogging) << "Unknown combination of texel format";
+ qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
@@ -356,13 +394,13 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
*/
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 +412,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:
@@ -465,6 +499,12 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
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;
@@ -508,7 +548,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 +561,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;
@@ -555,7 +596,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
// 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;
}
@@ -645,7 +686,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
// //texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA;
// break;
default:
- qCDebug(gpugllogging) << "Unknown combination of texel format";
+ qCWarning(gpugllogging) << "Unknown combination of texel format";
}
break;
}
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..780dbe17a1 100644
--- a/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp
+++ b/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp
@@ -8,27 +8,22 @@
#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,
@@ -73,6 +68,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 +102,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..19f8575802 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 };
+
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..37134e5467 100644
--- a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp
+++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp
@@ -12,41 +12,90 @@
#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;
+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;
+ }
+
+ GLESTexture* object = Backend::getGPUObject(texture);
+ if (!object) {
+ switch (texture.getUsageType()) {
+ case TextureUsageType::RENDERBUFFER:
+ object = new GLESAttachmentTexture(shared_from_this(), texture);
+ break;
+
+ case TextureUsageType::STRICT_RESOURCE:
+ qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str();
+ object = new GLESStrictResourceTexture(shared_from_this(), texture);
+ break;
+
+ case TextureUsageType::RESOURCE:
+ qCDebug(gpugllogging) << "variable / Strict texture " << texture.source().c_str();
+ object = new GLESResourceTexture(shared_from_this(), texture);
+ GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer);
+ 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 +104,62 @@ 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();
- }
- }
- //}
-}
+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) {
+ qDebug() << "[UNIMPLEMENTED] GL_TEXTURE_2D internalFormat: " << internalFormat;
+ /*switch (internalFormat) {
+ case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
+ case GL_COMPRESSED_RED_RGTC1:
+ case GL_COMPRESSED_RG_RGTC2:
+ case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
+ case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
+ glCompressedTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, internalFormat,
+ static_cast(sourceSize), sourcePointer);
+ break;
+ default:*/
+ glTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
+ //break;
+ //}
+ } else if (GL_TEXTURE_CUBE_MAP == _target) {
+ auto target = GLTexture::CUBE_FACE_LAYOUT[face];
+ qDebug() << "[UNIMPLEMENTED] GL_TEXTURE_CUBE_MAP internalFormat: " << internalFormat;
-void GLESTexture::updateSize() const {
- setSize(_virtualSize);
- if (!_id) {
- return;
+ /*switch (internalFormat) {
+ case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
+ case GL_COMPRESSED_RED_RGTC1:
+ case GL_COMPRESSED_RG_RGTC2:
+ case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
+ case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
+ glCompressedTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, internalFormat,
+ static_cast(sourceSize), sourcePointer);
+ break;
+ default:*/
+ glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
+ // break;
+ //}
+ } else {
+ // TODO: implement for android
+ assert(false);
+ amountCopied = 0;
}
-
- 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();
+ return amountCopied;
}
-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);
- }
- }
- }
-}
-
-void GLESBackend::GLESTexture::syncSampler() const {
+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);
@@ -161,16 +170,452 @@ void GLESBackend::GLESTexture::syncSampler() const {
glTexParameteri(_target, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]);
glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR_EXT, (const float*)&sampler.getBorderColor());
-
-
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