Merge remote-tracking branch 'gcalero/android_render_pr' into android_nov
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.highfidelity.gvrinterface">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.highfidelity.hifiinterface">
|
||||
|
||||
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="26" />
|
||||
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
|
@ -7,11 +8,13 @@
|
|||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
|
||||
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
|
||||
|
||||
<application
|
||||
android:name="org.qtproject.qt5.android.bindings.QtApplication"
|
||||
android:label="@string/app_name"
|
||||
android:hardwareAccelerated="true"
|
||||
android:allowBackup="true"
|
||||
android:screenOrientation="unspecified"
|
||||
|
@ -19,18 +22,34 @@
|
|||
android:icon="@mipmap/ic_launcher"
|
||||
android:launchMode="singleTop"
|
||||
android:roundIcon="@mipmap/ic_launcher_round">
|
||||
|
||||
<activity android:name="io.highfidelity.hifiinterface.PermissionChecker">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="io.highfidelity.hifiinterface.WebViewActivity"
|
||||
android:theme="@android:style/Theme.Material.Light.NoActionBar"/>
|
||||
<!-- We don't want to show this on Daydream yet (we need to fix the turn-around problem on this screen)
|
||||
<activity android:name="io.highfidelity.hifiinterface.GvrLoaderActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="com.google.intent.category.DAYDREAM"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
-->
|
||||
<activity
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
|
||||
android:name=".InterfaceActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="landscape"
|
||||
android:resizeableActivity="false">
|
||||
android:launchMode="singleTop"
|
||||
android:enableVrMode="com.google.vr.vrcore/com.google.vr.vrcore.common.VrCoreListenerService"
|
||||
>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="com.google.intent.category.DAYDREAM"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="native-lib"/>
|
||||
|
@ -43,4 +62,8 @@
|
|||
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
<uses-feature android:name="android.software.vr.mode" android:required="true"/>
|
||||
<uses-feature android:name="android.hardware.vr.high_performance" android:required="true"/>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
#include <jni.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtQml/QQmlEngine>
|
||||
#include <QtQml/QQmlFileSelector>
|
||||
#include <QtQuick/QQuickView>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtWebView/QtWebView>
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <gl/GLWindow.h>
|
||||
|
||||
#include <ui/OffscreenQmlSurface.h>
|
||||
|
||||
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();
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>simple.qml</file>
|
||||
<file>+android/simple.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -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 }
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
34
android/app/src/main/res/layout/activity_web_view.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<Toolbar
|
||||
android:id="@+id/toolbar_actionbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/actionBarSize"
|
||||
android:layout_alignParentTop="true"
|
||||
style="@android:style/Widget.Material.ActionBar"
|
||||
android:titleTextAppearance="@style/ActionBarTitleStyle"
|
||||
android:subtitleTextAppearance="@style/ActionBarSubtitleStyle"
|
||||
android:navigationIcon="@drawable/ic_close_black_24dp"
|
||||
android:contentInsetStart="0dp"
|
||||
android:contentInsetStartWithNavigation="0dp"
|
||||
android:title="">
|
||||
</Toolbar>
|
||||
|
||||
<WebView
|
||||
android:id="@+id/web_view"
|
||||
android:layout_below="@id/toolbar_actionbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<ProgressBar
|
||||
android:id="@+id/toolbarProgressBar"
|
||||
android:layout_below="@id/toolbar_actionbar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="3dp"
|
||||
android:indeterminate="false"
|
||||
android:padding="0dp" />
|
||||
</RelativeLayout>
|
10
android/app/src/main/res/menu/web_view_menu.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_share"
|
||||
android:title="@string/web_view_action_share"
|
||||
android:showAsAction="never"/>
|
||||
<item android:id="@+id/action_browser"
|
||||
android:title="@string/web_view_action_open_in_browser"
|
||||
android:showAsAction="never"/>
|
||||
</menu>
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 8.2 KiB |
12
android/app/src/main/res/values/dimens.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
|
||||
<!-- Hack to have a desired text size for both orientations (default portrait is too big) -->
|
||||
<!-- Default text size for action bar title.-->
|
||||
<dimen name="text_size_title_material_toolbar">14dp</dimen>
|
||||
<!-- Default text size for action bar subtitle.-->
|
||||
<dimen name="text_size_subtitle_material_toolbar">12dp</dimen>
|
||||
</resources>
|
|
@ -1,3 +1,8 @@
|
|||
<resources>
|
||||
<string name="app_name">TestApp</string>
|
||||
<string name="app_name" translatable="false">Interface</string>
|
||||
<string name="web_view_action_open_in_browser" translatable="false">Open in browser</string>
|
||||
<string name="web_view_action_share" translatable="false">Share link</string>
|
||||
<string name="web_view_action_share_subject" translatable="false">Shared a link</string>
|
||||
<string name="web_view_action_share_chooser" translatable="false">Share link</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -11,5 +11,15 @@
|
|||
<!--item name="android:background">@color/white_opaque</item-->
|
||||
</style>
|
||||
|
||||
|
||||
<!-- Overriding text size so it's not so big in portrait -->
|
||||
<style name="ActionBarTitleStyle"
|
||||
parent="@android:style/TextAppearance.Material.Widget.ActionBar.Title">
|
||||
<item name="android:textSize">@dimen/text_size_title_material_toolbar</item>
|
||||
</style>
|
||||
<style name="ActionBarSubtitleStyle"
|
||||
parent="@android:style/TextAppearance.Material.Widget.ActionBar.Subtitle">
|
||||
<item name="android:textSize">@dimen/text_size_subtitle_material_toolbar</item>
|
||||
</style>
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -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<String, List<String>> 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<String>()
|
||||
}
|
||||
if (!fileName.isEmpty()) {
|
||||
directoryContents[pathName].add(fileName);
|
||||
}
|
||||
}
|
||||
DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile));
|
||||
for (Map.Entry<String, List<String>> 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
|
||||
/*
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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 "<intent-filter>\
|
||||
\n <action android:name='android.intent.action.VIEW' />\
|
||||
\n <category android:name='android.intent.category.DEFAULT' />\
|
||||
\n <category android:name='android.intent.category.BROWSABLE' />\
|
||||
\n <data android:scheme='hifi' />\
|
||||
\n </intent-filter>"
|
||||
)
|
||||
|
||||
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})
|
||||
|
|
394
interface/resources/qml/+android/Stats.qml
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
63
interface/resources/qml/hifi/+android/Desktop.qml
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +209,10 @@
|
|||
#include "commerce/QmlCommerce.h"
|
||||
|
||||
#include "webbrowser/WebBrowserSuggestionsEngine.h"
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include <QtAndroidExtras/QAndroidJniObject>
|
||||
#include <android/log.h>
|
||||
#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<StatTracker>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
|
||||
DependencyManager::set<Preferences>();
|
||||
DependencyManager::set<recording::ClipCache>();
|
||||
DependencyManager::set<recording::Deck>();
|
||||
DependencyManager::set<recording::Recorder>();
|
||||
DependencyManager::set<AddressManager>();
|
||||
DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
|
||||
DependencyManager::set<recording::ClipCache>();
|
||||
DependencyManager::set<GeometryCache>();
|
||||
DependencyManager::set<ModelCache>();
|
||||
DependencyManager::set<ScriptCache>();
|
||||
|
@ -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<UpdateSceneTask>("UpdateScene");
|
||||
_renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraJob", cullFunctor);
|
||||
#ifndef Q_OS_ANDROID
|
||||
_renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraJob", cullFunctor, isDeferred);
|
||||
#endif
|
||||
_renderEngine->addJob<RenderViewTask>("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<StatTracker>()->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<DialogsManager>()->toggleLoginDialog();
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
DependencyManager::get<DeferredLightingEffect>()->init();
|
||||
|
||||
#endif
|
||||
DependencyManager::get<AvatarManager>()->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<UserActivityLoggerScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("App", this);
|
||||
scriptEngine->registerFunction("App", "isAndroid", Application::isAndroid, 0);
|
||||
|
||||
scriptEngine->registerGlobalObject("LimitlessSpeechRecognition", DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("GooglePoly", DependencyManager::get<GooglePolyScriptingInterface>().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()) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<SecondaryCameraJob>("SecondaryCamera");
|
||||
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
|
||||
assert(items.canCast<RenderFetchCullSortTask::Output>());
|
||||
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
|
||||
if (!isDeferred) {
|
||||
task.addJob<RenderForwardTask>("Forward", items);
|
||||
} else {
|
||||
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
|
||||
}
|
||||
task.addJob<EndSecondaryCameraFrame>("EndSecondaryCamera", cachedArg);
|
||||
}
|
|
@ -71,7 +71,7 @@ public:
|
|||
using JobModel = render::Task::Model<SecondaryCameraRenderTask, Config>;
|
||||
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
|
||||
|
|
|
@ -105,6 +105,18 @@ int main(int argc, const char* argv[]) {
|
|||
if (allowMultipleInstances) {
|
||||
instanceMightBeRunning = false;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
std::vector<QString> assetDirs = {
|
||||
"/resources",
|
||||
"/scripts",
|
||||
};
|
||||
QDir dirInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
for (std::vector<QString>::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
|
||||
|
|
|
@ -39,7 +39,8 @@ AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() {
|
|||
void AssetMappingsScriptingInterface::setMapping(QString path, QString hash, QJSValue callback) {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
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<AssetClient>();
|
||||
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<AssetClient>();
|
||||
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<AssetClient>();
|
||||
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<AssetClient>();
|
||||
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<AssetClient>();
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
#include <AudioClient.h>
|
||||
#include <QObject>
|
||||
#include <QFuture>
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include <QTcpSocket>
|
||||
#endif
|
||||
class LimitlessConnection : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
#include <QOpenGLContext>
|
||||
|
||||
#include <plugins/DisplayPlugin.h>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include <gl/Config.h>
|
||||
#endif
|
||||
ResourceImageItem::ResourceImageItem() : QQuickFramebufferObject() {
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
connect(textureCache.data(), SIGNAL(spectatorCameraFramebufferReset()), this, SLOT(update()));
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
#include <mutex>
|
||||
|
||||
#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() {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -22,17 +22,18 @@
|
|||
#include "nvToolsExt.h"
|
||||
#endif
|
||||
|
||||
#include <shared/GlobalAppProperties.h>
|
||||
#include <GPUIdent.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
|
||||
#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<GLBackend*>(qApp->property(GL_BACKEND_PROPERTY_NAME).value<void*>());
|
||||
INSTANCE = static_cast<GLBackend*>(qApp->property(hifi::properties::gl::BACKEND).value<void*>());
|
||||
}
|
||||
return *INSTANCE;
|
||||
}
|
||||
|
@ -65,9 +65,6 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin
|
|||
return GLShader::makeProgram(getBackend(), shader, slotBindings);
|
||||
}
|
||||
|
||||
std::array<QString, 45> 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<void()> 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) {
|
||||
|
|
|
@ -53,248 +53,259 @@
|
|||
#endif
|
||||
namespace gpu { namespace gl {
|
||||
|
||||
class GLBackend : public Backend, public std::enable_shared_from_this<GLBackend> {
|
||||
// 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<GLBackend> {
|
||||
// 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<void()> 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<void()> 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<std::string> profileRanges;
|
||||
mutable Mutex _trashMutex;
|
||||
mutable std::list<std::pair<GLuint, Size>> _buffersTrash;
|
||||
mutable std::list<std::pair<GLuint, Size>> _texturesTrash;
|
||||
mutable std::list<std::pair<GLuint, Texture::ExternalRecycler>> _externalTexturesTrash;
|
||||
mutable std::list<GLuint> _framebuffersTrash;
|
||||
mutable std::list<GLuint> _shadersTrash;
|
||||
mutable std::list<GLuint> _programsTrash;
|
||||
mutable std::list<GLuint> _queriesTrash;
|
||||
mutable std::list<std::function<void()>> _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<std::string> profileRanges;
|
||||
mutable Mutex _trashMutex;
|
||||
mutable std::list<std::pair<GLuint, Size>> _buffersTrash;
|
||||
mutable std::list<std::pair<GLuint, Size>> _texturesTrash;
|
||||
mutable std::list<std::pair<GLuint, Texture::ExternalRecycler>> _externalTexturesTrash;
|
||||
mutable std::list<GLuint> _framebuffersTrash;
|
||||
mutable std::list<GLuint> _shadersTrash;
|
||||
mutable std::list<GLuint> _programsTrash;
|
||||
mutable std::list<GLuint> _queriesTrash;
|
||||
mutable std::list<std::function<void()>> _lambdaQueue;
|
||||
|
||||
typedef std::bitset<MAX_NUM_ATTRIBUTES> ActivationCache;
|
||||
ActivationCache _attributeActivation { 0 };
|
||||
void renderPassTransfer(const Batch& batch);
|
||||
void renderPassDraw(const Batch& batch);
|
||||
void setupStereoSide(int side);
|
||||
|
||||
typedef std::bitset<MAX_NUM_INPUT_BUFFERS> 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<GLuint> _bufferVBOs;
|
||||
typedef std::bitset<MAX_NUM_ATTRIBUTES> ActivationCache;
|
||||
ActivationCache _attributeActivation { 0 };
|
||||
|
||||
glm::vec4 _colorAttribute{ 0.0f };
|
||||
typedef std::bitset<MAX_NUM_INPUT_BUFFERS> 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<GLuint> _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<CameraBufferElement>;
|
||||
using TransformCameras = std::vector<CameraBufferElement>;
|
||||
|
||||
TransformCamera _camera;
|
||||
TransformCameras _cameras;
|
||||
TransformCamera _camera;
|
||||
TransformCameras _cameras;
|
||||
|
||||
mutable std::map<std::string, GLvoid*> _drawCallInfoOffsets;
|
||||
mutable std::map<std::string, GLvoid*> _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<size_t, size_t>;
|
||||
using List = std::list<Pair>;
|
||||
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<size_t, size_t>;
|
||||
using List = std::list<Pair>;
|
||||
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<BufferPointer, MAX_NUM_UNIFORM_BUFFERS> _buffers;
|
||||
//Buffers _buffers { };
|
||||
} _uniform;
|
||||
virtual void transferTransformState(const Batch& batch) const = 0;
|
||||
|
||||
void releaseUniformBuffer(uint32_t slot);
|
||||
void resetUniformStage();
|
||||
struct UniformStageState {
|
||||
std::array<BufferPointer, MAX_NUM_UNIFORM_BUFFERS> _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<TexturePointer, MAX_NUM_RESOURCE_TEXTURES> _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<BufferPointer, MAX_NUM_RESOURCE_BUFFERS> _buffers;
|
||||
std::array<TexturePointer, MAX_NUM_RESOURCE_TEXTURES> _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<gpu::Buffer>(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<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
|
||||
BufferView _cameraCorrectionBufferIdentity { gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
|
||||
|
||||
PipelineStageState() {
|
||||
_cameraCorrectionBuffer.edit<CameraCorrection>() = 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>() = CameraCorrection();
|
||||
_cameraCorrectionBufferIdentity.edit<CameraCorrection>() = 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
|
||||
|
|
|
@ -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) {
|
||||
|
|
530
libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp
Normal file
|
@ -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 <gl/GLShaders.h>
|
||||
|
||||
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<GLenum, NUM_SHADER_DOMAINS> 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<std::string, NUM_SHADER_DOMAINS> 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<std::string, GLShader::NumVersions> 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<GLint> uniformBufferSlotMap(maxNumUniformBufferSlots, -1);
|
||||
|
||||
struct UniformBlockInfo {
|
||||
using Vector = std::vector<UniformBlockInfo>;
|
||||
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?";
|
||||
}
|
||||
}
|
||||
|
|
@ -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<GLsync>(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<GLTexture>(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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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<GLBackend>& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {}
|
||||
~GLFramebuffer();
|
||||
|
|
|
@ -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<GLenum, NUM_SHADER_DOMAINS> 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<std::string, NUM_SHADER_DOMAINS> 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<std::string, GLShader::NumVersions> 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<GLShader>(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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<GLint> uniformBufferSlotMap(maxNumUniformBufferSlots, -1);
|
||||
|
||||
struct UniformBlockInfo {
|
||||
using Vector = std::vector<UniformBlockInfo>;
|
||||
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<GLint> resourceBufferSlotMap(maxNumResourceBufferSlots, -1);
|
||||
|
||||
struct ResourceBlockInfo {
|
||||
using Vector = std::vector<ResourceBlockInfo>;
|
||||
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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -8,27 +8,22 @@
|
|||
|
||||
#include "GLTexture.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include "GLTextureTransfer.h"
|
||||
#include "GLBackend.h"
|
||||
|
||||
using namespace gpu;
|
||||
using namespace gpu::gl;
|
||||
|
||||
std::shared_ptr<GLTextureTransferHelper> 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<GLenum>& GLTexture::getFaceTargets(GLenum target) {
|
||||
static std::vector<GLenum> cubeFaceTargets {
|
||||
|
@ -96,228 +102,602 @@ const std::vector<GLenum>& 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<GLBackend>& 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<GLBackend>& 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<GLBackend>& 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<GLuint&>(_id) = 0;
|
||||
}
|
||||
Backend::textureExternalCount.decrement();
|
||||
}
|
||||
|
||||
|
||||
// Variable sized textures
|
||||
using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState;
|
||||
using WorkQueue = GLVariableAllocationSupport::WorkQueue;
|
||||
using TransferJobPointer = GLVariableAllocationSupport::TransferJobPointer;
|
||||
|
||||
std::list<TextureWeakPointer> GLVariableAllocationSupport::_memoryManagedTextures;
|
||||
MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle };
|
||||
std::atomic<bool> 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<void()> 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<GLuint&>(_size) = size;
|
||||
TransferJob::TransferJob(const GLTexture& parent, std::function<void()> 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<GLTextureTransferHelper>();
|
||||
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<GLTexture>(*texturePointer);
|
||||
GLVariableAllocationSupport* vargltexture = dynamic_cast<GLVariableAllocationSupport*>(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<TexturePointer> 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<GLTexture>(*texture);
|
||||
GLVariableAllocationSupport* vartexture = dynamic_cast<GLVariableAllocationSupport*>(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<GLTexture>(*texture);
|
||||
GLVariableAllocationSupport* vartexture = dynamic_cast<GLVariableAllocationSupport*>(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<GLTexture>(*texture);
|
||||
GLVariableAllocationSupport* vartexture = dynamic_cast<GLVariableAllocationSupport*>(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<GLTexture>(*texture);
|
||||
vartexture = dynamic_cast<GLVariableAllocationSupport*>(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);
|
||||
}
|
||||
}
|
|
@ -8,10 +8,15 @@
|
|||
#ifndef hifi_gpu_gl_GLTexture_h
|
||||
#define hifi_gpu_gl_GLTexture_h
|
||||
|
||||
#include <QtCore/QThreadPool>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "GLShared.h"
|
||||
#include "GLTextureTransfer.h"
|
||||
#include "GLBackend.h"
|
||||
#include "GLTexelFormat.h"
|
||||
#include <thread>
|
||||
|
||||
#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<TextureWeakPointer, float>;
|
||||
struct QueuePairLess {
|
||||
bool operator()(const QueuePair& a, const QueuePair& b) {
|
||||
return a.second < b.second;
|
||||
}
|
||||
};
|
||||
using WorkQueue = std::priority_queue<QueuePair, std::vector<QueuePair>, QueuePairLess>;
|
||||
|
||||
class TransferJob {
|
||||
using VoidLambda = std::function<void()>;
|
||||
using VoidLambdaQueue = std::queue<VoidLambda>;
|
||||
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<void> _bufferingStatus;
|
||||
static QThreadPool* _bufferThreadPool;
|
||||
#endif
|
||||
|
||||
public:
|
||||
TransferJob(const TransferJob& other) = delete;
|
||||
TransferJob(const GLTexture& parent, std::function<void()> 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<TransferJob>;
|
||||
using TransferQueue = std::queue<TransferJobPointer>;
|
||||
static MemoryPressureState _memoryPressureState;
|
||||
|
||||
public:
|
||||
static void addMemoryManagedTexture(const TexturePointer& texturePointer);
|
||||
|
||||
protected:
|
||||
static size_t _frameTexturesCreated;
|
||||
static std::atomic<bool> _memoryPressureStateStale;
|
||||
static std::list<TextureWeakPointer> _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<Texture> {
|
||||
using Parent = GLObject<Texture>;
|
||||
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<GLTextureTransferHelper> _textureTransferHelper;
|
||||
|
||||
template <typename GLTextureType>
|
||||
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<GLsync>(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<GLTextureType>(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<GLTextureType>(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 <typename GLTextureType>
|
||||
static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) {
|
||||
if (!texture) {
|
||||
return 0;
|
||||
}
|
||||
GLTexture* object { nullptr };
|
||||
if (shouldSync) {
|
||||
object = sync<GLTextureType>(backend, texture, shouldSync);
|
||||
} else {
|
||||
object = Backend::getGPUObject<GLTextureType>(*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<GLenum>& 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<GLenum>& getFaceTargets(GLenum textureType);
|
||||
|
||||
static GLenum getGLTextureType(const Texture& texture);
|
||||
|
||||
|
||||
const GLuint _size { 0 }; // true size as reported by the gl api
|
||||
std::atomic<GLSyncState> _syncState { GLSyncState::Idle };
|
||||
|
||||
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id, bool transferrable);
|
||||
GLTexture(const std::weak_ptr<gl::GLBackend>& 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<void()> 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<gl::GLBackend>& backend, const Texture& texture, GLuint id);
|
||||
};
|
||||
|
||||
class GLExternalTexture : public GLTexture {
|
||||
using Parent = GLTexture;
|
||||
friend class GLBackend;
|
||||
public:
|
||||
~GLExternalTexture();
|
||||
protected:
|
||||
GLExternalTexture(const std::weak_ptr<gl::GLBackend>& 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
|
||||
|
|
|
@ -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 <gl/GLHelpers.h>
|
||||
#include <gl/Context.h>
|
||||
|
||||
#include "GLShared.h"
|
||||
#include "GLTexture.h"
|
||||
|
||||
#ifdef HAVE_NSIGHT
|
||||
#include "nvToolsExt.h"
|
||||
std::unordered_map<TexturePointer, nvtxRangeId_t> _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<GLTexture>(*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<GLuint, TEXTURE_TRANSFER_PBO_COUNT> 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<GLTexture>(*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<GLTexture>(*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;
|
||||
}
|
|
@ -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 <QtGlobal>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
#include <GenericQueueThread.h>
|
||||
|
||||
#include <gl/Context.h>
|
||||
|
||||
#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<TexturePointer>;
|
||||
using TextureListIterator = TextureList::iterator;
|
||||
|
||||
class GLTextureTransferHelper : public GenericThread {
|
||||
public:
|
||||
using VoidLambda = std::function<void()>;
|
||||
using VoidLambdaList = std::list<VoidLambda>;
|
||||
using Pointer = std::shared_ptr<GLTextureTransferHelper>;
|
||||
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
|
|
@ -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<GLBackend>& backend, const Texture& buffer, GLuint externalId);
|
||||
GLESTexture(const std::weak_ptr<GLBackend>& 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<GLBackend>& 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<void()> 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<GLBackend>& 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<GLBackend>& backend, const Texture& texture);
|
||||
~GLESAttachmentTexture();
|
||||
};
|
||||
|
||||
class GLESStrictResourceTexture : public GLESFixedAllocationTexture {
|
||||
using Parent = GLESFixedAllocationTexture;
|
||||
friend class GLESBackend;
|
||||
protected:
|
||||
GLESStrictResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||
~GLESStrictResourceTexture();
|
||||
};
|
||||
|
||||
class GLESVariableAllocationTexture : public GLESTexture, public GLVariableAllocationSupport {
|
||||
using Parent = GLESTexture;
|
||||
friend class GLESBackend;
|
||||
using PromoteLambda = std::function<void()>;
|
||||
|
||||
|
||||
protected:
|
||||
GLESVariableAllocationTexture(const std::weak_ptr<GLBackend>& 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<GLBackend>& 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;
|
||||
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
|
@ -67,3 +67,27 @@ GLuint GLESBackend::getBufferID(const Buffer& buffer) {
|
|||
GLBuffer* GLESBackend::syncGPUObject(const Buffer& buffer) {
|
||||
return GLESBuffer::sync<GLESBuffer>(*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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<GLESBackend::GLESTexture>(*_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<GLESBackend::GLESTexture>(*_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<GLenum> 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<GLESFramebuffer>(*this, framebuffer);
|
||||
}
|
||||
|
||||
|
|
113
libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp
Normal file
|
@ -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();
|
||||
}
|
||||
|
|
@ -12,41 +12,90 @@
|
|||
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
// #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<GLESTexture>(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<GLESVariableAllocationTexture*> (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<GLBackend>& backend, const Texture& texture)
|
||||
: GLTexture(backend, texture, allocate(texture)) {
|
||||
}
|
||||
|
||||
void GLESTexture::withPreservedTexture(std::function<void()> 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<GLESTexture>(*this, texture, transfer);
|
||||
}
|
||||
|
||||
GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texture, bool transfer) {
|
||||
return GLESTexture::sync<GLESTexture>(*this, texture, transfer);
|
||||
}
|
||||
|
||||
GLESTexture::GLESTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint externalId)
|
||||
: GLTexture(backend, texture, externalId) {
|
||||
}
|
||||
|
||||
GLESTexture::GLESTexture(const std::weak_ptr<GLBackend>& 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<GLsizei>(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<GLsizei>(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<GLBackend>& 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<uint16_t>(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<GLBackend>& 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<GLBackend>& 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<GLBackend>& 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<uint16_t>(_populatedMip, 2);
|
||||
uint16_t allocatedMip = std::max<uint16_t>(_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<MipDesc> sourceMips(numMips);
|
||||
|
||||
std::vector<GLubyte> bytes;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT);
|
||||
glBindTexture(texTarget, srcId);
|
||||
const auto& faceTargets = GLTexture::getFaceTargets(texTarget);
|
||||
GLint internalFormat { 0 };
|
||||
|
||||
// Collect the mip description from the source texture
|
||||
GLint bufferOffset { 0 };
|
||||
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
|
||||
auto& sourceMip = sourceMips[mip];
|
||||
|
||||
uint16_t sourceLevel = mip - srcMipOffset;
|
||||
|
||||
// Grab internal format once
|
||||
if (internalFormat == 0) {
|
||||
glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat);
|
||||
}
|
||||
|
||||
// Collect the size of the first face, and then compute the total size offset needed for this mip level
|
||||
auto mipDimensions = texture.evalMipDimensions(mip);
|
||||
sourceMip._width = mipDimensions.x;
|
||||
sourceMip._height = mipDimensions.y;
|
||||
#ifdef DEBUG_COPY
|
||||
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width);
|
||||
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height);
|
||||
#endif
|
||||
// TODO: retrieve the size of a compressed image
|
||||
assert(false);
|
||||
//glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize);
|
||||
sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize;
|
||||
sourceMip._offset = bufferOffset;
|
||||
bufferOffset += sourceMip._size;
|
||||
gpu::gl::checkGLError();
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
// Allocate the PBO to accomodate for all the mips to copy
|
||||
GLuint pbo { 0 };
|
||||
glGenBuffers(1, &pbo);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STATIC_COPY);
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
// Transfer from source texture to pbo
|
||||
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
|
||||
auto& sourceMip = sourceMips[mip];
|
||||
|
||||
uint16_t sourceLevel = mip - srcMipOffset;
|
||||
|
||||
for (GLint f = 0; f < (GLint)faceTargets.size(); f++) {
|
||||
// TODO: implement for android
|
||||
//glGetCompressedTexImage(faceTargets[f], sourceLevel, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
// Now populate the new texture from the pbo
|
||||
glBindTexture(texTarget, 0);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_TEX_UNIT);
|
||||
|
||||
// Transfer from pbo to new texture
|
||||
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
|
||||
auto& sourceMip = sourceMips[mip];
|
||||
|
||||
uint16_t destLevel = mip - destMipOffset;
|
||||
|
||||
for (GLint f = 0; f < (GLint)faceTargets.size(); f++) {
|
||||
#ifdef DEBUG_COPY
|
||||
GLint destWidth, destHeight, destSize;
|
||||
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth);
|
||||
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_HEIGHT, &destHeight);
|
||||
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize);
|
||||
#endif
|
||||
glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat,
|
||||
sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
|
||||
gpu::gl::checkGLError();
|
||||
}
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glDeleteBuffers(1, &pbo);
|
||||
}
|
||||
|
||||
void GLESVariableAllocationTexture::copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
|
||||
uint16_t numMips = _gpuObject.getNumMips();
|
||||
withPreservedTexture([&] {
|
||||
if (_texelFormat.isCompressed()) {
|
||||
copyCompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips);
|
||||
} else {
|
||||
copyUncompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GLESVariableAllocationTexture::promote() {
|
||||
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
|
||||
Q_ASSERT(_allocatedMip > 0);
|
||||
|
||||
uint16_t targetAllocatedMip = _allocatedMip - std::min<uint16_t>(_allocatedMip, 2);
|
||||
targetAllocatedMip = std::max<uint16_t>(_minAllocatedMip, targetAllocatedMip);
|
||||
|
||||
GLuint oldId = _id;
|
||||
auto oldSize = _size;
|
||||
uint16_t oldAllocatedMip = _allocatedMip;
|
||||
|
||||
// create new texture
|
||||
const_cast<GLuint&>(_id) = allocate(_gpuObject);
|
||||
|
||||
// allocate storage for new level
|
||||
allocateStorage(targetAllocatedMip);
|
||||
|
||||
// copy pre-existing mips
|
||||
copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip);
|
||||
|
||||
// destroy the old texture
|
||||
glDeleteTextures(1, &oldId);
|
||||
|
||||
// Update sampler
|
||||
syncSampler();
|
||||
|
||||
// update the memory usage
|
||||
Backend::textureResourceGPUMemSize.update(oldSize, 0);
|
||||
// no change to Backend::textureResourcePopulatedGPUMemSize
|
||||
|
||||
populateTransferQueue();
|
||||
}
|
||||
|
||||
void GLESVariableAllocationTexture::demote() {
|
||||
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
|
||||
Q_ASSERT(_allocatedMip < _maxAllocatedMip);
|
||||
auto oldId = _id;
|
||||
auto oldSize = _size;
|
||||
auto oldPopulatedMip = _populatedMip;
|
||||
|
||||
// allocate new texture
|
||||
const_cast<GLuint&>(_id) = allocate(_gpuObject);
|
||||
uint16_t oldAllocatedMip = _allocatedMip;
|
||||
allocateStorage(_allocatedMip + 1);
|
||||
_populatedMip = std::max(_populatedMip, _allocatedMip);
|
||||
|
||||
|
||||
// copy pre-existing mips
|
||||
copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip);
|
||||
|
||||
// destroy the old texture
|
||||
glDeleteTextures(1, &oldId);
|
||||
|
||||
// Update sampler
|
||||
syncSampler();
|
||||
|
||||
// update the memory usage
|
||||
Backend::textureResourceGPUMemSize.update(oldSize, 0);
|
||||
// Demoting unpopulate the memory delta
|
||||
if (oldPopulatedMip != _populatedMip) {
|
||||
auto numPopulatedDemoted = _populatedMip - oldPopulatedMip;
|
||||
Size amountUnpopulated = 0;
|
||||
for (int i = 0; i < numPopulatedDemoted; i++) {
|
||||
amountUnpopulated += _gpuObject.evalMipSize(oldPopulatedMip + i);
|
||||
}
|
||||
decrementPopulatedSize(amountUnpopulated);
|
||||
}
|
||||
populateTransferQueue();
|
||||
}
|
||||
|
||||
|
||||
void GLESVariableAllocationTexture::populateTransferQueue() {
|
||||
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
|
||||
if (_populatedMip <= _allocatedMip) {
|
||||
return;
|
||||
}
|
||||
_pendingTransfers = TransferQueue();
|
||||
|
||||
const uint8_t maxFace = GLTexture::getFaceCount(_target);
|
||||
uint16_t sourceMip = _populatedMip;
|
||||
do {
|
||||
--sourceMip;
|
||||
auto targetMip = sourceMip - _allocatedMip;
|
||||
auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip);
|
||||
for (uint8_t face = 0; face < maxFace; ++face) {
|
||||
if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the mip is less than the max transfer size, then just do it in one transfer
|
||||
if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) {
|
||||
// Can the mip be transferred in one go
|
||||
_pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face));
|
||||
continue;
|
||||
}
|
||||
|
||||
// break down the transfers into chunks so that no single transfer is
|
||||
// consuming more than X bandwidth
|
||||
// For compressed format, regions must be a multiple of the 4x4 tiles, so enforce 4 lines as the minimum block
|
||||
auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face);
|
||||
const auto lines = mipDimensions.y;
|
||||
const uint32_t BLOCK_NUM_LINES { 4 };
|
||||
const auto numBlocks = (lines + (BLOCK_NUM_LINES - 1)) / BLOCK_NUM_LINES;
|
||||
auto bytesPerBlock = mipSize / numBlocks;
|
||||
Q_ASSERT(0 == (mipSize % lines));
|
||||
uint32_t linesPerTransfer = BLOCK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerBlock);
|
||||
uint32_t lineOffset = 0;
|
||||
while (lineOffset < lines) {
|
||||
uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer);
|
||||
_pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset));
|
||||
lineOffset += linesToCopy;
|
||||
}
|
||||
}
|
||||
|
||||
// queue up the sampler and populated mip change for after the transfer has completed
|
||||
_pendingTransfers.emplace(new TransferJob(*this, [=] {
|
||||
_populatedMip = sourceMip;
|
||||
syncSampler();
|
||||
}));
|
||||
} while (sourceMip != _allocatedMip);
|
||||
}
|
||||
|
||||
// resource textures
|
||||
using GLESResourceTexture = GLESBackend::GLESResourceTexture;
|
||||
|
||||
GLESResourceTexture::GLESResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GLESVariableAllocationTexture(backend, texture) {
|
||||
if (texture.isAutogenerateMips()) {
|
||||
generateMips();
|
||||
}
|
||||
}
|
||||
|
||||
GLESResourceTexture::~GLESResourceTexture() {
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -38,9 +38,9 @@ void GLESBackend::transferTransformState(const Batch& batch) const {
|
|||
}
|
||||
|
||||
if (!batch._objects.empty()) {
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, _transform._objectBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
glBindBuffer(GL_TEXTURE_BUFFER, _transform._objectBuffer);
|
||||
glBufferData(GL_TEXTURE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_TEXTURE_BUFFER, 0);
|
||||
}
|
||||
|
||||
if (!batch._namedData.empty()) {
|
||||
|
@ -58,10 +58,44 @@ void GLESBackend::transferTransformState(const Batch& batch) const {
|
|||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._objectBuffer);
|
||||
glActiveTexture(GL_TEXTURE0 + GLESBackend::TRANSFORM_OBJECT_SLOT);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, _transform._objectBufferTexture);
|
||||
if (!batch._objects.empty()) {
|
||||
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, _transform._objectBuffer);
|
||||
}
|
||||
|
||||
CHECK_GL_ERROR();
|
||||
|
||||
// Make sure the current Camera offset is unknown before render Draw
|
||||
_transform._currentCameraOffset = INVALID_OFFSET;
|
||||
}
|
||||
|
||||
|
||||
void GLESBackend::updateTransform(const Batch& batch) {
|
||||
_transform.update(_commandIndex, _stereo);
|
||||
|
||||
auto& drawCallInfoBuffer = batch.getDrawCallInfoBuffer();
|
||||
if (batch._currentNamedCall.empty()) {
|
||||
auto& drawCallInfo = drawCallInfoBuffer[_currentDraw];
|
||||
if (_transform._enabledDrawcallInfoBuffer) {
|
||||
glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is disabled
|
||||
_transform._enabledDrawcallInfoBuffer = false;
|
||||
}
|
||||
glVertexAttribI4i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused, 0, 0);
|
||||
//glVertexAttribI2i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused);
|
||||
} else {
|
||||
if (!_transform._enabledDrawcallInfoBuffer) {
|
||||
glEnableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is enabled
|
||||
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
|
||||
glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, (isStereo() ? 2 : 1));
|
||||
#else
|
||||
glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, 1);
|
||||
#endif
|
||||
_transform._enabledDrawcallInfoBuffer = true;
|
||||
}
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer);
|
||||
glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_UNSIGNED_SHORT, 0, _transform._drawCallInfoOffsets[batch._currentNamedCall]);
|
||||
}
|
||||
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
|
@ -134,7 +134,7 @@ public:
|
|||
|
||||
float getAspectRatio() const { return getWidth() / (float) getHeight() ; }
|
||||
|
||||
#ifndef ANDROID
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
static const uint32 MAX_NUM_RENDER_BUFFERS = 8;
|
||||
#else
|
||||
static const uint32 MAX_NUM_RENDER_BUFFERS = 4;
|
||||
|
|
|
@ -680,6 +680,7 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
|
|||
|
||||
void generateMips(gpu::Texture* texture, QImage&& image, const std::atomic<bool>& abortProcessing = false, int face = -1) {
|
||||
#if CPU_MIPMAPS
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
PROFILE_RANGE(resource_parse, "generateMips");
|
||||
|
||||
if (image.format() == QIMAGE_HDR_FORMAT) {
|
||||
|
@ -687,6 +688,16 @@ void generateMips(gpu::Texture* texture, QImage&& image, const std::atomic<bool>
|
|||
} else {
|
||||
generateLDRMips(texture, std::move(image), abortProcessing, face);
|
||||
}
|
||||
|
||||
#else
|
||||
//texture->setAutoGenerateMips(false);
|
||||
texture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
for (uint16 level = 1; level < texture->getNumMips(); ++level) {
|
||||
QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level));
|
||||
QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
texture->assignStoredMip(level, mipImage.byteCount(), mipImage.constBits());
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
texture->setAutoGenerateMips(true);
|
||||
#endif
|
||||
|
@ -727,9 +738,15 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
|
|||
bool validAlpha = image.hasAlphaChannel();
|
||||
bool alphaAsMask = false;
|
||||
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
#else
|
||||
if (image.format() != QImage::Format_RGBA8888) {
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (validAlpha) {
|
||||
processTextureAlpha(image, validAlpha, alphaAsMask);
|
||||
|
@ -769,6 +786,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
|
|||
}
|
||||
theTexture->setUsage(usage.build());
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture.get(), std::move(image), abortProcessing);
|
||||
}
|
||||
|
||||
|
@ -875,6 +893,7 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr
|
|||
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture.get(), std::move(image), abortProcessing);
|
||||
}
|
||||
|
||||
|
@ -911,6 +930,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr
|
|||
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture.get(), std::move(image), abortProcessing);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ vec2 hash2D(vec3 position) {
|
|||
}
|
||||
|
||||
float noise3D(vec3 position) {
|
||||
float n = textureLod(fadeMaskMap, hash2D(position), 0).r;
|
||||
float n = textureLod(fadeMaskMap, hash2D(position), 0.0).r;
|
||||
return pow(n, 1.0/2.2); // Remove sRGB. Need to fix this later directly in the texture
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ float evalFadeNoiseGradient(FadeObjectParams params, vec3 position) {
|
|||
vec3 noisePositionFloored = floor(noisePosition);
|
||||
vec3 noisePositionFraction = fract(noisePosition);
|
||||
|
||||
noisePositionFraction = noisePositionFraction*noisePositionFraction*(3 - 2*noisePositionFraction);
|
||||
noisePositionFraction = noisePositionFraction*noisePositionFraction*(3.0 - 2.0*noisePositionFraction);
|
||||
|
||||
float noiseLowXLowYLowZ = noise3D(noisePositionFloored);
|
||||
float noiseLowXHighYLowZ = noise3D(noisePositionFloored+vec3(0,1,0));
|
||||
|
@ -84,7 +84,7 @@ float evalFadeAlpha(FadeObjectParams params, vec3 position) {
|
|||
}
|
||||
|
||||
void applyFadeClip(FadeObjectParams params, vec3 position) {
|
||||
if (evalFadeAlpha(params, position) < 0) {
|
||||
if (evalFadeAlpha(params, position) < 0.0) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
@ -95,12 +95,12 @@ void applyFade(FadeObjectParams params, vec3 position, out vec3 emissive) {
|
|||
alpha = -alpha;
|
||||
}
|
||||
|
||||
if (alpha < 0) {
|
||||
if (alpha < 0.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
float edgeMask = alpha * fadeParameters[params.category]._edgeWidthInvWidth.y;
|
||||
float edgeAlpha = 1.0-clamp(edgeMask, 0, 1);
|
||||
float edgeAlpha = 1.0-clamp(edgeMask, 0.0, 1.0);
|
||||
|
||||
edgeMask = step(edgeMask, 1.f);
|
||||
edgeAlpha *= edgeAlpha; // Square to have a nice ease out
|
||||
|
|
|
@ -18,42 +18,62 @@
|
|||
#include <ViewFrustum.h>
|
||||
#include <gpu/Context.h>
|
||||
#include "StencilMaskPass.h"
|
||||
#include "ZoneRenderer.h"
|
||||
#include "FadeEffect.h"
|
||||
#include "BackgroundStage.h"
|
||||
|
||||
#include "FramebufferCache.h"
|
||||
#include "TextureCache.h"
|
||||
|
||||
#include <gpu/Texture.h>
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include "nop_frag.h"
|
||||
|
||||
using namespace render;
|
||||
extern void initForwardPipelines(ShapePlumber& plumber);
|
||||
extern void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
||||
|
||||
void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output) {
|
||||
auto items = input.get<Input>();
|
||||
auto fadeEffect = DependencyManager::get<FadeEffect>();
|
||||
|
||||
// Prepare the ShapePipelines
|
||||
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
|
||||
initForwardPipelines(*shapePlumber);
|
||||
initForwardPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter());
|
||||
|
||||
// Extract opaques / transparents / lights / metas / overlays / background
|
||||
const auto& opaques = items.get0()[RenderFetchCullSortTask::OPAQUE_SHAPE];
|
||||
// const auto& transparents = items.get0()[RenderFetchCullSortTask::TRANSPARENT_SHAPE];
|
||||
const auto& transparents = items.get0()[RenderFetchCullSortTask::TRANSPARENT_SHAPE];
|
||||
// const auto& lights = items.get0()[RenderFetchCullSortTask::LIGHT];
|
||||
// const auto& metas = items.get0()[RenderFetchCullSortTask::META];
|
||||
const auto& metas = items.get0()[RenderFetchCullSortTask::META];
|
||||
// const auto& overlayOpaques = items.get0()[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE];
|
||||
// const auto& overlayTransparents = items.get0()[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE];
|
||||
const auto& background = items.get0()[RenderFetchCullSortTask::BACKGROUND];
|
||||
//const auto& background = items.get0()[RenderFetchCullSortTask::BACKGROUND];
|
||||
// const auto& spatialSelection = items[1];
|
||||
|
||||
const auto framebuffer = task.addJob<PrepareFramebuffer>("PrepareFramebuffer");
|
||||
|
||||
task.addJob<Draw>("DrawOpaques", opaques, shapePlumber);
|
||||
task.addJob<Stencil>("Stencil");
|
||||
task.addJob<DrawBackground>("DrawBackground", background);
|
||||
|
||||
// Bounds do not draw on stencil buffer, so they must come last
|
||||
task.addJob<DrawBounds>("DrawBounds", opaques);
|
||||
const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");
|
||||
|
||||
// Filter zones from the general metas bucket
|
||||
const auto zones = task.addJob<ZoneRendererTask>("ZoneRenderer", metas);
|
||||
|
||||
// task.addJob<DrawBackground>("DrawBackground", background);
|
||||
// Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job
|
||||
task.addJob<DrawBackgroundStage>("DrawBackgroundDeferred", lightingModel);
|
||||
|
||||
{ // Debug the bounds of the rendered items, still look at the zbuffer
|
||||
|
||||
task.addJob<DrawBounds>("DrawMetaBounds", metas);
|
||||
task.addJob<DrawBounds>("DrawBounds", opaques);
|
||||
|
||||
task.addJob<DrawBounds>("DrawZones", zones);
|
||||
}
|
||||
|
||||
task.addJob<Draw>("DrawTransparents", transparents, shapePlumber);
|
||||
|
||||
|
||||
// Blit!
|
||||
task.addJob<Blit>("Blit", framebuffer);
|
||||
|
@ -75,11 +95,11 @@ void PrepareFramebuffer::run(const RenderContextPointer& renderContext,
|
|||
|
||||
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
||||
auto colorTexture = gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
auto colorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_framebuffer->setRenderBuffer(0, colorTexture);
|
||||
|
||||
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
|
||||
auto depthTexture = gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
auto depthTexture = gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_framebuffer->setDepthStencilBuffer(depthTexture, depthFormat);
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ using namespace std::placeholders;
|
|||
|
||||
void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest = false);
|
||||
void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
||||
void initForwardPipelines(ShapePlumber& plumber);
|
||||
void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
||||
void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
|
||||
|
||||
void addPlumberPipeline(ShapePlumber& plumber,
|
||||
|
@ -436,12 +436,13 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
|
|||
skinModelShadowFadeVertex, modelShadowFadePixel, batchSetter, itemSetter);
|
||||
}
|
||||
|
||||
void initForwardPipelines(render::ShapePlumber& plumber) {
|
||||
void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter) {
|
||||
// Vertex shaders
|
||||
auto modelVertex = gpu::Shader::createVertex(std::string(model_vert));
|
||||
auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert));
|
||||
auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert));
|
||||
auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert));
|
||||
auto skinModelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_fade_vert));
|
||||
|
||||
// Pixel shaders
|
||||
auto modelPixel = gpu::Shader::createPixel(std::string(forward_model_frag));
|
||||
|
@ -449,38 +450,43 @@ void initForwardPipelines(render::ShapePlumber& plumber) {
|
|||
auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_map_frag));
|
||||
auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_specular_map_frag));
|
||||
auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_specular_map_frag));
|
||||
auto modelNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_map_fade_frag));
|
||||
|
||||
using Key = render::ShapeKey;
|
||||
auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, nullptr, nullptr);
|
||||
auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, _4, _5);
|
||||
// Opaques
|
||||
addPipeline(
|
||||
Key::Builder().withMaterial(),
|
||||
modelVertex, modelPixel);
|
||||
modelVertex, modelPixel, nullptr, nullptr);
|
||||
addPipeline(
|
||||
Key::Builder().withMaterial().withUnlit(),
|
||||
modelVertex, modelUnlitPixel);
|
||||
modelVertex, modelUnlitPixel, nullptr, nullptr);
|
||||
addPipeline(
|
||||
Key::Builder().withMaterial().withTangents(),
|
||||
modelNormalMapVertex, modelNormalMapPixel);
|
||||
modelNormalMapVertex, modelNormalMapPixel, nullptr, nullptr);
|
||||
addPipeline(
|
||||
Key::Builder().withMaterial().withSpecular(),
|
||||
modelVertex, modelSpecularMapPixel);
|
||||
modelVertex, modelSpecularMapPixel, nullptr, nullptr);
|
||||
addPipeline(
|
||||
Key::Builder().withMaterial().withTangents().withSpecular(),
|
||||
modelNormalMapVertex, modelNormalSpecularMapPixel);
|
||||
modelNormalMapVertex, modelNormalSpecularMapPixel, nullptr, nullptr);
|
||||
// Skinned
|
||||
addPipeline(
|
||||
Key::Builder().withMaterial().withSkinned(),
|
||||
skinModelVertex, modelPixel);
|
||||
skinModelVertex, modelPixel, nullptr, nullptr);
|
||||
addPipeline(
|
||||
Key::Builder().withMaterial().withSkinned().withTangents(),
|
||||
skinModelNormalMapVertex, modelNormalMapPixel);
|
||||
skinModelNormalMapVertex, modelNormalMapPixel, nullptr, nullptr);
|
||||
addPipeline(
|
||||
Key::Builder().withMaterial().withSkinned().withSpecular(),
|
||||
skinModelVertex, modelSpecularMapPixel);
|
||||
skinModelVertex, modelSpecularMapPixel, nullptr, nullptr);
|
||||
addPipeline(
|
||||
Key::Builder().withMaterial().withSkinned().withTangents().withSpecular(),
|
||||
skinModelNormalMapVertex, modelNormalSpecularMapPixel);
|
||||
skinModelNormalMapVertex, modelNormalSpecularMapPixel, nullptr, nullptr);
|
||||
addPipeline(
|
||||
Key::Builder().withMaterial().withSkinned().withTangents().withFade(),
|
||||
skinModelNormalMapFadeVertex, modelNormalMapFadePixel, batchSetter, itemSetter, nullptr, nullptr);
|
||||
|
||||
}
|
||||
|
||||
void addPlumberPipeline(ShapePlumber& plumber,
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptString>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QtCore/QDateTime>
|
||||
|
||||
class ScriptEngine;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include "ConsoleScriptingInterface.h"
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QUrl>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QRegularExpression>
|
||||
|
@ -31,6 +32,8 @@ QString TEMP_DIR_FORMAT { "%1-%2-%3" };
|
|||
const QString& PathUtils::resourcesPath() {
|
||||
#ifdef Q_OS_MAC
|
||||
static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/";
|
||||
#elif defined (ANDROID)
|
||||
static const QString staticResourcePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources/";
|
||||
#else
|
||||
static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/";
|
||||
#endif
|
||||
|
@ -50,7 +53,9 @@ const QString& PathUtils::projectRootPath() {
|
|||
#endif
|
||||
|
||||
const QString& PathUtils::qmlBasePath() {
|
||||
#ifdef DEV_BUILD
|
||||
#ifdef Q_OS_ANDROID
|
||||
static const QString staticResourcePath = "qrc:///qml/";
|
||||
#elif defined (DEV_BUILD)
|
||||
static const QString staticResourcePath = QUrl::fromLocalFile(projectRootPath() + "/interface/resources/qml/").toString();
|
||||
#else
|
||||
static const QString staticResourcePath = "qrc:///qml/";
|
||||
|
@ -71,7 +76,11 @@ QString PathUtils::getAppLocalDataPath() {
|
|||
}
|
||||
|
||||
// otherwise return standard path
|
||||
#ifdef Q_OS_ANDROID
|
||||
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/";
|
||||
#else
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/";
|
||||
#endif
|
||||
}
|
||||
|
||||
QString PathUtils::getAppDataFilePath(const QString& filename) {
|
||||
|
@ -203,3 +212,31 @@ bool PathUtils::isDescendantOf(const QUrl& childURL, const QUrl& parentURL) {
|
|||
QString parent = stripFilename(parentURL);
|
||||
return child.startsWith(parent, PathUtils::getFSCaseSensitivity());
|
||||
}
|
||||
|
||||
void PathUtils::copyDirDeep(QString src, QString dst) {
|
||||
QDir dir = QDir::root();
|
||||
dir.mkpath(dst);
|
||||
QDirIterator it(src, QStringList() << "*", QDir::Files|QDir::AllDirs, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString f = it.next();
|
||||
QFileInfo fInfo(f);
|
||||
QString newDst = dst + (dst.endsWith("/")?"":"/") + fInfo.fileName();
|
||||
if (fInfo.isFile()) {
|
||||
QFile dfile(f);
|
||||
if (dfile.exists(f))
|
||||
{
|
||||
if (dfile.copy(newDst)) {
|
||||
QFile::setPermissions(newDst, QFile::ReadOwner);
|
||||
} else {
|
||||
QFile::setPermissions(newDst, QFile::ReadOwner);
|
||||
// sometimes copy returns false but it worked anyway
|
||||
//qWarning() << "Could not copy to " << newDst;
|
||||
}
|
||||
}
|
||||
} else if (fInfo.isDir() ) {
|
||||
copyDirDeep(f, newDst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ public:
|
|||
// note: this is FS-case-sensitive version of parentURL.isParentOf(childURL)
|
||||
static bool isDescendantOf(const QUrl& childURL, const QUrl& parentURL);
|
||||
static QUrl defaultScriptsLocation(const QString& newDefault = "");
|
||||
static void copyDirDeep(QString src, QString dst);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -84,7 +84,11 @@ void FileUtils::locateFile(QString filePath) {
|
|||
QString FileUtils::standardPath(QString subfolder) {
|
||||
// standard path
|
||||
// Mac: ~/Library/Application Support/Interface
|
||||
#ifdef Q_OS_ANDROID
|
||||
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||
#else
|
||||
QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
#endif
|
||||
if (!subfolder.startsWith("/")) {
|
||||
subfolder.prepend("/");
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public: \
|
|||
private:
|
||||
|
||||
#define HIFI_QML_DEF(x) \
|
||||
const QUrl x::QML = QUrl(#x ".qml"); \
|
||||
const QUrl x::QML = QUrl("qrc:///qml/" #x ".qml"); \
|
||||
const QString x::NAME = #x; \
|
||||
\
|
||||
void x::registerType() { \
|
||||
|
|
|
@ -554,7 +554,9 @@ void OffscreenQmlSurface::render() {
|
|||
|
||||
GLuint texture = offscreenTextures.getNextTexture(_size);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
|
||||
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);
|
||||
#endif
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
_renderControl->render();
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
|
|
@ -16,13 +16,14 @@
|
|||
#if !defined(Q_OS_ANDROID)
|
||||
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
|
||||
|
||||
FileTypeProfile::FileTypeProfile(QObject* parent) :
|
||||
QQuickWebEngineProfile(parent)
|
||||
FileTypeProfile::FileTypeProfile(QObject* parent)
|
||||
: QQuickWebEngineProfile(parent)
|
||||
{
|
||||
static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";
|
||||
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
|
||||
|
||||
auto requestInterceptor = new FileTypeRequestInterceptor(this);
|
||||
setRequestInterceptor(requestInterceptor);
|
||||
|
||||
}
|
||||
#endif
|
|
@ -22,4 +22,4 @@ void FileTypeRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info
|
|||
RequestFilters::interceptFileType(info);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -11,14 +11,20 @@
|
|||
|
||||
#ifndef hifi_HFTabletWebEngineRequestInterceptor_h
|
||||
#define hifi_HFTabletWebEngineRequestInterceptor_h
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <QWebEngineUrlRequestInterceptor>
|
||||
|
||||
class HFTabletWebEngineRequestInterceptor : public QWebEngineUrlRequestInterceptor {
|
||||
class HFTabletWebEngineRequestInterceptor
|
||||
: public QWebEngineUrlRequestInterceptor
|
||||
{
|
||||
public:
|
||||
HFTabletWebEngineRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {};
|
||||
|
||||
HFTabletWebEngineRequestInterceptor(QObject* parent)
|
||||
: QWebEngineUrlRequestInterceptor(parent)
|
||||
{};
|
||||
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // hifi_HFWebEngineRequestInterceptor_h
|
||||
|
|
|
@ -17,13 +17,12 @@
|
|||
|
||||
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
|
||||
|
||||
HFWebEngineProfile::HFWebEngineProfile(QObject* parent) :
|
||||
QQuickWebEngineProfile(parent)
|
||||
HFWebEngineProfile::HFWebEngineProfile(QObject* parent)
|
||||
: QQuickWebEngineProfile(parent)
|
||||
{
|
||||
setStorageName(QML_WEB_ENGINE_STORAGE_NAME);
|
||||
|
||||
// we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user
|
||||
auto requestInterceptor = new HFWebEngineRequestInterceptor(this);
|
||||
auto requestInterceptor = new HFWebEngineRequestInterceptor(this);
|
||||
setRequestInterceptor(requestInterceptor);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,4 +22,4 @@ void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& i
|
|||
RequestFilters::interceptHFWebEngineRequest(info);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -79,4 +79,4 @@ void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info) {
|
|||
info.setHttpHeader(CONTENT_HEADER.toLocal8Bit(), TYPE_VALUE.toLocal8Bit());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,16 @@
|
|||
//
|
||||
|
||||
var DEFAULT_SCRIPTS_COMBINED = [
|
||||
];
|
||||
|
||||
function pushAll(dest, orig) {
|
||||
for (var k in orig) {
|
||||
dest.push(orig[k]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!App.isAndroid()) {
|
||||
pushAll(DEFAULT_SCRIPTS_COMBINED, [
|
||||
"system/progress.js",
|
||||
"system/away.js",
|
||||
"system/audio.js",
|
||||
|
@ -30,12 +40,40 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/dialTone.js",
|
||||
"system/firstPersonHMD.js",
|
||||
"system/tablet-ui/tabletUI.js"
|
||||
];
|
||||
]);
|
||||
} else {
|
||||
pushAll(DEFAULT_SCRIPTS_COMBINED, [
|
||||
"system/progress.js"/*,
|
||||
"system/away.js",
|
||||
"system/controllers/controllerDisplayManager.js",
|
||||
"system/controllers/handControllerGrabAndroid.js",
|
||||
"system/controllers/handControllerPointerAndroid.js",
|
||||
"system/controllers/squeezeHands.js",
|
||||
"system/controllers/grab.js",
|
||||
"system/controllers/teleport.js",
|
||||
"system/controllers/toggleAdvancedMovementForHandControllers.js",
|
||||
"system/dialTone.js",
|
||||
"system/firstPersonHMD.js",
|
||||
"system/bubble.js",
|
||||
"system/android.js",
|
||||
"developer/debugging/debugAndroidMouse.js"*/
|
||||
]);
|
||||
}
|
||||
|
||||
var DEFAULT_SCRIPTS_SEPARATE = [
|
||||
"system/controllers/controllerScripts.js"
|
||||
//"system/chat.js"
|
||||
];
|
||||
|
||||
|
||||
if (!App.isAndroid()) {
|
||||
pushAll(DEFAULT_SCRIPTS_SEPARATE, [
|
||||
"system/controllers/controllerScripts.js"
|
||||
]);
|
||||
} else {
|
||||
pushAll(DEFAULT_SCRIPTS_SEPARATE, []);
|
||||
}
|
||||
|
||||
// add a menu item for debugging
|
||||
var MENU_CATEGORY = "Developer";
|
||||
var MENU_ITEM = "Debug defaultScripts.js";
|
||||
|
|