From b93e91b9f3b757ce76411eb1380f41dc459d3f6b Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 12 Sep 2017 14:38:27 -0700 Subject: [PATCH] New android toolchain --- .gitattributes | 1 + .gitignore | 5 + BUILD_ANDROID.md | 59 +- CMakeLists.txt | 17 +- android/app/CMakeLists.txt | 8 + android/app/build.gradle | 57 + android/app/proguard-rules.pro | 25 + android/app/src/main/AndroidManifest.xml | 37 + android/app/src/main/cpp/GoogleVRHelpers.h | 50 + android/app/src/main/cpp/native-lib.cpp | 78 + android/app/src/main/cpp/renderer.cpp | 636 ++++++ android/app/src/main/cpp/renderer.h | 60 + .../saintandreas/testapp/MainActivity.java | 105 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3418 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 4208 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2206 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2555 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4842 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 6114 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7718 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10056 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10486 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 14696 bytes android/app/src/main/res/values/colors.xml | 4 + android/app/src/main/res/values/strings.xml | 3 + android/app/src/main/res/values/styles.xml | 15 + android/build.gradle | 91 + android/gradle.properties | 17 + android/settings.gradle | 1 + cmake/android/AndroidManifest.xml.in | 82 - cmake/android/QtCreateAPK.cmake | 159 -- cmake/android/android.toolchain.cmake | 1725 ----------------- cmake/android/deployment-file.json.in | 13 - cmake/android/strings.xml.in | 11 - cmake/externals/glm/CMakeLists.txt | 2 +- cmake/externals/tbb/CMakeLists.txt | 4 +- cmake/init.cmake | 21 +- cmake/macros/AutoScribeShader.cmake | 4 +- cmake/macros/SetPackagingParameters.cmake | 1 + cmake/macros/SetupHifiLibrary.cmake | 4 +- cmake/macros/SetupQt.cmake | 2 +- cmake/macros/TargetGlew.cmake | 12 +- cmake/macros/TargetOpenGL.cmake | 6 +- cmake/macros/TargetOpenSSL.cmake | 32 + cmake/macros/TargetTBB.cmake | 24 + interface/CMakeLists.txt | 6 - libraries/audio-client/src/AudioClient.cpp | 30 + libraries/audio-client/src/AudioClient.h | 9 +- libraries/audio/src/AudioGate.cpp | 7 +- libraries/avatars/src/AvatarData.cpp | 2 +- libraries/controllers/CMakeLists.txt | 4 - .../src/display-plugins/DisplayPlugin.cpp | 3 + .../hmd/DebugHmdDisplayPlugin.cpp | 10 +- .../hmd/DebugHmdDisplayPlugin.h | 1 - .../entities-renderer/src/paintStroke.slf | 2 +- .../entities/src/EntityScriptingInterface.cpp | 28 +- .../entities/src/EntityScriptingInterface.h | 12 +- libraries/entities/src/EntityTree.cpp | 2 +- libraries/entities/src/PolyLineEntityItem.h | 12 +- libraries/entities/src/ShapeEntityItem.cpp | 2 +- libraries/fbx/src/OBJReader.cpp | 13 +- libraries/fbx/src/OBJReader.h | 2 +- libraries/gl/CMakeLists.txt | 6 +- libraries/gl/src/gl/Config.cpp | 35 + libraries/gl/src/gl/Config.h | 66 +- libraries/gl/src/gl/GLHelpers.cpp | 7 + libraries/gl/src/gl/GLHelpers.h | 7 +- libraries/gl/src/gl/OffscreenGLCanvas.cpp | 2 +- libraries/gl/src/gl/OpenGLVersionChecker.cpp | 3 - libraries/gpu-gl/CMakeLists.txt | 5 - .../gpu-gl/src/gpu/gl41/GL41BackendShader.cpp | 4 +- .../gpu-gl/src/gpu/gl45/GL45BackendShader.cpp | 3 +- libraries/gpu-gles/CMakeLists.txt | 11 + libraries/gpu-gles/src/gpu/gl/GLBackend.cpp | 722 +++++++ libraries/gpu-gles/src/gpu/gl/GLBackend.h | 427 ++++ .../gpu-gles/src/gpu/gl/GLBackendInput.cpp | 338 ++++ .../gpu-gles/src/gpu/gl/GLBackendOutput.cpp | 169 ++ .../gpu-gles/src/gpu/gl/GLBackendPipeline.cpp | 250 +++ .../gpu-gles/src/gpu/gl/GLBackendQuery.cpp | 93 + .../gpu-gles/src/gpu/gl/GLBackendState.cpp | 334 ++++ .../gpu-gles/src/gpu/gl/GLBackendTexture.cpp | 40 + .../src/gpu/gl/GLBackendTransform.cpp | 212 ++ libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp | 35 + libraries/gpu-gles/src/gpu/gl/GLBuffer.h | 66 + .../gpu-gles/src/gpu/gl/GLFramebuffer.cpp | 48 + libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h | 77 + .../gpu-gles/src/gpu/gl/GLInputFormat.cpp | 33 + libraries/gpu-gles/src/gpu/gl/GLInputFormat.h | 29 + libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp | 62 + libraries/gpu-gles/src/gpu/gl/GLPipeline.h | 29 + libraries/gpu-gles/src/gpu/gl/GLQuery.h | 67 + libraries/gpu-gles/src/gpu/gl/GLShader.cpp | 224 +++ libraries/gpu-gles/src/gpu/gl/GLShader.h | 59 + libraries/gpu-gles/src/gpu/gl/GLShared.cpp | 879 +++++++++ libraries/gpu-gles/src/gpu/gl/GLShared.h | 167 ++ libraries/gpu-gles/src/gpu/gl/GLState.cpp | 248 +++ libraries/gpu-gles/src/gpu/gl/GLState.h | 73 + .../gpu-gles/src/gpu/gl/GLTexelFormat.cpp | 648 +++++++ libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h | 32 + libraries/gpu-gles/src/gpu/gl/GLTexture.cpp | 323 +++ libraries/gpu-gles/src/gpu/gl/GLTexture.h | 233 +++ .../gpu-gles/src/gpu/gl/GLTextureTransfer.cpp | 207 ++ .../gpu-gles/src/gpu/gl/GLTextureTransfer.h | 78 + .../gpu-gles/src/gpu/gles/GLESBackend.cpp | 197 ++ libraries/gpu-gles/src/gpu/gles/GLESBackend.h | 99 + .../src/gpu/gles/GLESBackendBuffer.cpp | 69 + .../src/gpu/gles/GLESBackendInput.cpp | 29 + .../src/gpu/gles/GLESBackendOutput.cpp | 169 ++ .../src/gpu/gles/GLESBackendQuery.cpp | 38 + .../src/gpu/gles/GLESBackendTexture.cpp | 176 ++ .../src/gpu/gles/GLESBackendTransform.cpp | 67 + libraries/gpu/src/gpu/Buffer.h | 1 + .../gpu/DrawTexcoordRectTransformUnitQuad.slv | 2 +- .../gpu/src/gpu/DrawTransformUnitQuad.slv | 2 +- .../gpu/src/gpu/DrawUnitQuadTexcoord.slv | 2 +- .../gpu/DrawViewportQuadTransformTexcoord.slv | 2 +- libraries/gpu/src/gpu/Forward.h | 5 + libraries/gpu/src/gpu/Frame.cpp | 4 + libraries/gpu/src/gpu/Frame.h | 2 +- libraries/gpu/src/gpu/Framebuffer.cpp | 1 + libraries/gpu/src/gpu/Framebuffer.h | 4 + libraries/gpu/src/gpu/Query.cpp | 4 +- libraries/gpu/src/gpu/Transform.slh | 15 +- libraries/image/CMakeLists.txt | 14 +- libraries/image/src/image/Image.cpp | 13 +- .../src/input-plugins/TouchscreenDevice.h | 2 +- libraries/ktx/CMakeLists.txt | 2 +- libraries/midi/src/Midi.h | 2 +- .../src/model-networking/ModelCache.cpp | 22 +- libraries/model/src/model/Light.slh | 2 +- .../src/model/LightIrradiance.shared.slh | 2 +- libraries/networking/CMakeLists.txt | 46 +- libraries/networking/src/AddressManager.cpp | 4 + libraries/networking/src/AssetClient.cpp | 4 + .../networking/src/AssetResourceRequest.cpp | 2 +- .../networking/src/HTTPResourceRequest.cpp | 6 +- libraries/networking/src/LimitedNodeList.cpp | 4 + libraries/networking/src/NodeList.cpp | 2 +- libraries/networking/src/NodePermissions.cpp | 21 +- libraries/networking/src/NodePermissions.h | 9 +- .../networking/src/udt/CongestionControl.h | 4 +- .../networking/src/udt/ControlPacket.cpp | 2 +- libraries/physics/src/CharacterRayResult.h | 26 +- .../physics/src/CharacterSweepResult.cpp | 6 +- libraries/physics/src/CharacterSweepResult.h | 4 +- .../physics/src/CollisionRenderMeshCache.h | 2 +- libraries/procedural/CMakeLists.txt | 2 +- libraries/render-utils/CMakeLists.txt | 2 +- .../render-utils/src/DeferredBufferRead.slh | 2 +- libraries/render-utils/src/FadeEffect.h | 4 +- libraries/render-utils/src/ForwardBuffer.slh | 68 + .../render-utils/src/ForwardBufferWrite.slh | 63 + .../render-utils/src/ForwardGlobalLight.slh | 198 ++ libraries/render-utils/src/GeometryCache.cpp | 4 +- .../src/LightClusterGrid_shared.slh | 14 +- libraries/render-utils/src/LightPoint.slh | 4 +- libraries/render-utils/src/LightSpot.slh | 4 +- libraries/render-utils/src/LightingModel.slh | 4 +- .../render-utils/src/MaterialTextures.slh | 4 +- .../render-utils/src/MeshPartPayload.cpp | 1 - libraries/render-utils/src/Model.h | 4 +- libraries/render-utils/src/PickItemsJob.cpp | 36 +- libraries/render-utils/src/PickItemsJob.h | 14 +- .../render-utils/src/SubsurfaceScattering.slh | 2 +- libraries/render-utils/src/forward_model.slf | 55 +- .../src/forward_model_normal_map.slf | 8 +- .../src/forward_model_normal_specular_map.slf | 8 +- .../src/forward_model_specular_map.slf | 6 +- .../src/forward_model_translucent.slf | 81 + .../render-utils/src/forward_model_unlit.slf | 6 +- libraries/render-utils/src/glowLine.slf | 2 +- .../src/lightClusters_drawClusterContent.slv | 6 +- .../lightClusters_drawClusterFromDepth.slv | 4 +- .../src/lightClusters_drawGrid.slv | 6 +- libraries/render-utils/src/simple.slf | 4 +- .../src/surfaceGeometry_makeCurvature.slf | 2 +- libraries/render/CMakeLists.txt | 3 +- libraries/render/src/render/Engine.h | 2 +- libraries/render/src/render/Item.h | 2 +- libraries/render/src/render/TransitionStage.h | 18 +- .../render/src/render/drawItemBounds.slf | 2 +- .../src/UsersScriptingInterface.cpp | 2 +- libraries/script-engine/src/Vec3.cpp | 2 +- .../script-engine/src/WebSocketClass.cpp | 2 +- libraries/shared/CMakeLists.txt | 2 - libraries/shared/src/DependencyManager.h | 16 +- libraries/shared/src/PathUtils.cpp | 12 +- libraries/shared/src/Preferences.h | 8 +- libraries/shared/src/SettingHandle.h | 2 +- libraries/shared/src/shared/PlatformHacks.h | 18 + .../shared/src/shared/platform/AndroidHacks.h | 50 + libraries/ui/CMakeLists.txt | 6 +- libraries/ui/src/VrMenu.cpp | 2 +- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 8 +- plugins/oculus/src/OculusHelpers.cpp | 2 +- .../src/OculusLegacyDisplayPlugin.cpp | 2 +- .../src/OculusLegacyDisplayPlugin.h | 2 +- tests/shaders/src/main.cpp | 33 +- tools/oven/src/ui/BakeWidget.cpp | 2 +- 199 files changed, 9414 insertions(+), 2358 deletions(-) create mode 100644 android/app/CMakeLists.txt create mode 100644 android/app/build.gradle create mode 100644 android/app/proguard-rules.pro create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/cpp/GoogleVRHelpers.h create mode 100644 android/app/src/main/cpp/native-lib.cpp create mode 100644 android/app/src/main/cpp/renderer.cpp create mode 100644 android/app/src/main/cpp/renderer.h create mode 100644 android/app/src/main/java/org/saintandreas/testapp/MainActivity.java create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/values/colors.xml create mode 100644 android/app/src/main/res/values/strings.xml create mode 100644 android/app/src/main/res/values/styles.xml create mode 100644 android/build.gradle create mode 100644 android/gradle.properties create mode 100644 android/settings.gradle delete mode 100755 cmake/android/AndroidManifest.xml.in delete mode 100644 cmake/android/QtCreateAPK.cmake delete mode 100755 cmake/android/android.toolchain.cmake delete mode 100644 cmake/android/deployment-file.json.in delete mode 100644 cmake/android/strings.xml.in create mode 100644 cmake/macros/TargetOpenSSL.cmake create mode 100644 cmake/macros/TargetTBB.cmake create mode 100644 libraries/gl/src/gl/Config.cpp create mode 100644 libraries/gpu-gles/CMakeLists.txt create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBackend.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBackend.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLBuffer.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLInputFormat.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLPipeline.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLQuery.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLShader.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLShader.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLShared.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLShared.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLState.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLState.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLTexture.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLTexture.h create mode 100644 libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp create mode 100644 libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h create mode 100644 libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp create mode 100644 libraries/gpu-gles/src/gpu/gles/GLESBackend.h create mode 100644 libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp create mode 100644 libraries/gpu-gles/src/gpu/gles/GLESBackendInput.cpp create mode 100644 libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp create mode 100644 libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp create mode 100644 libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp create mode 100644 libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp create mode 100644 libraries/render-utils/src/ForwardBuffer.slh create mode 100644 libraries/render-utils/src/ForwardBufferWrite.slh create mode 100644 libraries/render-utils/src/ForwardGlobalLight.slh create mode 100644 libraries/render-utils/src/forward_model_translucent.slf create mode 100644 libraries/shared/src/shared/PlatformHacks.h create mode 100644 libraries/shared/src/shared/platform/AndroidHacks.h diff --git a/.gitattributes b/.gitattributes index 406780d20a..4a06c4288a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,6 +10,7 @@ *.json text *.js text *.qml text +*.qrc text *.slf text *.slh text *.slv text diff --git a/.gitignore b/.gitignore index d6227f1f30..8aa82865a4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,11 @@ ext/ Makefile *.user +# Android Studio +*.iml +local.properties +android/libraries + # Xcode *.xcodeproj *.xcworkspace diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index d69d20ee8a..cc51e58b1d 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -1,19 +1,56 @@ Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Android specific instructions are found in this file. -### Android Dependencies +# Android Dependencies You will need the following tools to build our Android targets. -* [cmake](http://www.cmake.org/download/) ~> 3.5.1 -* [Qt](http://www.qt.io/download-open-source/#) ~> 5.6.2 -* [ant](http://ant.apache.org/bindownload.cgi) ~> 1.9.4 -* [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) ~> r10d -* [Android SDK](http://developer.android.com/sdk/installing/index.html) ~> 24.4.1.1 - * Install the latest Platform-tools - * Install the latest Build-tools - * Install the SDK Platform for API Level 19 - * Install Sources for Android SDK for API Level 19 - * Install the ARM EABI v7a System Image if you want to run an emulator. +* [Qt](http://www.qt.io/download-open-source/#) ~> 5.9.1 +* [Android Studio](https://developer.android.com/studio/index.html) +* [Google VR SDK](https://github.com/googlevr/gvr-android-sdk/releases) +* [Gradle](https://gradle.org/releases/) + +### Qt + +Download the Qt online installer. Run the installer and select the android_armv7 binaries. Installing to the default path is recommended + +### Android Studio + +Download the Android Studio installer and run it. Once installed, at the welcome screen, click configure in the lower right corner and select SDK manager + +From the SDK Platforms tab, select API level 26. + +* Install the ARM EABI v7a System Image if you want to run an emulator. + +From the SDK Tools tab select the following + +* Android SDK Build-Tools +* GPU Debugging Tools +* CMake (even if you have a separate CMake installation) +* LLDB +* Android SDK Platform-Tools +* Android SDK Tools +* Android SDK Tools +* NDK (even if you have the NDK installed separately) + +### Google VR SDK + +Download the 1.8 Google VR SDK [release](https://github.com/googlevr/gvr-android-sdk/archive/v1.80.0.zip). Unzip the archive to a location on your drive. + +### Gradle + +Download [Gradle 4.1](https://services.gradle.org/distributions/gradle-4.1-all.zip) and unzip it on your local drive. You may wish to add the location of the bin directory within the archive to your path + +#### Set up machine specific Gradle properties + +Create a `gradle.properties` file in ~/.gradle. Edit the file to contain the following + + QT5_ROOT=C\:\\Qt\\5.9.1\\android_armv7 + GVR_ROOT=C\:\\Android\\gvr-android-sdk + +Replace the paths with your local installations of Qt5 and the Google VR SDK + + +# TODO fix the rest You will also need to cross-compile the dependencies required for all platforms for Android, and help CMake find these compiled libraries on your machine. diff --git a/CMakeLists.txt b/CMakeLists.txt index be513abddb..9d3296a168 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,16 @@ -if (WIN32) +# If we're running under the gradle build, HIFI_ANDROID will be set here, but +# ANDROID will not be set until after the `project` statement. This is the *ONLY* +# place you need to use `HIFI_ANDROID` instead of `ANDROID` +if (WIN32 AND NOT HIFI_ANDROID) cmake_minimum_required(VERSION 3.7) else() cmake_minimum_required(VERSION 3.2) endif() -include("cmake/init.cmake") - project(hifi) +include("cmake/init.cmake") + include("cmake/compiler.cmake") if (NOT DEFINED SERVER_ONLY) @@ -54,11 +57,13 @@ endif() file(GLOB_RECURSE CMAKE_SRC cmake/*.cmake cmake/CMakeLists.txt) add_custom_target(cmake SOURCES ${CMAKE_SRC}) GroupSources("cmake") +unset(CMAKE_SRC) file(GLOB_RECURSE JS_SRC scripts/*.js unpublishedScripts/*.js) add_custom_target(js SOURCES ${JS_SRC}) GroupSources("scripts") GroupSources("unpublishedScripts") +unset(JS_SRC) # Locate the required Qt build on the filesystem setup_qt() @@ -77,6 +82,12 @@ option(USE_NSIGHT "Attempt to find the nSight libraries" 1) set_packaging_parameters() +# FIXME hack to work on the proper Android toolchain +if (ANDROID) + add_subdirectory(android/app) + return() +endif() + # add subdirectories for all targets if (BUILD_SERVER) add_subdirectory(assignment-client) diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt new file mode 100644 index 0000000000..2d6df925e9 --- /dev/null +++ b/android/app/CMakeLists.txt @@ -0,0 +1,8 @@ +set(TARGET_NAME native-lib) +setup_hifi_library() +link_hifi_libraries(shared networking gl gpu gpu-gles render-utils) +autoscribe_shader_lib(gpu model render render-utils) +target_opengl() +target_link_libraries(native-lib android log m) +target_include_directories(native-lib PRIVATE "${GVR_ROOT}/libraries/headers") +target_link_libraries(native-lib "C:/Users/bdavis/Git/hifi/android/libraries/jni/armeabi-v7a/libgvr.so") diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000000..bd1c596bf3 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,57 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + buildToolsVersion "26.0.1" + defaultConfig { + applicationId "org.saintandreas.testapp" + minSdkVersion 24 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + ndk { abiFilters 'armeabi-v7a' } + externalNativeBuild { + cmake { + arguments '-DHIFI_ANDROID=1', + '-DANDROID_PLATFORM=android-24', + '-DANDROID_TOOLCHAIN=clang', + '-DANDROID_STL=gnustl_shared', + '-DGVR_ROOT=' + GVR_ROOT, + '-DNATIVE_SCRIBE=c:/bin/scribe.exe', + "-DHIFI_ANDROID_PRECOMPILED=${project.rootDir}/libraries/jni/armeabi-v7a" + } + } + jackOptions { enabled true } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + sourceSets { + main { + jniLibs.srcDirs += '../libraries/jni'; + } + } + externalNativeBuild { + cmake { + path '../../CMakeLists.txt' + } + } +} + +dependencies { + compile fileTree(dir: "${project.rootDir}/libraries/jar", include: 'QtAndroid-bundled.jar') + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.google.vr:sdk-audio:1.80.0' + compile 'com.google.vr:sdk-base:1.80.0' +} + +build.dependsOn(':extractQt5') diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000000..b3c0078513 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Android\SDK/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..05547bd5ae --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/cpp/GoogleVRHelpers.h b/android/app/src/main/cpp/GoogleVRHelpers.h new file mode 100644 index 0000000000..10c46b036f --- /dev/null +++ b/android/app/src/main/cpp/GoogleVRHelpers.h @@ -0,0 +1,50 @@ +#include +#include +#include + +namespace googlevr { + + // Convert a GVR matrix to GLM matrix + glm::mat4 toGlm(const gvr::Mat4f &matrix) { + glm::mat4 result; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + result[j][i] = matrix.m[i][j]; + } + } + return result; + } + + // Given a field of view in degrees, compute the corresponding projection +// matrix. + glm::mat4 perspectiveMatrixFromView(const gvr::Rectf& fov, float z_near, float z_far) { + const float x_left = -std::tan(fov.left * M_PI / 180.0f) * z_near; + const float x_right = std::tan(fov.right * M_PI / 180.0f) * z_near; + const float y_bottom = -std::tan(fov.bottom * M_PI / 180.0f) * z_near; + const float y_top = std::tan(fov.top * M_PI / 180.0f) * z_near; + const float Y = (2 * z_near) / (y_top - y_bottom); + const float A = (x_right + x_left) / (x_right - x_left); + const float B = (y_top + y_bottom) / (y_top - y_bottom); + const float C = (z_near + z_far) / (z_near - z_far); + const float D = (2 * z_near * z_far) / (z_near - z_far); + + glm::mat4 result { 0 }; + result[2][0] = A; + result[1][1] = Y; + result[2][1] = B; + result[2][2] = C; + result[3][2] = D; + result[2][3] = -1; + return result; + } + + glm::quat toGlm(const gvr::ControllerQuat& q) { + glm::quat result; + result.w = q.qw; + result.x = q.qx; + result.y = q.qy; + result.z = q.qz; + return result; + } + +} diff --git a/android/app/src/main/cpp/native-lib.cpp b/android/app/src/main/cpp/native-lib.cpp new file mode 100644 index 0000000000..156d43d849 --- /dev/null +++ b/android/app/src/main/cpp/native-lib.cpp @@ -0,0 +1,78 @@ +#include + +#include +#include + +#include "renderer.h" + +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()); +} + +static jlong toJni(NativeRenderer *renderer) { + return reinterpret_cast(renderer); +} + +static NativeRenderer *fromJni(jlong renderer) { + return reinterpret_cast(renderer); +} + +#define JNI_METHOD(r, name) JNIEXPORT r JNICALL Java_org_saintandreas_testapp_MainActivity_##name + +extern "C" { + +JNI_METHOD(jlong, nativeCreateRenderer) +(JNIEnv *env, jclass clazz, jobject class_loader, jobject android_context, jlong native_gvr_api) { + qInstallMessageHandler(messageHandler); +#if defined(GVR) + auto gvrContext = reinterpret_cast(native_gvr_api); + return toJni(new NativeRenderer(gvrContext)); +#else + return toJni(new NativeRenderer(nullptr)); +#endif +} + +JNI_METHOD(void, nativeDestroyRenderer) +(JNIEnv *env, jclass clazz, jlong renderer) { + delete fromJni(renderer); +} + +JNI_METHOD(void, nativeInitializeGl) +(JNIEnv *env, jobject obj, jlong renderer) { + fromJni(renderer)->InitializeGl(); +} + +JNI_METHOD(void, nativeDrawFrame) +(JNIEnv *env, jobject obj, jlong renderer) { + fromJni(renderer)->DrawFrame(); +} + +JNI_METHOD(void, nativeOnTriggerEvent) +(JNIEnv *env, jobject obj, jlong renderer) { + fromJni(renderer)->OnTriggerEvent(); +} + +JNI_METHOD(void, nativeOnPause) +(JNIEnv *env, jobject obj, jlong renderer) { + fromJni(renderer)->OnPause(); +} + +JNI_METHOD(void, nativeOnResume) +(JNIEnv *env, jobject obj, jlong renderer) { + fromJni(renderer)->OnResume(); +} + +} // extern "C" diff --git a/android/app/src/main/cpp/renderer.cpp b/android/app/src/main/cpp/renderer.cpp new file mode 100644 index 0000000000..a877ebd777 --- /dev/null +++ b/android/app/src/main/cpp/renderer.cpp @@ -0,0 +1,636 @@ +#include "renderer.h" + +#include + +#include +#include + +#include "GoogleVRHelpers.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#endif + + +template +void withFrameBuffer(gvr::Frame& frame, int32_t index, F f) { + frame.BindBuffer(index); + f(); + frame.Unbind(); +} + + +static const uint64_t kPredictionTimeWithoutVsyncNanos = 50000000; + +// Each shader has two variants: a single-eye ES 2.0 variant, and a multiview +// ES 3.0 variant. The multiview vertex shaders use transforms defined by +// arrays of mat4 uniforms, using gl_ViewID_OVR to determine the array index. + +#define UNIFORM_LIGHT_POS 20 +#define UNIFORM_M 16 +#define UNIFORM_MV 8 +#define UNIFORM_MVP 0 + +#if 0 +uniform Transform { // API uses “Transform[2]” to refer to instance 2 + mat4 u_MVP[2]; + mat4 u_MVMatrix[2]; + mat4 u_Model; + vec3 u_LightPos[2]; +}; +static const char *kDiffuseLightingVertexShader = R"glsl( +#version 300 es +#extension GL_OVR_multiview2 : enable + +layout(num_views=2) in; + +layout(location = 0) uniform mat4 u_MVP[2]; +layout(location = 8) uniform mat4 u_MVMatrix[2]; +layout(location = 16) uniform mat4 u_Model; +layout(location = 20) uniform vec3 u_LightPos[2]; + +layout(location = 0) in vec4 a_Position; +layout(location = 1) in vec4 a_Color; +layout(location = 2) in vec3 a_Normal; + +out vec4 v_Color; +out vec3 v_Grid; + +void main() { + mat4 mvp = u_MVP[gl_ViewID_OVR]; + mat4 modelview = u_MVMatrix[gl_ViewID_OVR]; + vec3 lightpos = u_LightPos[gl_ViewID_OVR]; + v_Grid = vec3(u_Model * a_Position); + vec3 modelViewVertex = vec3(modelview * a_Position); + vec3 modelViewNormal = vec3(modelview * vec4(a_Normal, 0.0)); + float distance = length(lightpos - modelViewVertex); + vec3 lightVector = normalize(lightpos - modelViewVertex); + float diffuse = max(dot(modelViewNormal, lightVector), 0.5); + diffuse = diffuse * (1.0 / (1.0 + (0.00001 * distance * distance))); + v_Color = vec4(a_Color.rgb * diffuse, a_Color.a); + gl_Position = mvp * a_Position; +} +)glsl"; +#endif + + +static const char *kSimepleVertexShader = R"glsl( +#version 300 es +#extension GL_OVR_multiview2 : enable + +layout(num_views=2) in; + +layout(location = 0) in vec4 a_Position; + +out vec4 v_Color; + +void main() { + v_Color = vec4(a_Position.xyz, 1.0); + gl_Position = vec4(a_Position.xyz, 1.0); +} +)glsl"; + + +static const char *kPassthroughFragmentShader = R"glsl( +#version 300 es +precision mediump float; +in vec4 v_Color; +out vec4 FragColor; + +void main() { FragColor = v_Color; } +)glsl"; + +static void CheckGLError(const char* label) { + int gl_error = glGetError(); + if (gl_error != GL_NO_ERROR) { + qWarning("GL error @ %s: %d", label, gl_error); + // Crash immediately to make OpenGL errors obvious. + abort(); + } +} + +// Contains vertex, normal and other data. +namespace cube { + const std::array CUBE_COORDS{{ + // Front face + -1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + + // Right face + 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, + 1.0f, 1.0f, -1.0f, + 1.0f, -1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, 1.0f, -1.0f, + + // Back face + 1.0f, 1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + -1.0f, -1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + + // Left face + -1.0f, 1.0f, -1.0f, + -1.0f, -1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, + -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + + // Top face + -1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, -1.0f, + + // Bottom face + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, 1.0f, + -1.0f, -1.0f, 1.0f, + -1.0f, -1.0f, -1.0f + }}; + + const std::array CUBE_COLORS{{ + // front, green + 0.0f, 0.5273f, 0.2656f, + 0.0f, 0.5273f, 0.2656f, + 0.0f, 0.5273f, 0.2656f, + 0.0f, 0.5273f, 0.2656f, + 0.0f, 0.5273f, 0.2656f, + 0.0f, 0.5273f, 0.2656f, + + // right, blue + 0.0f, 0.3398f, 0.9023f, + 0.0f, 0.3398f, 0.9023f, + 0.0f, 0.3398f, 0.9023f, + 0.0f, 0.3398f, 0.9023f, + 0.0f, 0.3398f, 0.9023f, + 0.0f, 0.3398f, 0.9023f, + + // back, also green + 0.0f, 0.5273f, 0.2656f, + 0.0f, 0.5273f, 0.2656f, + 0.0f, 0.5273f, 0.2656f, + 0.0f, 0.5273f, 0.2656f, + 0.0f, 0.5273f, 0.2656f, + 0.0f, 0.5273f, 0.2656f, + + // left, also blue + 0.0f, 0.3398f, 0.9023f, + 0.0f, 0.3398f, 0.9023f, + 0.0f, 0.3398f, 0.9023f, + 0.0f, 0.3398f, 0.9023f, + 0.0f, 0.3398f, 0.9023f, + 0.0f, 0.3398f, 0.9023f, + + // top, red + 0.8359375f, 0.17578125f, 0.125f, + 0.8359375f, 0.17578125f, 0.125f, + 0.8359375f, 0.17578125f, 0.125f, + 0.8359375f, 0.17578125f, 0.125f, + 0.8359375f, 0.17578125f, 0.125f, + 0.8359375f, 0.17578125f, 0.125f, + + // bottom, also red + 0.8359375f, 0.17578125f, 0.125f, + 0.8359375f, 0.17578125f, 0.125f, + 0.8359375f, 0.17578125f, 0.125f, + 0.8359375f, 0.17578125f, 0.125f, + 0.8359375f, 0.17578125f, 0.125f, + 0.8359375f, 0.17578125f, 0.125f + }}; + + const std::array CUBE_NORMALS{{ + // Front face + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + + // Right face + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + + // Back face + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -1.0f, + + // Left face + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + + // Top face + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + + // Bottom face + 0.0f, -1.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, -1.0f, 0.0f + }}; +} + +namespace triangle { + static std::array TRIANGLE_VERTS {{ + -0.5f, -0.5f, 0.0f, + 0.5f, -0.5f, 0.0f, + 0.0f, 0.5f, 0.0f + }}; +} + +std::array buildViewports(const std::unique_ptr &gvrapi) { + return { {gvrapi->CreateBufferViewport(), gvrapi->CreateBufferViewport()} }; +}; + +const std::string VERTEX_SHADER_DEFINES{ R"GLSL( +#version 300 es +#extension GL_EXT_clip_cull_distance : enable +#define GPU_VERTEX_SHADER +#define GPU_SSBO_TRANSFORM_OBJECT 1 +#define GPU_TRANSFORM_IS_STEREO +#define GPU_TRANSFORM_STEREO_CAMERA +#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED +#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN +)GLSL" }; + +const std::string PIXEL_SHADER_DEFINES{ R"GLSL( +#version 300 es +precision mediump float; +#define GPU_PIXEL_SHADER +#define GPU_TRANSFORM_IS_STEREO +#define GPU_TRANSFORM_STEREO_CAMERA +#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED +#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN +)GLSL" }; + + +#if defined(GVR) +NativeRenderer::NativeRenderer(gvr_context *vrContext) : + _gvrapi(new gvr::GvrApi(vrContext, false)), + _viewports(buildViewports(_gvrapi)), + _gvr_viewer_type(_gvrapi->GetViewerType()) +#else +NativeRenderer::NativeRenderer(void *vrContext) +#endif +{ + start = std::chrono::system_clock::now(); + qDebug() << "QQQ" << __FUNCTION__; +} + + +/** + * Converts a raw text file, saved as a resource, into an OpenGL ES shader. + * + * @param type The type of shader we will be creating. + * @param resId The resource ID of the raw text file. + * @return The shader object handler. + */ +int LoadGLShader(int type, const char *shadercode) { + GLuint result = 0; + std::string shaderError; + static const std::string SHADER_DEFINES; + if (!gl::compileShader(type, shadercode, SHADER_DEFINES, result, shaderError)) { + qWarning() << "QQQ" << __FUNCTION__ << "Shader compile failure" << shaderError.c_str(); + } + return result; +} + +// Computes a texture size that has approximately half as many pixels. This is +// equivalent to scaling each dimension by approximately sqrt(2)/2. +static gvr::Sizei HalfPixelCount(const gvr::Sizei &in) { + // Scale each dimension by sqrt(2)/2 ~= 7/10ths. + gvr::Sizei out; + out.width = (7 * in.width) / 10; + out.height = (7 * in.height) / 10; + return out; +} + + +#if defined(GVR) +void NativeRenderer::InitializeVR() { + _gvrapi->InitializeGl(); + bool multiviewEnabled = _gvrapi->IsFeatureSupported(GVR_FEATURE_MULTIVIEW); + qWarning() << "QQQ" << __FUNCTION__ << "Multiview enabled " << multiviewEnabled; + // Because we are using 2X MSAA, we can render to half as many pixels and + // achieve similar quality. + _renderSize = HalfPixelCount(_gvrapi->GetMaximumEffectiveRenderTargetSize()); + + std::vector specs; + specs.push_back(_gvrapi->CreateBufferSpec()); + specs[0].SetColorFormat(GVR_COLOR_FORMAT_RGBA_8888); + specs[0].SetDepthStencilFormat(GVR_DEPTH_STENCIL_FORMAT_DEPTH_16); + specs[0].SetSamples(2); + gvr::Sizei half_size = {_renderSize.width / 2, _renderSize.height}; + specs[0].SetMultiviewLayers(2); + specs[0].SetSize(half_size); + + _swapchain.reset(new gvr::SwapChain(_gvrapi->CreateSwapChain(specs))); + _viewportlist.reset(new gvr::BufferViewportList(_gvrapi->CreateEmptyBufferViewportList())); +} +void NativeRenderer::PrepareFramebuffer() { + const gvr::Sizei recommended_size = HalfPixelCount( + _gvrapi->GetMaximumEffectiveRenderTargetSize()); + if (_renderSize.width != recommended_size.width || + _renderSize.height != recommended_size.height) { + // We need to resize the framebuffer. Note that multiview uses two texture + // layers, each with half the render width. + gvr::Sizei framebuffer_size = recommended_size; + framebuffer_size.width /= 2; + _swapchain->ResizeBuffer(0, framebuffer_size); + _renderSize = recommended_size; + } +} +#endif + +void testShaderBuild(const char* vs_src, const char * fs_src) { + std::string error; + GLuint vs, fs; + if (!gl::compileShader(GL_VERTEX_SHADER, vs_src, VERTEX_SHADER_DEFINES, vs, error) || + !gl::compileShader(GL_FRAGMENT_SHADER, fs_src, PIXEL_SHADER_DEFINES, fs, error)) { + throw std::runtime_error("Failed to compile shader"); + } + auto pr = gl::compileProgram({ vs, fs }, error); + if (!pr) { + throw std::runtime_error("Failed to link shader"); + } +} + +void NativeRenderer::InitializeGl() { + qDebug() << "QQQ" << __FUNCTION__; + //gl::initModuleGl(); +#if defined(GVR) + InitializeVR(); +#endif + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + + + + const uint32_t vertShader = LoadGLShader(GL_VERTEX_SHADER, kSimepleVertexShader); + //const uint32_t vertShader = LoadGLShader(GL_VERTEX_SHADER, kDiffuseLightingVertexShader); + const uint32_t fragShader = LoadGLShader(GL_FRAGMENT_SHADER, kPassthroughFragmentShader); + std::string error; + _cubeProgram = gl::compileProgram({ vertShader, fragShader }, error); + CheckGLError("build program"); + + glGenBuffers(1, &_cubeBuffer); + glBindBuffer(GL_ARRAY_BUFFER, _cubeBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 9, triangle::TRIANGLE_VERTS.data(), GL_STATIC_DRAW); + /* + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 3, NULL, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 0, sizeof(float) * 108, cube::CUBE_COORDS.data()); + glBufferSubData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 1, sizeof(float) * 108, cube::CUBE_COLORS.data()); + glBufferSubData(GL_ARRAY_BUFFER, sizeof(float) * 108 * 2, sizeof(float) * 108, cube::CUBE_NORMALS.data()); + */ + glBindBuffer(GL_ARRAY_BUFFER, 0); + CheckGLError("upload vertices"); + + glGenVertexArrays(1, &_cubeVao); + glBindBuffer(GL_ARRAY_BUFFER, _cubeBuffer); + glBindVertexArray(_cubeVao); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + /* + glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, (const void*)(sizeof(float) * 108 * 1) ); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 3, GL_FLOAT, false, 0, (const void*)(sizeof(float) * 108 * 2)); + glEnableVertexAttribArray(2); + */ + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + CheckGLError("build vao "); + + static std::once_flag once; + std::call_once(once, [&]{ + testShaderBuild(sdf_text3D_vert, sdf_text3D_frag); + + testShaderBuild(DrawTransformUnitQuad_vert, DrawTexture_frag); + testShaderBuild(DrawTexcoordRectTransformUnitQuad_vert, DrawTexture_frag); + testShaderBuild(DrawViewportQuadTransformTexcoord_vert, DrawTexture_frag); + testShaderBuild(DrawTransformUnitQuad_vert, DrawTextureOpaque_frag); + testShaderBuild(DrawTransformUnitQuad_vert, DrawColoredTexture_frag); + + testShaderBuild(simple_vert, simple_frag); + testShaderBuild(simple_vert, simple_textured_frag); + testShaderBuild(simple_vert, simple_textured_unlit_frag); + testShaderBuild(deferred_light_vert, directional_ambient_light_frag); + testShaderBuild(deferred_light_vert, directional_skybox_light_frag); + testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag); + testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag); + + testShaderBuild(model_vert, model_frag); + testShaderBuild(model_normal_map_vert, model_normal_map_frag); + testShaderBuild(model_vert, model_specular_map_frag); + testShaderBuild(model_normal_map_vert, model_normal_specular_map_frag); + testShaderBuild(model_vert, model_translucent_frag); + testShaderBuild(model_normal_map_vert, model_translucent_frag); + testShaderBuild(model_lightmap_vert, model_lightmap_frag); + testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_map_frag); + testShaderBuild(model_lightmap_vert, model_lightmap_specular_map_frag); + testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_specular_map_frag); + + testShaderBuild(skin_model_vert, model_frag); + testShaderBuild(skin_model_normal_map_vert, model_normal_map_frag); + testShaderBuild(skin_model_vert, model_specular_map_frag); + testShaderBuild(skin_model_normal_map_vert, model_normal_specular_map_frag); + testShaderBuild(skin_model_vert, model_translucent_frag); + testShaderBuild(skin_model_normal_map_vert, model_translucent_frag); + + testShaderBuild(model_shadow_vert, model_shadow_frag); + + testShaderBuild(overlay3D_vert, overlay3D_frag); + +#if 0 + testShaderBuild(textured_particle_vert, textured_particle_frag); + testShaderBuild(skybox_vert, skybox_frag); + testShaderBuild(paintStroke_vert,paintStroke_frag); + testShaderBuild(polyvox_vert, polyvox_frag); +#endif + + }); + + qDebug() << "done"; +} + +static const float kZNear = 1.0f; +static const float kZFar = 100.0f; +static const gvr_rectf fullscreen = {0, 1, 0, 1}; + +void NativeRenderer::DrawFrame() { + auto now = std::chrono::duration_cast( + std::chrono::system_clock::now() - start); + glm::vec3 v; + v.r = (float) (now.count() % 1000) / 1000.0f; + v.g = 1.0f - v.r; + v.b = 1.0f; + + PrepareFramebuffer(); + + // A client app does its rendering here. + gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow(); + target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos; + + using namespace googlevr; + using namespace bilateral; + const auto gvrHeadPose = _gvrapi->GetHeadSpaceFromStartSpaceRotation(target_time); + _head_view = toGlm(gvrHeadPose); + _viewportlist->SetToRecommendedBufferViewports(); + + glm::mat4 eye_views[2]; + for_each_side([&](bilateral::Side side) { + int eye = index(side); + const gvr::Eye gvr_eye = eye == 0 ? GVR_LEFT_EYE : GVR_RIGHT_EYE; + const auto& eyeView = eye_views[eye] = toGlm(_gvrapi->GetEyeFromHeadMatrix(gvr_eye)) * _head_view; + auto& viewport = _viewports[eye]; + + _viewportlist->GetBufferViewport(eye, &viewport); + viewport.SetSourceUv(fullscreen); + viewport.SetSourceLayer(eye); + _viewportlist->SetBufferViewport(eye, viewport); + const auto &mvc = _modelview_cube[eye] = eyeView * _model_cube; + const auto &mvf = _modelview_floor[eye] = eyeView * _model_floor; + const gvr_rectf fov = viewport.GetSourceFov(); + const glm::mat4 perspective = perspectiveMatrixFromView(fov, kZNear, kZFar); + _modelview_projection_cube[eye] = perspective * mvc; + _modelview_projection_floor[eye] = perspective * mvf; + _light_pos_eye_space[eye] = glm::vec3(eyeView * _light_pos_world_space); + }); + + + gvr::Frame frame = _swapchain->AcquireFrame(); + withFrameBuffer(frame, 0, [&]{ + glClearColor(v.r, v.g, v.b, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, _renderSize.width / 2, _renderSize.height); + glUseProgram(_cubeProgram); + glBindVertexArray(_cubeVao); + glDrawArrays(GL_TRIANGLES, 0, 3); + /* + float* fp; + fp = (float*)&_light_pos_eye_space[0]; + glUniform3fv(UNIFORM_LIGHT_POS, 2, fp); + fp = (float*)&_modelview_cube[0]; + glUniformMatrix4fv(UNIFORM_MV, 2, GL_FALSE, fp); + fp = (float*)&_modelview_projection_cube[0]; + glUniformMatrix4fv(UNIFORM_MVP, 2, GL_FALSE, fp); + fp = (float*)&_model_cube; + glUniformMatrix4fv(UNIFORM_M, 1, GL_FALSE, fp); + glDrawArrays(GL_TRIANGLES, 0, 36); + */ + glBindVertexArray(0); + }); + + frame.Submit(*_viewportlist, gvrHeadPose); + CheckGLError("onDrawFrame"); + +} + +void NativeRenderer::OnTriggerEvent() { + qDebug() << "QQQ" << __FUNCTION__; +} + +void NativeRenderer::OnPause() { + qDebug() << "QQQ" << __FUNCTION__; + _gvrapi->PauseTracking(); +} + +void NativeRenderer::OnResume() { + qDebug() << "QQQ" << __FUNCTION__; + _gvrapi->ResumeTracking(); + _gvrapi->RefreshViewerProfile(); +} diff --git a/android/app/src/main/cpp/renderer.h b/android/app/src/main/cpp/renderer.h new file mode 100644 index 0000000000..df7c51cab4 --- /dev/null +++ b/android/app/src/main/cpp/renderer.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +#define GVR + +#if defined(GVR) +#include +#endif + +class NativeRenderer { +public: + +#if defined(GVR) + NativeRenderer(gvr_context* vrContext); +#else + NativeRenderer(void* vrContext); +#endif + + void InitializeGl(); + void DrawFrame(); + void OnTriggerEvent(); + void OnPause(); + void OnResume(); + +private: + + + std::chrono::time_point start; +#if defined(GVR) + void InitializeVR(); + void PrepareFramebuffer(); + + std::unique_ptr _gvrapi; + gvr::ViewerType _gvr_viewer_type; + std::unique_ptr _viewportlist; + std::unique_ptr _swapchain; + std::array _viewports; + gvr::Sizei _renderSize; +#endif + + uint32_t _cubeBuffer { 0 }; + uint32_t _cubeVao { 0 }; + uint32_t _cubeProgram { 0 }; + + glm::mat4 _head_view; + glm::mat4 _model_cube; + glm::mat4 _camera; + glm::mat4 _view; + glm::mat4 _model_floor; + + std::array _modelview_cube; + std::array _modelview_floor; + std::array _modelview_projection_cube; + std::array _modelview_projection_floor; + std::array _light_pos_eye_space; + const glm::vec4 _light_pos_world_space{ 0, 2, 0, 1}; +}; diff --git a/android/app/src/main/java/org/saintandreas/testapp/MainActivity.java b/android/app/src/main/java/org/saintandreas/testapp/MainActivity.java new file mode 100644 index 0000000000..7eea14dce9 --- /dev/null +++ b/android/app/src/main/java/org/saintandreas/testapp/MainActivity.java @@ -0,0 +1,105 @@ +package org.saintandreas.testapp; + +import android.app.Activity; +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.os.Bundle; +import android.view.View; + +import com.google.vr.ndk.base.AndroidCompat; +import com.google.vr.ndk.base.GvrLayout; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +public class MainActivity extends Activity { + private final static int IMMERSIVE_STICKY_VIEW_FLAGS = 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; + + static { + System.loadLibrary("gvr"); + System.loadLibrary("native-lib"); + } + + private long nativeRenderer; + private GvrLayout gvrLayout; + private GLSurfaceView surfaceView; + + private native long nativeCreateRenderer(ClassLoader appClassLoader, Context context, long nativeGvrContext); + private native void nativeDestroyRenderer(long renderer); + private native void nativeInitializeGl(long renderer); + private native void nativeDrawFrame(long renderer); + private native void nativeOnTriggerEvent(long renderer); + private native void nativeOnPause(long renderer); + private native void nativeOnResume(long renderer); + + class NativeRenderer implements GLSurfaceView.Renderer { + @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { nativeInitializeGl(nativeRenderer); } + @Override public void onSurfaceChanged(GL10 gl, int width, int height) { } + @Override public void onDrawFrame(GL10 gl) { + nativeDrawFrame(nativeRenderer); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setImmersiveSticky(); + getWindow() + .getDecorView() + .setOnSystemUiVisibilityChangeListener((int visibility)->{ + if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { setImmersiveSticky(); } + }); + + gvrLayout = new GvrLayout(this); + nativeRenderer = nativeCreateRenderer( + getClass().getClassLoader(), + getApplicationContext(), + gvrLayout.getGvrApi().getNativeGvrContext()); + + surfaceView = new GLSurfaceView(this); + surfaceView.setEGLContextClientVersion(3); + surfaceView.setEGLConfigChooser(8, 8, 8, 0, 0, 0); + surfaceView.setPreserveEGLContextOnPause(true); + surfaceView.setRenderer(new NativeRenderer()); + + gvrLayout.setPresentationView(surfaceView); + setContentView(gvrLayout); + if (gvrLayout.setAsyncReprojectionEnabled(true)) { + AndroidCompat.setSustainedPerformanceMode(this, true); + } + AndroidCompat.setVrModeEnabled(this, true); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + gvrLayout.shutdown(); + nativeDestroyRenderer(nativeRenderer); + nativeRenderer = 0; + } + + @Override + protected void onPause() { + surfaceView.queueEvent(()->nativeOnPause(nativeRenderer)); + surfaceView.onPause(); + gvrLayout.onPause(); + super.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + gvrLayout.onResume(); + surfaceView.onResume(); + surfaceView.queueEvent(()->nativeOnResume(nativeRenderer)); + } + + private void setImmersiveSticky() { + getWindow().getDecorView().setSystemUiVisibility(IMMERSIVE_STICKY_VIEW_FLAGS); + } +} diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..cde69bcccec65160d92116f20ffce4fce0b5245c GIT binary patch literal 3418 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..9a078e3e1a42d474c78470a73c7987cf7ac5d9a0 GIT binary patch literal 4208 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..3af2608a4492ef9ae63a77ec3305aedda89594cb GIT binary patch literal 6114 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..324e72cdd7480cb983fa1bcc7ce686e51ef87fe7 GIT binary patch literal 7718 zcmZ{p$IRXXZTL&R=k%^>oyT@agdZ03gy(S2h3uAn`xo;Q#=zt-9U<005SQ!gB=x zs7oODXN3&_0K`V!;5h&UZ~;I_7y#S?01&bR06xM1fVKnx=?nm%asS+=4+Q`K$68BG z8TjAbD76Oz004GZf9VAP1Qh=P1IYSJ3jj>q8p;ZWev1e2H;t5YO%LN0=$mni7RU$f zLQr88EfIGVt%NG&lR}Ru5f8tqGA@rxI*CpW-RBk}=$Ithq2wP3Vls&Vb!*0TQun?P zvP5Cr%)>u6MmbkSZEasFoe$CI&SH7vnfLAEUnk^VAAFzCD?>`7%ZuXw%Mmjq^3GRu zh%FI{QSjyKKU+*{0 z>wJT3cAWqaXqsK13(v&oB(A7rFxfzUOyi|(IETRwboZ0W*#s^rzG*HJA0Og&q;JjbZ>9hk;D_-&I`8g;WWARonr^_GWb! zT^Xw((8voS4L0z*{ivjkynrjAynQ8~i1WTJsz!dskVz`vpqt6Hl$-G6@!MiCsW&Ha zUuyPO(*F?Ulfdv#<9J(Z#xO&hVN$>wU|83iGz5X20^MZlbsTIV{HS)0 z*6SLok8QqXzG#Y%NvPCp=A2+TR7l&Oe7)5hC#|6h^k1Xe#9^%*)tvO1v;*7%%G2Yk zfA6~c@k+SR6#L6*%a^EoH=%ItTuNIIwlp0B${?a%r^vM6!B7*gtKM&q$sx$$pAcBh z5KL!AY)kU_bZ#No@6Ey&nX{WHf|7C?`ind*>6_WJ(t$x8`UR-Bwi-oT(ma+5`(Vmf zfEowTv6dp^&lx}co@v+;Lm3{Qw~$g>c8pnCOglkO@uGx?cHUIr({ z1YCBz?xW#IU0XwJRH27d8C<&k+`Q<3~< zGX5q-d7_*fD;{1?Afc5YvB=M<>g$oUFzjL zO2vX6eRbe|k}(Q}1~}L-ToUG=uODZ;N}Cf1;56|u4kh7oc-C#j(-3g6TGU5KXjJOh zKW`EL&QlkON3cK?R9S3Fj{)icgY(S5K$qM3HEKs~S>0=R8&adWdNbSf!h{=`@3poFp8L3j+;y*7% zq_oQN zbqPyz*^Ja~5})zdQhm_2JS#;xjb|V?+mC{FdIcR0GbE6YXg(Ta^gDK!w)M8A*Vm5{ zAyXo8k=nW_ll=EIdn}nwjM%N^rmmqjODY)Epnjf{LygEfFG@9w;NDEM8QyX)^4qGK zA-TEy>^~DQU$b(rF>%PeI5|lw`#9g5P9QF8niDDVw}|SFIbdU1_M!FH4t$8nT2lz%@JW(DF6PJ4l`8Kas zvp5b)3mVmDCYzsaa8|md7U5|67OS~hQrqr@c(?bRrmGQ>Mt-T-EO%dy0h)`!2AyKm zLDVS~t?yZ1J7%kSllk;Q9{Sv09HOdH3B`=5KEla_xmY6&IacCl$g5FU`rj;A@qZk# zthgTxH08NPxV*o$g7OkEHhpzflvT>XNetYqFk*Q)E6Pnj2V;w1_M`pkh?|HNMp&QN zYj&<)EBrc@4DPZgiV+(}3T0~}Opa0l_DMYV#jFDFC`cUFFWMen8>| zd{a9O(Af5&{GIC{GYdij2WFYVf#>drrE24?Qaxv#!zMn@5z74ICz7)7E`2GOs<9SJ zVzL|!nm#zH;Ml3N+-$|25zA~&)$FqLQi~

TYSD#W4maIvLbRYGJNG}V|+SXfTh>av49 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..9bec2e623103ac9713b00cad8502a057c1efda61 GIT binary patch literal 10056 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..344907f039 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #ffffff + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..5d6a4c1b99 --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + TestApp + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..033324ac58 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000000..77c3dd498c --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,91 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} + +task extractQt5jars(type: Copy) { + from fileTree(QT5_ROOT + "/jar") + into("${project.rootDir}/libraries/jar") + include("*.jar") +} + +task extractQt5so(type: Copy) { + from fileTree(QT5_ROOT + "/lib") + into("${project.rootDir}/libraries/jni/armeabi-v7a/") + include("libQt5AndroidExtras.so") + include("libQt5Concurrent.so") + include("libQt5Core.so") + include("libQt5Gamepad.so") + include("libQt5Gui.so") + include("libQt5Location.so") + include("libQt5Multimedia.so") + include("libQt5MultimediaQuick_p.so") + include("libQt5Network.so") + include("libQt5NetworkAuth.so") + include("libQt5OpenGL.so") + include("libQt5Positioning.so") + include("libQt5Qml.so") + include("libQt5Quick.so") + include("libQt5QuickControls2.so") + include("libQt5QuickParticles.so") + include("libQt5QuickTemplates2.so") + include("libQt5QuickWidgets.so") + include("libQt5Script.so") + include("libQt5ScriptTools.so") + include("libQt5Sensors.so") + include("libQt5Svg.so") + include("libQt5WebChannel.so") + include("libQt5WebSockets.so") + include("libQt5WebView.so") + include("libQt5Widgets.so") + include("libQt5Xml.so") + include("libQt5XmlPatterns.so") +} + +task extractAudioSo(type: Copy) { + from zipTree(GVR_ROOT + "/libraries/sdk-audio-1.80.0.aar") + into "${project.rootDir}/libraries/" + include "jni/armeabi-v7a/libgvr_audio.so" +} + +task extractGvrSo(type: Copy) { + from zipTree(GVR_ROOT + "/libraries/sdk-base-1.80.0.aar") + into "${project.rootDir}/libraries/" + include "jni/armeabi-v7a/libgvr.so" +} + +task extractNdk { } +extractNdk.dependsOn extractAudioSo +extractNdk.dependsOn extractGvrSo + +task extractQt5 { } +extractQt5.dependsOn extractQt5so +extractQt5.dependsOn extractQt5jars + +task extractBinaries { } +extractBinaries.dependsOn extractQt5 +extractBinaries.dependsOn extractNdk + +task deleteBinaries(type: Delete) { + delete "${project.rootDir}/libraries/jni" +} + +//clean.dependsOn(deleteBinaries) diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000000..aac7c9b461 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000000..e7b4def49c --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/cmake/android/AndroidManifest.xml.in b/cmake/android/AndroidManifest.xml.in deleted file mode 100755 index aa834f3384..0000000000 --- a/cmake/android/AndroidManifest.xml.in +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${ANDROID_EXTRA_ACTIVITY_XML} - - - - - - ${ANDROID_EXTRA_APPLICATION_XML} - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/cmake/android/QtCreateAPK.cmake b/cmake/android/QtCreateAPK.cmake deleted file mode 100644 index 30ee2f57bd..0000000000 --- a/cmake/android/QtCreateAPK.cmake +++ /dev/null @@ -1,159 +0,0 @@ -# -# QtCreateAPK.cmake -# -# Created by Stephen Birarda on 11/18/14. -# Copyright 2013 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 -# - -# -# OPTIONS -# These options will modify how QtCreateAPK behaves. May be useful if somebody wants to fork. -# For High Fidelity purposes these should not need to be changed. -# -set(ANDROID_THIS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) # Directory this CMake file is in - -if (POLICY CMP0026) - cmake_policy(SET CMP0026 OLD) -endif () - -macro(qt_create_apk) - if(ANDROID_APK_FULLSCREEN) - set(ANDROID_APK_THEME "android:theme=\"@android:style/Theme.NoTitleBar.Fullscreen\"") - else() - set(ANDROID_APK_THEME "") - endif() - - if (UPPER_CMAKE_BUILD_TYPE MATCHES RELEASE) - set(ANDROID_APK_DEBUGGABLE "false") - set(ANDROID_APK_RELEASE_LOCAL ${ANDROID_APK_RELEASE}) - else () - set(ANDROID_APK_DEBUGGABLE "true") - set(ANDROID_APK_RELEASE_LOCAL "0") - endif () - - # Create "AndroidManifest.xml" - configure_file("${ANDROID_THIS_DIRECTORY}/AndroidManifest.xml.in" "${ANDROID_APK_BUILD_DIR}/AndroidManifest.xml") - - # create "strings.xml" - configure_file("${ANDROID_THIS_DIRECTORY}/strings.xml.in" "${ANDROID_APK_BUILD_DIR}/res/values/strings.xml") - - # find androiddeployqt - find_program(ANDROID_DEPLOY_QT androiddeployqt HINTS "${QT_DIR}/bin") - - # set the path to our app shared library - set(EXECUTABLE_DESTINATION_PATH "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}/lib${TARGET_NAME}.so") - - # add our dependencies to the deployment file - get_property(_DEPENDENCIES TARGET ${TARGET_NAME} PROPERTY INTERFACE_LINK_LIBRARIES) - - foreach(_IGNORE_COPY IN LISTS IGNORE_COPY_LIBS) - list(REMOVE_ITEM _DEPENDENCIES ${_IGNORE_COPY}) - endforeach() - - foreach(_DEP IN LISTS _DEPENDENCIES) - if (NOT TARGET ${_DEP}) - list(APPEND _DEPS_LIST ${_DEP}) - else () - if(NOT _DEP MATCHES "Qt5::.*") - get_property(_DEP_LOCATION TARGET ${_DEP} PROPERTY "LOCATION_${CMAKE_BUILD_TYPE}") - - # recurisvely add libraries which are dependencies of this target - get_property(_DEP_DEPENDENCIES TARGET ${_DEP} PROPERTY INTERFACE_LINK_LIBRARIES) - - foreach(_SUB_DEP IN LISTS _DEP_DEPENDENCIES) - if (NOT TARGET ${_SUB_DEP} AND NOT _SUB_DEP MATCHES "Qt5::.*") - list(APPEND _DEPS_LIST ${_SUB_DEP}) - endif() - endforeach() - - list(APPEND _DEPS_LIST ${_DEP_LOCATION}) - endif() - endif () - endforeach() - - list(REMOVE_DUPLICATES _DEPS_LIST) - - # just copy static libs to apk libs folder - don't add to deps list - foreach(_LOCATED_DEP IN LISTS _DEPS_LIST) - if (_LOCATED_DEP MATCHES "\\.a$") - add_custom_command( - TARGET ${TARGET_NAME} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${_LOCATED_DEP} "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}" - ) - list(REMOVE_ITEM _DEPS_LIST ${_LOCATED_DEP}) - endif () - endforeach() - - string(REPLACE ";" "," _DEPS "${_DEPS_LIST}") - - configure_file("${ANDROID_THIS_DIRECTORY}/deployment-file.json.in" "${TARGET_NAME}-deployment.json") - - # copy the res folder from the target to the apk build dir - add_custom_target( - ${TARGET_NAME}-copy-res - COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/res" "${ANDROID_APK_BUILD_DIR}/res" - ) - - # copy the assets folder from the target to the apk build dir - add_custom_target( - ${TARGET_NAME}-copy-assets - COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/assets" "${ANDROID_APK_BUILD_DIR}/assets" - ) - - # copy the java folder from src to the apk build dir - add_custom_target( - ${TARGET_NAME}-copy-java - COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/src/java" "${ANDROID_APK_BUILD_DIR}/src" - ) - - # copy the libs folder from src to the apk build dir - add_custom_target( - ${TARGET_NAME}-copy-libs - COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/libs" "${ANDROID_APK_BUILD_DIR}/libs" - ) - - # handle setup for ndk-gdb - add_custom_target(${TARGET_NAME}-gdb DEPENDS ${TARGET_NAME}) - - if (ANDROID_APK_DEBUGGABLE) - get_property(TARGET_LOCATION TARGET ${TARGET_NAME} PROPERTY LOCATION) - - set(GDB_SOLIB_PATH ${ANDROID_APK_BUILD_DIR}/obj/local/${ANDROID_NDK_ABI_NAME}/) - - # generate essential Android Makefiles - file(WRITE ${ANDROID_APK_BUILD_DIR}/jni/Android.mk "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n") - file(WRITE ${ANDROID_APK_BUILD_DIR}/jni/Application.mk "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n") - - # create gdb.setup - get_directory_property(PROJECT_INCLUDES DIRECTORY ${PROJECT_SOURCE_DIR} INCLUDE_DIRECTORIES) - string(REGEX REPLACE ";" " " PROJECT_INCLUDES "${PROJECT_INCLUDES}") - file(WRITE ${ANDROID_APK_BUILD_DIR}/libs/${ANDROID_NDK_ABI_NAME}/gdb.setup "set solib-search-path ${GDB_SOLIB_PATH}\n") - file(APPEND ${ANDROID_APK_BUILD_DIR}/libs/${ANDROID_NDK_ABI_NAME}/gdb.setup "directory ${PROJECT_INCLUDES}\n") - - # copy lib to obj - add_custom_command(TARGET ${TARGET_NAME}-gdb PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${GDB_SOLIB_PATH}) - add_custom_command(TARGET ${TARGET_NAME}-gdb PRE_BUILD COMMAND cp ${TARGET_LOCATION} ${GDB_SOLIB_PATH}) - - # strip symbols - add_custom_command(TARGET ${TARGET_NAME}-gdb PRE_BUILD COMMAND ${CMAKE_STRIP} ${TARGET_LOCATION}) - endif () - - # use androiddeployqt to create the apk - add_custom_target(${TARGET_NAME}-apk - COMMAND ${ANDROID_DEPLOY_QT} --input "${TARGET_NAME}-deployment.json" --output "${ANDROID_APK_OUTPUT_DIR}" --android-platform android-${ANDROID_API_LEVEL} ${ANDROID_DEPLOY_QT_INSTALL} --verbose --deployment bundled "\\$(ARGS)" - DEPENDS ${TARGET_NAME} ${TARGET_NAME}-copy-res ${TARGET_NAME}-copy-assets ${TARGET_NAME}-copy-java ${TARGET_NAME}-copy-libs ${TARGET_NAME}-gdb - ) - - # rename the APK if the caller asked us to - if (ANDROID_APK_CUSTOM_NAME) - add_custom_command( - TARGET ${TARGET_NAME}-apk - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E rename "${ANDROID_APK_OUTPUT_DIR}/bin/QtApp-debug.apk" "${ANDROID_APK_OUTPUT_DIR}/bin/${ANDROID_APK_CUSTOM_NAME}" - ) - endif () -endmacro() \ No newline at end of file diff --git a/cmake/android/android.toolchain.cmake b/cmake/android/android.toolchain.cmake deleted file mode 100755 index 806cef6b18..0000000000 --- a/cmake/android/android.toolchain.cmake +++ /dev/null @@ -1,1725 +0,0 @@ -# Copyright (c) 2010-2011, Ethan Rublee -# Copyright (c) 2011-2014, Andrey Kamaev -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -# ------------------------------------------------------------------------------ -# Android CMake toolchain file, for use with the Android NDK r5-r10d -# Requires cmake 2.6.3 or newer (2.8.9 or newer is recommended). -# See home page: https://github.com/taka-no-me/android-cmake -# -# Usage Linux: -# $ export ANDROID_NDK=/absolute/path/to/the/android-ndk -# $ mkdir build && cd build -# $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .. -# $ make -j8 -# -# Usage Windows: -# You need native port of make to build your project. -# Android NDK r7 (and newer) already has make.exe on board. -# For older NDK you have to install it separately. -# For example, this one: http://gnuwin32.sourceforge.net/packages/make.htm -# -# $ SET ANDROID_NDK=C:\absolute\path\to\the\android-ndk -# $ mkdir build && cd build -# $ cmake.exe -G"MinGW Makefiles" -# -DCMAKE_TOOLCHAIN_FILE=path\to\the\android.toolchain.cmake -# -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%\prebuilt\windows\bin\make.exe" .. -# $ cmake.exe --build . -# -# -# Options (can be set as cmake parameters: -D=): -# ANDROID_NDK=/opt/android-ndk - path to the NDK root. -# Can be set as environment variable. Can be set only at first cmake run. -# -# ANDROID_ABI=armeabi-v7a - specifies the target Application Binary -# Interface (ABI). This option nearly matches to the APP_ABI variable -# used by ndk-build tool from Android NDK. -# -# Possible targets are: -# "armeabi" - ARMv5TE based CPU with software floating point operations -# "armeabi-v7a" - ARMv7 based devices with hardware FPU instructions -# this ABI target is used by default -# "armeabi-v7a with NEON" - same as armeabi-v7a, but -# sets NEON as floating-point unit -# "armeabi-v7a with VFPV3" - same as armeabi-v7a, but -# sets VFPV3 as floating-point unit (has 32 registers instead of 16) -# "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP -# "x86" - IA-32 instruction set -# "mips" - MIPS32 instruction set -# -# 64-bit ABIs for NDK r10 and newer: -# "arm64-v8a" - ARMv8 AArch64 instruction set -# "x86_64" - Intel64 instruction set (r1) -# "mips64" - MIPS64 instruction set (r6) -# -# ANDROID_NATIVE_API_LEVEL=android-8 - level of Android API compile for. -# Option is read-only when standalone toolchain is used. -# Note: building for "android-L" requires explicit configuration. -# -# ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 - the name of compiler -# toolchain to be used. The list of possible values depends on the NDK -# version. For NDK r10c the possible values are: -# -# * aarch64-linux-android-4.9 -# * aarch64-linux-android-clang3.4 -# * aarch64-linux-android-clang3.5 -# * arm-linux-androideabi-4.6 -# * arm-linux-androideabi-4.8 -# * arm-linux-androideabi-4.9 (default) -# * arm-linux-androideabi-clang3.4 -# * arm-linux-androideabi-clang3.5 -# * mips64el-linux-android-4.9 -# * mips64el-linux-android-clang3.4 -# * mips64el-linux-android-clang3.5 -# * mipsel-linux-android-4.6 -# * mipsel-linux-android-4.8 -# * mipsel-linux-android-4.9 -# * mipsel-linux-android-clang3.4 -# * mipsel-linux-android-clang3.5 -# * x86-4.6 -# * x86-4.8 -# * x86-4.9 -# * x86-clang3.4 -# * x86-clang3.5 -# * x86_64-4.9 -# * x86_64-clang3.4 -# * x86_64-clang3.5 -# -# ANDROID_FORCE_ARM_BUILD=OFF - set ON to generate 32-bit ARM instructions -# instead of Thumb. Is not available for "armeabi-v6 with VFP" -# (is forced to be ON) ABI. -# -# ANDROID_NO_UNDEFINED=ON - set ON to show all undefined symbols as linker -# errors even if they are not used. -# -# ANDROID_SO_UNDEFINED=OFF - set ON to allow undefined symbols in shared -# libraries. Automatically turned for NDK r5x and r6x due to GLESv2 -# problems. -# -# ANDROID_STL=gnustl_static - specify the runtime to use. -# -# Possible values are: -# none -> Do not configure the runtime. -# system -> Use the default minimal system C++ runtime library. -# Implies -fno-rtti -fno-exceptions. -# Is not available for standalone toolchain. -# system_re -> Use the default minimal system C++ runtime library. -# Implies -frtti -fexceptions. -# Is not available for standalone toolchain. -# gabi++_static -> Use the GAbi++ runtime as a static library. -# Implies -frtti -fno-exceptions. -# Available for NDK r7 and newer. -# Is not available for standalone toolchain. -# gabi++_shared -> Use the GAbi++ runtime as a shared library. -# Implies -frtti -fno-exceptions. -# Available for NDK r7 and newer. -# Is not available for standalone toolchain. -# stlport_static -> Use the STLport runtime as a static library. -# Implies -fno-rtti -fno-exceptions for NDK before r7. -# Implies -frtti -fno-exceptions for NDK r7 and newer. -# Is not available for standalone toolchain. -# stlport_shared -> Use the STLport runtime as a shared library. -# Implies -fno-rtti -fno-exceptions for NDK before r7. -# Implies -frtti -fno-exceptions for NDK r7 and newer. -# Is not available for standalone toolchain. -# gnustl_static -> Use the GNU STL as a static library. -# Implies -frtti -fexceptions. -# gnustl_shared -> Use the GNU STL as a shared library. -# Implies -frtti -fno-exceptions. -# Available for NDK r7b and newer. -# Silently degrades to gnustl_static if not available. -# c++_static -> Use the LLVM libc++ runtime as a static library. -# c++_shared -> Use the LLVM libc++ runtime as a shared library. -# -# ANDROID_STL_FORCE_FEATURES=ON - turn rtti and exceptions support based on -# chosen runtime. If disabled, then the user is responsible for settings -# these options. -# -# What?: -# android-cmake toolchain searches for NDK/toolchain in the following order: -# ANDROID_NDK - cmake parameter -# ANDROID_NDK - environment variable -# ANDROID_STANDALONE_TOOLCHAIN - cmake parameter -# ANDROID_STANDALONE_TOOLCHAIN - environment variable -# ANDROID_NDK - default locations -# ANDROID_STANDALONE_TOOLCHAIN - default locations -# -# Make sure to do the following in your scripts: -# SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}" ) -# SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}" ) -# The flags will be prepopulated with critical flags, so don't loose them. -# Also be aware that toolchain also sets configuration-specific compiler -# flags and linker flags. -# -# ANDROID and BUILD_ANDROID will be set to true, you may test any of these -# variables to make necessary Android-specific configuration changes. -# -# Also ARMEABI or ARMEABI_V7A or X86 or MIPS or ARM64_V8A or X86_64 or MIPS64 -# will be set true, mutually exclusive. NEON option will be set true -# if VFP is set to NEON. -# -# ------------------------------------------------------------------------------ - -cmake_minimum_required( VERSION 2.6.3 ) - -if( DEFINED CMAKE_CROSSCOMPILING ) - # subsequent toolchain loading is not really needed - return() -endif() - -if( CMAKE_TOOLCHAIN_FILE ) - # touch toolchain variable to suppress "unused variable" warning -endif() - -# inherit settings in recursive loads -get_property( _CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE ) -if( _CMAKE_IN_TRY_COMPILE ) - include( "${CMAKE_CURRENT_SOURCE_DIR}/../android.toolchain.config.cmake" OPTIONAL ) -endif() - -# this one is important -if( CMAKE_VERSION VERSION_GREATER "3.0.99" ) - set( CMAKE_SYSTEM_NAME Android ) -else() - set( CMAKE_SYSTEM_NAME Linux ) -endif() - -# this one not so much -set( CMAKE_SYSTEM_VERSION 1 ) - -# rpath makes low sense for Android -set( CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "" ) -set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." ) - -# NDK search paths -set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r10d -r10c -r10b -r10 -r9d -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) -if( NOT DEFINED ANDROID_NDK_SEARCH_PATHS ) - if( CMAKE_HOST_WIN32 ) - file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS ) - set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}" "$ENV{SystemDrive}/NVPACK" ) - else() - file( TO_CMAKE_PATH "$ENV{HOME}" ANDROID_NDK_SEARCH_PATHS ) - set( ANDROID_NDK_SEARCH_PATHS /opt "${ANDROID_NDK_SEARCH_PATHS}/NVPACK" ) - endif() -endif() -if( NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) - set( ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH /opt/android-toolchain ) -endif() - -# known ABIs -set( ANDROID_SUPPORTED_ABIS_arm "armeabi-v7a;armeabi;armeabi-v7a with NEON;armeabi-v7a with VFPV3;armeabi-v6 with VFP" ) -set( ANDROID_SUPPORTED_ABIS_arm64 "arm64-v8a" ) -set( ANDROID_SUPPORTED_ABIS_x86 "x86" ) -set( ANDROID_SUPPORTED_ABIS_x86_64 "x86_64" ) -set( ANDROID_SUPPORTED_ABIS_mips "mips" ) -set( ANDROID_SUPPORTED_ABIS_mips64 "mips64" ) - -# API level defaults -set( ANDROID_DEFAULT_NDK_API_LEVEL 8 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_arm64 21 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_x86 9 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_x86_64 21 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_mips 9 ) -set( ANDROID_DEFAULT_NDK_API_LEVEL_mips64 21 ) - - -macro( __LIST_FILTER listvar regex ) - if( ${listvar} ) - foreach( __val ${${listvar}} ) - if( __val MATCHES "${regex}" ) - list( REMOVE_ITEM ${listvar} "${__val}" ) - endif() - endforeach() - endif() -endmacro() - -macro( __INIT_VARIABLE var_name ) - set( __test_path 0 ) - foreach( __var ${ARGN} ) - if( __var STREQUAL "PATH" ) - set( __test_path 1 ) - break() - endif() - endforeach() - - if( __test_path AND NOT EXISTS "${${var_name}}" ) - unset( ${var_name} CACHE ) - endif() - - if( " ${${var_name}}" STREQUAL " " ) - set( __values 0 ) - foreach( __var ${ARGN} ) - if( __var STREQUAL "VALUES" ) - set( __values 1 ) - elseif( NOT __var STREQUAL "PATH" ) - if( __var MATCHES "^ENV_.*$" ) - string( REPLACE "ENV_" "" __var "${__var}" ) - set( __value "$ENV{${__var}}" ) - elseif( DEFINED ${__var} ) - set( __value "${${__var}}" ) - elseif( __values ) - set( __value "${__var}" ) - else() - set( __value "" ) - endif() - - if( NOT " ${__value}" STREQUAL " " AND (NOT __test_path OR EXISTS "${__value}") ) - set( ${var_name} "${__value}" ) - break() - endif() - endif() - endforeach() - unset( __value ) - unset( __values ) - endif() - - if( __test_path ) - file( TO_CMAKE_PATH "${${var_name}}" ${var_name} ) - endif() - unset( __test_path ) -endmacro() - -macro( __DETECT_NATIVE_API_LEVEL _var _path ) - set( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*.*$" ) - file( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" ) - if( NOT __apiFileContent ) - message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." ) - endif() - string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" ) - unset( __apiFileContent ) - unset( __ndkApiLevelRegex ) -endmacro() - -macro( __DETECT_TOOLCHAIN_MACHINE_NAME _var _root ) - if( EXISTS "${_root}" ) - file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) - __LIST_FILTER( __gccExePath "^[.].*" ) - list( LENGTH __gccExePath __gccExePathsCount ) - if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "Could not determine machine name for compiler from ${_root}" ) - set( ${_var} "" ) - else() - get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) - string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) - endif() - unset( __gccExePath ) - unset( __gccExePathsCount ) - unset( __gccExeName ) - else() - set( ${_var} "" ) - endif() -endmacro() - - -# fight against cygwin -set( ANDROID_FORBID_SYGWIN TRUE CACHE BOOL "Prevent cmake from working under cygwin and using cygwin tools") -mark_as_advanced( ANDROID_FORBID_SYGWIN ) -if( ANDROID_FORBID_SYGWIN ) - if( CYGWIN ) - message( FATAL_ERROR "Android NDK and android-cmake toolchain are not welcome Cygwin. It is unlikely that this cmake toolchain will work under cygwin. But if you want to try then you can set cmake variable ANDROID_FORBID_SYGWIN to FALSE and rerun cmake." ) - endif() - - if( CMAKE_HOST_WIN32 ) - # remove cygwin from PATH - set( __new_path "$ENV{PATH}") - __LIST_FILTER( __new_path "cygwin" ) - set(ENV{PATH} "${__new_path}") - unset(__new_path) - endif() -endif() - - -# detect current host platform -if( NOT DEFINED ANDROID_NDK_HOST_X64 AND (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64" OR CMAKE_HOST_APPLE) ) - set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" ) - mark_as_advanced( ANDROID_NDK_HOST_X64 ) -endif() - -set( TOOL_OS_SUFFIX "" ) -if( CMAKE_HOST_APPLE ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "darwin-x86_64" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME2 "darwin-x86" ) -elseif( CMAKE_HOST_WIN32 ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "windows-x86_64" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME2 "windows" ) - set( TOOL_OS_SUFFIX ".exe" ) -elseif( CMAKE_HOST_UNIX ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "linux-x86_64" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME2 "linux-x86" ) -else() - message( FATAL_ERROR "Cross-compilation on your platform is not supported by this cmake toolchain" ) -endif() - -if( NOT ANDROID_NDK_HOST_X64 ) - set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) -endif() - -# see if we have path to Android NDK -if( NOT ANDROID_NDK AND NOT ANDROID_STANDALONE_TOOLCHAIN ) - __INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) -endif() -if( NOT ANDROID_NDK ) - # see if we have path to Android standalone toolchain - __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN ) - - if( NOT ANDROID_STANDALONE_TOOLCHAIN ) - #try to find Android NDK in one of the the default locations - set( __ndkSearchPaths ) - foreach( __ndkSearchPath ${ANDROID_NDK_SEARCH_PATHS} ) - foreach( suffix ${ANDROID_SUPPORTED_NDK_VERSIONS} ) - list( APPEND __ndkSearchPaths "${__ndkSearchPath}/android-ndk${suffix}" ) - endforeach() - endforeach() - __INIT_VARIABLE( ANDROID_NDK PATH VALUES ${__ndkSearchPaths} ) - unset( __ndkSearchPaths ) - - if( ANDROID_NDK ) - message( STATUS "Using default path for Android NDK: ${ANDROID_NDK}" ) - message( STATUS " If you prefer to use a different location, please define a cmake or environment variable: ANDROID_NDK" ) - else() - #try to find Android standalone toolchain in one of the the default locations - __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) - - if( ANDROID_STANDALONE_TOOLCHAIN ) - message( STATUS "Using default path for standalone toolchain ${ANDROID_STANDALONE_TOOLCHAIN}" ) - message( STATUS " If you prefer to use a different location, please define the variable: ANDROID_STANDALONE_TOOLCHAIN" ) - endif( ANDROID_STANDALONE_TOOLCHAIN ) - endif( ANDROID_NDK ) - endif( NOT ANDROID_STANDALONE_TOOLCHAIN ) -endif( NOT ANDROID_NDK ) - -# remember found paths -if( ANDROID_NDK ) - get_filename_component( ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE ) - set( ANDROID_NDK "${ANDROID_NDK}" CACHE INTERNAL "Path of the Android NDK" FORCE ) - set( BUILD_WITH_ANDROID_NDK True ) - if( EXISTS "${ANDROID_NDK}/RELEASE.TXT" ) - file( STRINGS "${ANDROID_NDK}/RELEASE.TXT" ANDROID_NDK_RELEASE_FULL LIMIT_COUNT 1 REGEX "r[0-9]+[a-z]?" ) - string( REGEX MATCH "r([0-9]+)([a-z]?)" ANDROID_NDK_RELEASE "${ANDROID_NDK_RELEASE_FULL}" ) - else() - set( ANDROID_NDK_RELEASE "r1x" ) - set( ANDROID_NDK_RELEASE_FULL "unreleased" ) - endif() - string( REGEX REPLACE "r([0-9]+)([a-z]?)" "\\1*1000" ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE}" ) - string( FIND " abcdefghijklmnopqastuvwxyz" "${CMAKE_MATCH_2}" __ndkReleaseLetterNum ) - math( EXPR ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE_NUM}+${__ndkReleaseLetterNum}" ) -elseif( ANDROID_STANDALONE_TOOLCHAIN ) - get_filename_component( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" ABSOLUTE ) - # try to detect change - if( CMAKE_AR ) - string( LENGTH "${ANDROID_STANDALONE_TOOLCHAIN}" __length ) - string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidStandaloneToolchainPreviousPath ) - if( NOT __androidStandaloneToolchainPreviousPath STREQUAL ANDROID_STANDALONE_TOOLCHAIN ) - message( FATAL_ERROR "It is not possible to change path to the Android standalone toolchain on subsequent run." ) - endif() - unset( __androidStandaloneToolchainPreviousPath ) - unset( __length ) - endif() - set( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" CACHE INTERNAL "Path of the Android standalone toolchain" FORCE ) - set( BUILD_WITH_STANDALONE_TOOLCHAIN True ) -else() - list(GET ANDROID_NDK_SEARCH_PATHS 0 ANDROID_NDK_SEARCH_PATH) - message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolchain. - You should either set an environment variable: - export ANDROID_NDK=~/my-android-ndk - or - export ANDROID_STANDALONE_TOOLCHAIN=~/my-android-toolchain - or put the toolchain or NDK in the default path: - sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH}/android-ndk - sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" ) -endif() - -# android NDK layout -if( BUILD_WITH_ANDROID_NDK ) - if( NOT DEFINED ANDROID_NDK_LAYOUT ) - # try to automatically detect the layout - if( EXISTS "${ANDROID_NDK}/RELEASE.TXT") - set( ANDROID_NDK_LAYOUT "RELEASE" ) - elseif( EXISTS "${ANDROID_NDK}/../../linux-x86/toolchain/" ) - set( ANDROID_NDK_LAYOUT "LINARO" ) - elseif( EXISTS "${ANDROID_NDK}/../../gcc/" ) - set( ANDROID_NDK_LAYOUT "ANDROID" ) - endif() - endif() - set( ANDROID_NDK_LAYOUT "${ANDROID_NDK_LAYOUT}" CACHE STRING "The inner layout of NDK" ) - mark_as_advanced( ANDROID_NDK_LAYOUT ) - if( ANDROID_NDK_LAYOUT STREQUAL "LINARO" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment - set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../${ANDROID_NDK_HOST_SYSTEM_NAME}/toolchain" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) - elseif( ANDROID_NDK_LAYOUT STREQUAL "ANDROID" ) - set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment - set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../gcc/${ANDROID_NDK_HOST_SYSTEM_NAME}/arm" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) - else() # ANDROID_NDK_LAYOUT STREQUAL "RELEASE" - set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/toolchains" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME2}" ) - endif() - get_filename_component( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK_TOOLCHAINS_PATH}" ABSOLUTE ) - - # try to detect change of NDK - if( CMAKE_AR ) - string( LENGTH "${ANDROID_NDK_TOOLCHAINS_PATH}" __length ) - string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidNdkPreviousPath ) - if( NOT __androidNdkPreviousPath STREQUAL ANDROID_NDK_TOOLCHAINS_PATH ) - message( FATAL_ERROR "It is not possible to change the path to the NDK on subsequent CMake run. You must remove all generated files from your build folder first. - " ) - endif() - unset( __androidNdkPreviousPath ) - unset( __length ) - endif() -endif() - - -# get all the details about standalone toolchain -if( BUILD_WITH_STANDALONE_TOOLCHAIN ) - __DETECT_NATIVE_API_LEVEL( ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include/android/api-level.h" ) - set( ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) - set( __availableToolchains "standalone" ) - __DETECT_TOOLCHAIN_MACHINE_NAME( __availableToolchainMachines "${ANDROID_STANDALONE_TOOLCHAIN}" ) - if( NOT __availableToolchainMachines ) - message( FATAL_ERROR "Could not determine machine name of your toolchain. Probably your Android standalone toolchain is broken." ) - endif() - if( __availableToolchainMachines MATCHES x86_64 ) - set( __availableToolchainArchs "x86_64" ) - elseif( __availableToolchainMachines MATCHES i686 ) - set( __availableToolchainArchs "x86" ) - elseif( __availableToolchainMachines MATCHES aarch64 ) - set( __availableToolchainArchs "arm64" ) - elseif( __availableToolchainMachines MATCHES arm ) - set( __availableToolchainArchs "arm" ) - elseif( __availableToolchainMachines MATCHES mips64el ) - set( __availableToolchainArchs "mips64" ) - elseif( __availableToolchainMachines MATCHES mipsel ) - set( __availableToolchainArchs "mips" ) - endif() - execute_process( COMMAND "${ANDROID_STANDALONE_TOOLCHAIN}/bin/${__availableToolchainMachines}-gcc${TOOL_OS_SUFFIX}" -dumpversion - OUTPUT_VARIABLE __availableToolchainCompilerVersions OUTPUT_STRIP_TRAILING_WHITESPACE ) - string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9]+)?" __availableToolchainCompilerVersions "${__availableToolchainCompilerVersions}" ) - if( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/bin/clang${TOOL_OS_SUFFIX}" ) - list( APPEND __availableToolchains "standalone-clang" ) - list( APPEND __availableToolchainMachines ${__availableToolchainMachines} ) - list( APPEND __availableToolchainArchs ${__availableToolchainArchs} ) - list( APPEND __availableToolchainCompilerVersions ${__availableToolchainCompilerVersions} ) - endif() -endif() - -macro( __GLOB_NDK_TOOLCHAINS __availableToolchainsVar __availableToolchainsLst __toolchain_subpath ) - foreach( __toolchain ${${__availableToolchainsLst}} ) - if( "${__toolchain}" MATCHES "-clang3[.][0-9]$" AND NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}${__toolchain_subpath}" ) - SET( __toolchainVersionRegex "^TOOLCHAIN_VERSION[\t ]+:=[\t ]+(.*)$" ) - FILE( STRINGS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}/setup.mk" __toolchainVersionStr REGEX "${__toolchainVersionRegex}" ) - if( __toolchainVersionStr ) - string( REGEX REPLACE "${__toolchainVersionRegex}" "\\1" __toolchainVersionStr "${__toolchainVersionStr}" ) - string( REGEX REPLACE "-clang3[.][0-9]$" "-${__toolchainVersionStr}" __gcc_toolchain "${__toolchain}" ) - else() - string( REGEX REPLACE "-clang3[.][0-9]$" "-4.6" __gcc_toolchain "${__toolchain}" ) - endif() - unset( __toolchainVersionStr ) - unset( __toolchainVersionRegex ) - else() - set( __gcc_toolchain "${__toolchain}" ) - endif() - __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK_TOOLCHAINS_PATH}/${__gcc_toolchain}${__toolchain_subpath}" ) - if( __machine ) - string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9x]+)?$" __version "${__gcc_toolchain}" ) - if( __machine MATCHES x86_64 ) - set( __arch "x86_64" ) - elseif( __machine MATCHES i686 ) - set( __arch "x86" ) - elseif( __machine MATCHES aarch64 ) - set( __arch "arm64" ) - elseif( __machine MATCHES arm ) - set( __arch "arm" ) - elseif( __machine MATCHES mips64el ) - set( __arch "mips64" ) - elseif( __machine MATCHES mipsel ) - set( __arch "mips" ) - else() - set( __arch "" ) - endif() - #message("machine: !${__machine}!\narch: !${__arch}!\nversion: !${__version}!\ntoolchain: !${__toolchain}!\n") - if (__arch) - list( APPEND __availableToolchainMachines "${__machine}" ) - list( APPEND __availableToolchainArchs "${__arch}" ) - list( APPEND __availableToolchainCompilerVersions "${__version}" ) - list( APPEND ${__availableToolchainsVar} "${__toolchain}" ) - endif() - endif() - unset( __gcc_toolchain ) - endforeach() -endmacro() - -# get all the details about NDK -if( BUILD_WITH_ANDROID_NDK ) - file( GLOB ANDROID_SUPPORTED_NATIVE_API_LEVELS RELATIVE "${ANDROID_NDK}/platforms" "${ANDROID_NDK}/platforms/android-*" ) - string( REPLACE "android-" "" ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_SUPPORTED_NATIVE_API_LEVELS}" ) - set( __availableToolchains "" ) - set( __availableToolchainMachines "" ) - set( __availableToolchainArchs "" ) - set( __availableToolchainCompilerVersions "" ) - if( ANDROID_TOOLCHAIN_NAME AND EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_TOOLCHAIN_NAME}/" ) - # do not go through all toolchains if we know the name - set( __availableToolchainsLst "${ANDROID_TOOLCHAIN_NAME}" ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) - if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) - if( __availableToolchains ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) - endif() - endif() - endif() - if( NOT __availableToolchains ) - file( GLOB __availableToolchainsLst RELATIVE "${ANDROID_NDK_TOOLCHAINS_PATH}" "${ANDROID_NDK_TOOLCHAINS_PATH}/*" ) - if( __availableToolchainsLst ) - list(SORT __availableToolchainsLst) # we need clang to go after gcc - endif() - __LIST_FILTER( __availableToolchainsLst "^[.]" ) - __LIST_FILTER( __availableToolchainsLst "llvm" ) - __LIST_FILTER( __availableToolchainsLst "renderscript" ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) - if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) - __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) - if( __availableToolchains ) - set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) - endif() - endif() - endif() - if( NOT __availableToolchains ) - message( FATAL_ERROR "Could not find any working toolchain in the NDK. Probably your Android NDK is broken." ) - endif() -endif() - -# build list of available ABIs -set( ANDROID_SUPPORTED_ABIS "" ) -set( __uniqToolchainArchNames ${__availableToolchainArchs} ) -list( REMOVE_DUPLICATES __uniqToolchainArchNames ) -list( SORT __uniqToolchainArchNames ) -foreach( __arch ${__uniqToolchainArchNames} ) - list( APPEND ANDROID_SUPPORTED_ABIS ${ANDROID_SUPPORTED_ABIS_${__arch}} ) -endforeach() -unset( __uniqToolchainArchNames ) -if( NOT ANDROID_SUPPORTED_ABIS ) - message( FATAL_ERROR "No one of known Android ABIs is supported by this cmake toolchain." ) -endif() - -# choose target ABI -__INIT_VARIABLE( ANDROID_ABI VALUES ${ANDROID_SUPPORTED_ABIS} ) -# verify that target ABI is supported -list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx ) -if( __androidAbiIdx EQUAL -1 ) - string( REPLACE ";" "\", \"" PRINTABLE_ANDROID_SUPPORTED_ABIS "${ANDROID_SUPPORTED_ABIS}" ) - message( FATAL_ERROR "Specified ANDROID_ABI = \"${ANDROID_ABI}\" is not supported by this cmake toolchain or your NDK/toolchain. - Supported values are: \"${PRINTABLE_ANDROID_SUPPORTED_ABIS}\" - " ) -endif() -unset( __androidAbiIdx ) - -# set target ABI options -if( ANDROID_ABI STREQUAL "x86" ) - set( X86 true ) - set( ANDROID_NDK_ABI_NAME "x86" ) - set( ANDROID_ARCH_NAME "x86" ) - set( ANDROID_LLVM_TRIPLE "i686-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "i686" ) -elseif( ANDROID_ABI STREQUAL "x86_64" ) - set( X86 true ) - set( X86_64 true ) - set( ANDROID_NDK_ABI_NAME "x86_64" ) - set( ANDROID_ARCH_NAME "x86_64" ) - set( CMAKE_SYSTEM_PROCESSOR "x86_64" ) - set( ANDROID_LLVM_TRIPLE "x86_64-none-linux-android" ) -elseif( ANDROID_ABI STREQUAL "mips64" ) - set( MIPS64 true ) - set( ANDROID_NDK_ABI_NAME "mips64" ) - set( ANDROID_ARCH_NAME "mips64" ) - set( ANDROID_LLVM_TRIPLE "mips64el-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "mips64" ) -elseif( ANDROID_ABI STREQUAL "mips" ) - set( MIPS true ) - set( ANDROID_NDK_ABI_NAME "mips" ) - set( ANDROID_ARCH_NAME "mips" ) - set( ANDROID_LLVM_TRIPLE "mipsel-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "mips" ) -elseif( ANDROID_ABI STREQUAL "arm64-v8a" ) - set( ARM64_V8A true ) - set( ANDROID_NDK_ABI_NAME "arm64-v8a" ) - set( ANDROID_ARCH_NAME "arm64" ) - set( ANDROID_LLVM_TRIPLE "aarch64-none-linux-android" ) - set( CMAKE_SYSTEM_PROCESSOR "aarch64" ) - set( VFPV3 true ) - set( NEON true ) -elseif( ANDROID_ABI STREQUAL "armeabi" ) - set( ARMEABI true ) - set( ANDROID_NDK_ABI_NAME "armeabi" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv5te" ) -elseif( ANDROID_ABI STREQUAL "armeabi-v6 with VFP" ) - set( ARMEABI_V6 true ) - set( ANDROID_NDK_ABI_NAME "armeabi" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv6" ) - # need always fallback to older platform - set( ARMEABI true ) -elseif( ANDROID_ABI STREQUAL "armeabi-v7a") - set( ARMEABI_V7A true ) - set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) -elseif( ANDROID_ABI STREQUAL "armeabi-v7a with VFPV3" ) - set( ARMEABI_V7A true ) - set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) - set( VFPV3 true ) -elseif( ANDROID_ABI STREQUAL "armeabi-v7a with NEON" ) - set( ARMEABI_V7A true ) - set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) - set( ANDROID_ARCH_NAME "arm" ) - set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) - set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) - set( VFPV3 true ) - set( NEON true ) -else() - message( SEND_ERROR "Unknown ANDROID_ABI=\"${ANDROID_ABI}\" is specified." ) -endif() - -if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" ) - # really dirty hack - # it is not possible to change CMAKE_SYSTEM_PROCESSOR after the first run... - file( APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" "SET(CMAKE_SYSTEM_PROCESSOR \"${CMAKE_SYSTEM_PROCESSOR}\")\n" ) -endif() - -if( ANDROID_ARCH_NAME STREQUAL "arm" AND NOT ARMEABI_V6 ) - __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD VALUES OFF ) - set( ANDROID_FORCE_ARM_BUILD ${ANDROID_FORCE_ARM_BUILD} CACHE BOOL "Use 32-bit ARM instructions instead of Thumb-1" FORCE ) - mark_as_advanced( ANDROID_FORCE_ARM_BUILD ) -else() - unset( ANDROID_FORCE_ARM_BUILD CACHE ) -endif() - -# choose toolchain -if( ANDROID_TOOLCHAIN_NAME ) - list( FIND __availableToolchains "${ANDROID_TOOLCHAIN_NAME}" __toolchainIdx ) - if( __toolchainIdx EQUAL -1 ) - list( SORT __availableToolchains ) - string( REPLACE ";" "\n * " toolchains_list "${__availableToolchains}" ) - set( toolchains_list " * ${toolchains_list}") - message( FATAL_ERROR "Specified toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is missing in your NDK or broken. Please verify that your NDK is working or select another compiler toolchain. -To configure the toolchain set CMake variable ANDROID_TOOLCHAIN_NAME to one of the following values:\n${toolchains_list}\n" ) - endif() - list( GET __availableToolchainArchs ${__toolchainIdx} __toolchainArch ) - if( NOT __toolchainArch STREQUAL ANDROID_ARCH_NAME ) - message( SEND_ERROR "Selected toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is not able to compile binaries for the \"${ANDROID_ARCH_NAME}\" platform." ) - endif() -else() - set( __toolchainIdx -1 ) - set( __applicableToolchains "" ) - set( __toolchainMaxVersion "0.0.0" ) - list( LENGTH __availableToolchains __availableToolchainsCount ) - math( EXPR __availableToolchainsCount "${__availableToolchainsCount}-1" ) - foreach( __idx RANGE ${__availableToolchainsCount} ) - list( GET __availableToolchainArchs ${__idx} __toolchainArch ) - if( __toolchainArch STREQUAL ANDROID_ARCH_NAME ) - list( GET __availableToolchainCompilerVersions ${__idx} __toolchainVersion ) - string( REPLACE "x" "99" __toolchainVersion "${__toolchainVersion}") - if( __toolchainVersion VERSION_GREATER __toolchainMaxVersion ) - set( __toolchainMaxVersion "${__toolchainVersion}" ) - set( __toolchainIdx ${__idx} ) - endif() - endif() - endforeach() - unset( __availableToolchainsCount ) - unset( __toolchainMaxVersion ) - unset( __toolchainVersion ) -endif() -unset( __toolchainArch ) -if( __toolchainIdx EQUAL -1 ) - message( FATAL_ERROR "No one of available compiler toolchains is able to compile for ${ANDROID_ARCH_NAME} platform." ) -endif() -list( GET __availableToolchains ${__toolchainIdx} ANDROID_TOOLCHAIN_NAME ) -list( GET __availableToolchainMachines ${__toolchainIdx} ANDROID_TOOLCHAIN_MACHINE_NAME ) -list( GET __availableToolchainCompilerVersions ${__toolchainIdx} ANDROID_COMPILER_VERSION ) - -unset( __toolchainIdx ) -unset( __availableToolchains ) -unset( __availableToolchainMachines ) -unset( __availableToolchainArchs ) -unset( __availableToolchainCompilerVersions ) - -# choose native API level -__INIT_VARIABLE( ANDROID_NATIVE_API_LEVEL ENV_ANDROID_NATIVE_API_LEVEL ANDROID_API_LEVEL ENV_ANDROID_API_LEVEL ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME} ANDROID_DEFAULT_NDK_API_LEVEL ) -string( REPLACE "android-" "" ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" ) -string( STRIP "${ANDROID_NATIVE_API_LEVEL}" ANDROID_NATIVE_API_LEVEL ) -# adjust API level -set( __real_api_level ${ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME}} ) -foreach( __level ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) - if( (__level LESS ANDROID_NATIVE_API_LEVEL OR __level STREQUAL ANDROID_NATIVE_API_LEVEL) AND NOT __level LESS __real_api_level ) - set( __real_api_level ${__level} ) - endif() -endforeach() -if( __real_api_level AND NOT ANDROID_NATIVE_API_LEVEL STREQUAL __real_api_level ) - message( STATUS "Adjusting Android API level 'android-${ANDROID_NATIVE_API_LEVEL}' to 'android-${__real_api_level}'") - set( ANDROID_NATIVE_API_LEVEL ${__real_api_level} ) -endif() -unset(__real_api_level) -# validate -list( FIND ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_NATIVE_API_LEVEL}" __levelIdx ) -if( __levelIdx EQUAL -1 ) - message( SEND_ERROR "Specified Android native API level 'android-${ANDROID_NATIVE_API_LEVEL}' is not supported by your NDK/toolchain." ) -else() - if( BUILD_WITH_ANDROID_NDK ) - __DETECT_NATIVE_API_LEVEL( __realApiLevel "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include/android/api-level.h" ) - if( NOT __realApiLevel EQUAL ANDROID_NATIVE_API_LEVEL AND NOT __realApiLevel GREATER 9000 ) - message( SEND_ERROR "Specified Android API level (${ANDROID_NATIVE_API_LEVEL}) does not match to the level found (${__realApiLevel}). Probably your copy of NDK is broken." ) - endif() - unset( __realApiLevel ) - endif() - set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE ) - set( CMAKE_ANDROID_API ${ANDROID_NATIVE_API_LEVEL} ) - if( CMAKE_VERSION VERSION_GREATER "2.8" ) - list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS ) - set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) - endif() -endif() -unset( __levelIdx ) - - -# remember target ABI -set( ANDROID_ABI "${ANDROID_ABI}" CACHE STRING "The target ABI for Android. If arm, then armeabi-v7a is recommended for hardware floating point." FORCE ) -if( CMAKE_VERSION VERSION_GREATER "2.8" ) - list( SORT ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME} ) - set_property( CACHE ANDROID_ABI PROPERTY STRINGS ${ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME}} ) -endif() - - -# runtime choice (STL, rtti, exceptions) -if( NOT ANDROID_STL ) - set( ANDROID_STL gnustl_static ) -endif() -set( ANDROID_STL "${ANDROID_STL}" CACHE STRING "C++ runtime" ) -set( ANDROID_STL_FORCE_FEATURES ON CACHE BOOL "automatically configure rtti and exceptions support based on C++ runtime" ) -mark_as_advanced( ANDROID_STL ANDROID_STL_FORCE_FEATURES ) - -if( BUILD_WITH_ANDROID_NDK ) - if( NOT "${ANDROID_STL}" MATCHES "^(none|system|system_re|gabi\\+\\+_static|gabi\\+\\+_shared|stlport_static|stlport_shared|gnustl_static|gnustl_shared|c\\+\\+_static|c\\+\\+_shared)$") - message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". -The possible values are: - none -> Do not configure the runtime. - system -> Use the default minimal system C++ runtime library. - system_re -> Same as system but with rtti and exceptions. - gabi++_static -> Use the GAbi++ runtime as a static library. - gabi++_shared -> Use the GAbi++ runtime as a shared library. - stlport_static -> Use the STLport runtime as a static library. - stlport_shared -> Use the STLport runtime as a shared library. - gnustl_static -> (default) Use the GNU STL as a static library. - gnustl_shared -> Use the GNU STL as a shared library. - c++_static -> Use the LLVM libc++ runtime as a static library. - c++_shared -> Use the LLVM libc++ runtime as a shared library. -" ) - endif() -elseif( BUILD_WITH_STANDALONE_TOOLCHAIN ) - if( NOT "${ANDROID_STL}" MATCHES "^(none|gnustl_static|gnustl_shared)$") - message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". -The possible values are: - none -> Do not configure the runtime. - gnustl_static -> (default) Use the GNU STL as a static library. - gnustl_shared -> Use the GNU STL as a shared library. -" ) - endif() -endif() - -unset( ANDROID_RTTI ) -unset( ANDROID_EXCEPTIONS ) -unset( ANDROID_STL_INCLUDE_DIRS ) -unset( __libstl ) -unset( __libsupcxx ) - -if( NOT _CMAKE_IN_TRY_COMPILE AND ANDROID_NDK_RELEASE STREQUAL "r7b" AND ARMEABI_V7A AND NOT VFPV3 AND ANDROID_STL MATCHES "gnustl" ) - message( WARNING "The GNU STL armeabi-v7a binaries from NDK r7b can crash non-NEON devices. The files provided with NDK r7b were not configured properly, resulting in crashes on Tegra2-based devices and others when trying to use certain floating-point functions (e.g., cosf, sinf, expf). -You are strongly recommended to switch to another NDK release. -" ) -endif() - -if( NOT _CMAKE_IN_TRY_COMPILE AND X86 AND ANDROID_STL MATCHES "gnustl" AND ANDROID_NDK_RELEASE STREQUAL "r6" ) - message( WARNING "The x86 system header file from NDK r6 has incorrect definition for ptrdiff_t. You are recommended to upgrade to a newer NDK release or manually patch the header: -See https://android.googlesource.com/platform/development.git f907f4f9d4e56ccc8093df6fee54454b8bcab6c2 - diff --git a/ndk/platforms/android-9/arch-x86/include/machine/_types.h b/ndk/platforms/android-9/arch-x86/include/machine/_types.h - index 5e28c64..65892a1 100644 - --- a/ndk/platforms/android-9/arch-x86/include/machine/_types.h - +++ b/ndk/platforms/android-9/arch-x86/include/machine/_types.h - @@ -51,7 +51,11 @@ typedef long int ssize_t; - #endif - #ifndef _PTRDIFF_T - #define _PTRDIFF_T - -typedef long ptrdiff_t; - +# ifdef __ANDROID__ - + typedef int ptrdiff_t; - +# else - + typedef long ptrdiff_t; - +# endif - #endif -" ) -endif() - - -# setup paths and STL for standalone toolchain -if( BUILD_WITH_STANDALONE_TOOLCHAIN ) - set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) - set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) - set( ANDROID_SYSROOT "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot" ) - - if( NOT ANDROID_STL STREQUAL "none" ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/include/c++/${ANDROID_COMPILER_VERSION}" ) - if( NOT EXISTS "${ANDROID_STL_INCLUDE_DIRS}" ) - # old location ( pre r8c ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}" ) - endif() - if( ARMEABI_V7A AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/bits" ) - list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}" ) - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb/bits" ) - list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb" ) - else() - list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" ) - endif() - # always search static GNU STL to get the location of libsupc++.a - if( ARMEABI_V7A AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb" ) - elseif( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}" ) - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb" ) - elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libstdc++.a" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib" ) - endif() - if( __libstl ) - set( __libsupcxx "${__libstl}/libsupc++.a" ) - set( __libstl "${__libstl}/libstdc++.a" ) - endif() - if( NOT EXISTS "${__libsupcxx}" ) - message( FATAL_ERROR "The required libstdsupc++.a is missing in your standalone toolchain. - Usually it happens because of bug in make-standalone-toolchain.sh script from NDK r7, r7b and r7c. - You need to either upgrade to newer NDK or manually copy - $ANDROID_NDK/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a - to - ${__libsupcxx} - " ) - endif() - if( ANDROID_STL STREQUAL "gnustl_shared" ) - if( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) - elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) - set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) - endif() - endif() - endif() -endif() - -# clang -if( "${ANDROID_TOOLCHAIN_NAME}" STREQUAL "standalone-clang" ) - set( ANDROID_COMPILER_IS_CLANG 1 ) - execute_process( COMMAND "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/clang${TOOL_OS_SUFFIX}" --version OUTPUT_VARIABLE ANDROID_CLANG_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) - string( REGEX MATCH "[0-9]+[.][0-9]+" ANDROID_CLANG_VERSION "${ANDROID_CLANG_VERSION}") -elseif( "${ANDROID_TOOLCHAIN_NAME}" MATCHES "-clang3[.][0-9]?$" ) - string( REGEX MATCH "3[.][0-9]$" ANDROID_CLANG_VERSION "${ANDROID_TOOLCHAIN_NAME}") - string( REGEX REPLACE "-clang${ANDROID_CLANG_VERSION}$" "-${ANDROID_COMPILER_VERSION}" ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) - if( NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}/bin/clang${TOOL_OS_SUFFIX}" ) - message( FATAL_ERROR "Could not find the Clang compiler driver" ) - endif() - set( ANDROID_COMPILER_IS_CLANG 1 ) - set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) -else() - set( ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) - unset( ANDROID_COMPILER_IS_CLANG CACHE ) -endif() - -string( REPLACE "." "" _clang_name "clang${ANDROID_CLANG_VERSION}" ) -if( NOT EXISTS "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" ) - set( _clang_name "clang" ) -endif() - - -# setup paths and STL for NDK -if( BUILD_WITH_ANDROID_NDK ) - set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) - set( ANDROID_SYSROOT "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}" ) - - if( ANDROID_STL STREQUAL "none" ) - # do nothing - elseif( ANDROID_STL STREQUAL "system" ) - set( ANDROID_RTTI OFF ) - set( ANDROID_EXCEPTIONS OFF ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) - elseif( ANDROID_STL STREQUAL "system_re" ) - set( ANDROID_RTTI ON ) - set( ANDROID_EXCEPTIONS ON ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) - elseif( ANDROID_STL MATCHES "gabi" ) - if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - message( FATAL_ERROR "gabi++ is not available in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.") - endif() - set( ANDROID_RTTI ON ) - set( ANDROID_EXCEPTIONS OFF ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/gabi++/include" ) - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gabi++/libs/${ANDROID_NDK_ABI_NAME}/libgabi++_static.a" ) - elseif( ANDROID_STL MATCHES "stlport" ) - if( NOT ANDROID_NDK_RELEASE_NUM LESS 8004 ) # before r8d - set( ANDROID_EXCEPTIONS ON ) - else() - set( ANDROID_EXCEPTIONS OFF ) - endif() - if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - set( ANDROID_RTTI OFF ) - else() - set( ANDROID_RTTI ON ) - endif() - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/stlport/stlport" ) - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/stlport/libs/${ANDROID_NDK_ABI_NAME}/libstlport_static.a" ) - elseif( ANDROID_STL MATCHES "gnustl" ) - set( ANDROID_EXCEPTIONS ON ) - set( ANDROID_RTTI ON ) - if( EXISTS "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) - if( ARMEABI_V7A AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.7" AND ANDROID_NDK_RELEASE STREQUAL "r8d" ) - # gnustl binary for 4.7 compiler is buggy :( - # TODO: look for right fix - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.6" ) - else() - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) - endif() - else() - set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++" ) - endif() - set( ANDROID_STL_INCLUDE_DIRS "${__libstl}/include" "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/include" "${__libstl}/include/backward" ) - if( EXISTS "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) - set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) - else() - set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libstdc++.a" ) - endif() - elseif( ANDROID_STL MATCHES "c\\+\\+_shared" OR ANDROID_STL MATCHES "c\\+\\+_static" ) - set( ANDROID_EXCEPTIONS ON ) - set( ANDROID_RTTI ON ) - set( ANDROID_CXX_ROOT "${ANDROID_NDK}/sources/cxx-stl/" ) - set( ANDROID_LLVM_ROOT "${ANDROID_CXX_ROOT}/llvm-libc++" ) - - if( X86 ) - set( ANDROID_ABI_INCLUDE_DIRS "${ANDROID_CXX_ROOT}/gabi++/include" ) - else() - set( ANDROID_ABI_INCLUDE_DIRS "${ANDROID_CXX_ROOT}/llvm-libc++abi/include" ) - endif() - - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_LLVM_ROOT}/libcxx/include" "${ANDROID_ABI_INCLUDE_DIRS}" ) - - # android support sfiles - include_directories ( SYSTEM ${ANDROID_NDK}/sources/android/support/include ) - - if(ANDROID_STL MATCHES "c\\+\\+_shared") - set ( LLVM_LIBRARY_NAME "libc++_shared.so") - else() - set ( LLVM_LIBRARY_NAME "libc++_static.a" ) - endif () - - if( EXISTS "${ANDROID_LLVM_ROOT}/libs/${ANDROID_NDK_ABI_NAME}/${LLVM_LIBRARY_NAME}" ) - set( __libstl "${ANDROID_LLVM_ROOT}/libs/${ANDROID_NDK_ABI_NAME}/${LLVM_LIBRARY_NAME}" ) - else() - message( FATAL_ERROR "Could not find libc++ library" ) - endif() - else() - message( FATAL_ERROR "Unknown runtime: ${ANDROID_STL}" ) - endif() - # find libsupc++.a - rtti & exceptions - if( ANDROID_STL STREQUAL "system_re" OR ANDROID_STL MATCHES "gnustl" ) - set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r8b or newer - if( NOT EXISTS "${__libsupcxx}" ) - set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r7-r8 - endif() - if( NOT EXISTS "${__libsupcxx}" ) # before r7 - if( ARMEABI_V7A ) - if( ANDROID_FORCE_ARM_BUILD ) - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libsupc++.a" ) - else() - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" ) - endif() - elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD ) - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libsupc++.a" ) - else() - set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" ) - endif() - endif() - if( NOT EXISTS "${__libsupcxx}") - message( ERROR "Could not find libsupc++.a for a chosen platform. Either your NDK is not supported or is broken.") - endif() - endif() -endif() - - -# case of shared STL linkage -if( ANDROID_STL MATCHES "shared" AND DEFINED __libstl ) - string( REPLACE "_static.a" "_shared.so" __libstl "${__libstl}" ) - # TODO: check if .so file exists before the renaming -endif() - - -# ccache support -__INIT_VARIABLE( _ndk_ccache NDK_CCACHE ENV_NDK_CCACHE ) -if( _ndk_ccache ) - if( DEFINED NDK_CCACHE AND NOT EXISTS NDK_CCACHE ) - unset( NDK_CCACHE CACHE ) - endif() - find_program( NDK_CCACHE "${_ndk_ccache}" DOC "The path to ccache binary") -else() - unset( NDK_CCACHE CACHE ) -endif() -unset( _ndk_ccache ) - - -# setup the cross-compiler -if( NOT CMAKE_C_COMPILER ) - if( NDK_CCACHE AND NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) - set( CMAKE_C_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C compiler" ) - set( CMAKE_CXX_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C++ compiler" ) - if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_C_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") - set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") - else() - set( CMAKE_C_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") - set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") - endif() - else() - if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_C_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") - set( CMAKE_CXX_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") - else() - set( CMAKE_C_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler" ) - set( CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler" ) - endif() - endif() - set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "assembler" ) - set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" ) - if( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" ) - # Use gcc-ar if we have it for better LTO support. - set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) - else() - set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) - endif() - set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" ) - set( CMAKE_NM "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm" ) - set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" ) - set( CMAKE_OBJDUMP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objdump${TOOL_OS_SUFFIX}" CACHE PATH "objdump" ) - set( CMAKE_RANLIB "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ranlib${TOOL_OS_SUFFIX}" CACHE PATH "ranlib" ) -endif() - -set( _CMAKE_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_MACHINE_NAME}-" ) -if( CMAKE_VERSION VERSION_LESS 2.8.5 ) - set( CMAKE_ASM_COMPILER_ARG1 "-c" ) -endif() -if( APPLE ) - find_program( CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool ) - if( NOT CMAKE_INSTALL_NAME_TOOL ) - message( FATAL_ERROR "Could not find install_name_tool, please check your installation." ) - endif() - mark_as_advanced( CMAKE_INSTALL_NAME_TOOL ) -endif() - -# Force set compilers because standard identification works badly for us -include( CMakeForceCompiler ) -CMAKE_FORCE_C_COMPILER( "${CMAKE_C_COMPILER}" GNU ) -if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_C_COMPILER_ID Clang ) -endif() -set( CMAKE_C_PLATFORM_ID Linux ) -if( X86_64 OR MIPS64 OR ARM64_V8A ) - set( CMAKE_C_SIZEOF_DATA_PTR 8 ) -else() - set( CMAKE_C_SIZEOF_DATA_PTR 4 ) -endif() -set( CMAKE_C_HAS_ISYSROOT 1 ) -set( CMAKE_C_COMPILER_ABI ELF ) -CMAKE_FORCE_CXX_COMPILER( "${CMAKE_CXX_COMPILER}" GNU ) -if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_CXX_COMPILER_ID Clang) -endif() -set( CMAKE_CXX_PLATFORM_ID Linux ) -set( CMAKE_CXX_SIZEOF_DATA_PTR ${CMAKE_C_SIZEOF_DATA_PTR} ) -set( CMAKE_CXX_HAS_ISYSROOT 1 ) -set( CMAKE_CXX_COMPILER_ABI ELF ) -set( CMAKE_CXX_SOURCE_FILE_EXTENSIONS cc cp cxx cpp CPP c++ C ) -# force ASM compiler (required for CMake < 2.8.5) -set( CMAKE_ASM_COMPILER_ID_RUN TRUE ) -set( CMAKE_ASM_COMPILER_ID GNU ) -set( CMAKE_ASM_COMPILER_WORKS TRUE ) -set( CMAKE_ASM_COMPILER_FORCED TRUE ) -set( CMAKE_COMPILER_IS_GNUASM 1) -set( CMAKE_ASM_SOURCE_FILE_EXTENSIONS s S asm ) - -foreach( lang C CXX ASM ) - if( ANDROID_COMPILER_IS_CLANG ) - set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_CLANG_VERSION} ) - else() - set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_COMPILER_VERSION} ) - endif() -endforeach() - -# flags and definitions -remove_definitions( -DANDROID ) -add_definitions( -DANDROID ) - -if( ANDROID_SYSROOT MATCHES "[ ;\"]" ) - if( CMAKE_HOST_WIN32 ) - # try to convert path to 8.3 form - file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "@echo %~s1" ) - execute_process( COMMAND "$ENV{ComSpec}" /c "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "${ANDROID_SYSROOT}" - OUTPUT_VARIABLE __path OUTPUT_STRIP_TRAILING_WHITESPACE - RESULT_VARIABLE __result ERROR_QUIET ) - if( __result EQUAL 0 ) - file( TO_CMAKE_PATH "${__path}" ANDROID_SYSROOT ) - set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) - else() - set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" ) - endif() - else() - set( ANDROID_CXX_FLAGS "'--sysroot=${ANDROID_SYSROOT}'" ) - endif() - if( NOT _CMAKE_IN_TRY_COMPILE ) - # quotes can break try_compile and compiler identification - message(WARNING "Path to your Android NDK (or toolchain) has non-alphanumeric symbols.\nThe build might be broken.\n") - endif() -else() - set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) -endif() - -# NDK flags -if (ARM64_V8A ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) - set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) - endif() -elseif( ARMEABI OR ARMEABI_V7A) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) - if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 ) - set( ANDROID_CXX_FLAGS_RELEASE "-mthumb -fomit-frame-pointer -fno-strict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -finline-limit=64" ) - endif() - else() - # always compile ARMEABI_V6 in arm mode; otherwise there is no difference from ARMEABI - set( ANDROID_CXX_FLAGS_RELEASE "-marm -fomit-frame-pointer -fstrict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) - endif() - endif() -elseif( X86 OR X86_64 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) - endif() - set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) - set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) -elseif( MIPS OR MIPS64 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-strict-aliasing -finline-functions -funwind-tables -fmessage-length=0" ) - set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer" ) - set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer" ) - if( NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" ) - set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) - endif() -elseif() - set( ANDROID_CXX_FLAGS_RELEASE "" ) - set( ANDROID_CXX_FLAGS_DEBUG "" ) -endif() - -set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) # good/necessary when porting desktop libraries - -if( NOT X86 AND NOT ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "-Wno-psabi ${ANDROID_CXX_FLAGS}" ) -endif() - -if( NOT ANDROID_COMPILER_VERSION VERSION_LESS "4.6" ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -no-canonical-prefixes" ) # see https://android-review.googlesource.com/#/c/47564/ -endif() - -# ABI-specific flags -if( ARMEABI_V7A ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv7-a -mfloat-abi=softfp" ) - if( NEON ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=neon" ) - elseif( VFPV3 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3" ) - else() - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3-d16" ) - endif() -elseif( ARMEABI_V6 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv6 -mfloat-abi=softfp -mfpu=vfp" ) # vfp == vfpv2 -elseif( ARMEABI ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv5te -mtune=xscale -msoft-float" ) -endif() - -if( ANDROID_STL MATCHES "gnustl" AND (EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}") ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) - set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) - set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) -else() - set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) - set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) - set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) -endif() - -# STL -if( EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" ) - if( EXISTS "${__libstl}" ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"" ) - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libstl}\"" ) - endif() - if( EXISTS "${__libsupcxx}" ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) - # C objects: - set( CMAKE_C_CREATE_SHARED_LIBRARY " -o " ) - set( CMAKE_C_CREATE_SHARED_MODULE " -o " ) - set( CMAKE_C_LINK_EXECUTABLE " -o " ) - set( CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) - set( CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) - set( CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) - endif() - if( ANDROID_STL MATCHES "gnustl" ) - if( NOT EXISTS "${ANDROID_LIBM_PATH}" ) - set( ANDROID_LIBM_PATH -lm ) - endif() - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} ${ANDROID_LIBM_PATH}" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} ${ANDROID_LIBM_PATH}" ) - set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} ${ANDROID_LIBM_PATH}" ) - endif() -endif() - -# variables controlling optional build flags -if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - # libGLESv2.so in NDK's prior to r7 refers to missing external symbols. - # So this flag option is required for all projects using OpenGL from native. - __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON ) -else() - __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) -endif() -__INIT_VARIABLE( ANDROID_NO_UNDEFINED VALUES ON ) -__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) -__INIT_VARIABLE( ANDROID_GOLD_LINKER VALUES ON ) -__INIT_VARIABLE( ANDROID_NOEXECSTACK VALUES ON ) -__INIT_VARIABLE( ANDROID_RELRO VALUES ON ) - -set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" ) -set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) -set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Put each function in separate section and enable garbage collection of unused input sections at link time" ) -set( ANDROID_GOLD_LINKER ${ANDROID_GOLD_LINKER} CACHE BOOL "Enables gold linker" ) -set( ANDROID_NOEXECSTACK ${ANDROID_NOEXECSTACK} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) -set( ANDROID_RELRO ${ANDROID_RELRO} CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" ) -mark_as_advanced( ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_FUNCTION_LEVEL_LINKING ANDROID_GOLD_LINKER ANDROID_NOEXECSTACK ANDROID_RELRO ) - -# linker flags -set( ANDROID_LINKER_FLAGS "" ) - -if( ARMEABI_V7A ) - # this is *required* to use the following linker flags that routes around - # a CPU bug in some Cortex-A8 implementations: - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--fix-cortex-a8" ) -endif() - -if( ANDROID_NO_UNDEFINED ) - if( MIPS ) - # there is some sysroot-related problem in mips linker... - if( NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined -Wl,-rpath-link,${ANDROID_SYSROOT}/usr/lib" ) - endif() - else() - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined" ) - endif() -endif() - -if( ANDROID_SO_UNDEFINED ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-allow-shlib-undefined" ) -endif() - -if( ANDROID_FUNCTION_LEVEL_LINKING ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" ) -endif() - -if( ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" ) - if( ANDROID_GOLD_LINKER AND (CMAKE_HOST_UNIX OR ANDROID_NDK_RELEASE_NUM GREATER 8002) AND (ARMEABI OR ARMEABI_V7A OR X86) ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" ) - elseif( ANDROID_NDK_RELEASE_NUM GREATER 8002 ) # after r8b - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=bfd" ) - elseif( ANDROID_NDK_RELEASE STREQUAL "r8b" AND ARMEABI AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "The default bfd linker from arm GCC 4.6 toolchain can fail with 'unresolvable R_ARM_THM_CALL relocation' error message. See https://code.google.com/p/android/issues/detail?id=35342 - On Linux and OS X host platform you can workaround this problem using gold linker (default). - Rerun cmake with -DANDROID_GOLD_LINKER=ON option in case of problems. -" ) - endif() -endif() # version 4.6 - -if( ANDROID_NOEXECSTACK ) - if( ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Xclang -mnoexecstack" ) - else() - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Wa,--noexecstack" ) - endif() - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,noexecstack" ) -endif() - -if( ANDROID_RELRO ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now" ) -endif() - -if( ANDROID_COMPILER_IS_CLANG ) - set( ANDROID_CXX_FLAGS "-target ${ANDROID_LLVM_TRIPLE} -Qunused-arguments ${ANDROID_CXX_FLAGS}" ) - if( BUILD_WITH_ANDROID_NDK ) - set( ANDROID_CXX_FLAGS "-gcc-toolchain ${ANDROID_TOOLCHAIN_ROOT} ${ANDROID_CXX_FLAGS}" ) - endif() -endif() - -# cache flags -set( CMAKE_CXX_FLAGS "" CACHE STRING "c++ flags" ) -set( CMAKE_C_FLAGS "" CACHE STRING "c flags" ) -set( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c++ Release flags" ) -set( CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c Release flags" ) -set( CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c++ Debug flags" ) -set( CMAKE_C_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c Debug flags" ) -set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "shared linker flags" ) -set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "module linker flags" ) -set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "executable linker flags" ) - -# put flags to cache (for debug purpose only) -set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Android specific c/c++ flags" ) -set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE}" CACHE INTERNAL "Android specific c/c++ Release flags" ) -set( ANDROID_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG}" CACHE INTERNAL "Android specific c/c++ Debug flags" ) -set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Android specific c/c++ linker flags" ) - -# finish flags -set( CMAKE_CXX_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" ) -set( CMAKE_C_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_C_FLAGS}" ) -set( CMAKE_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}" ) -set( CMAKE_C_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}" ) -set( CMAKE_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}" ) -set( CMAKE_C_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}" ) -set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) -set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) -set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) - -if( MIPS AND BUILD_WITH_ANDROID_NDK AND ANDROID_NDK_RELEASE STREQUAL "r8" ) - set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_SHARED_LINKER_FLAGS}" ) - set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_MODULE_LINKER_FLAGS}" ) - set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.x ${CMAKE_EXE_LINKER_FLAGS}" ) -endif() - -# pie/pic -if( NOT (ANDROID_NATIVE_API_LEVEL LESS 16) AND (NOT DEFINED ANDROID_APP_PIE OR ANDROID_APP_PIE) AND (CMAKE_VERSION VERSION_GREATER 2.8.8) ) - set( CMAKE_POSITION_INDEPENDENT_CODE TRUE ) - set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie") -else() - set( CMAKE_POSITION_INDEPENDENT_CODE FALSE ) - set( CMAKE_CXX_FLAGS "-fpic ${CMAKE_CXX_FLAGS}" ) - set( CMAKE_C_FLAGS "-fpic ${CMAKE_C_FLAGS}" ) -endif() - -# configure rtti -if( DEFINED ANDROID_RTTI AND ANDROID_STL_FORCE_FEATURES ) - if( ANDROID_RTTI ) - set( CMAKE_CXX_FLAGS "-frtti ${CMAKE_CXX_FLAGS}" ) - else() - set( CMAKE_CXX_FLAGS "-fno-rtti ${CMAKE_CXX_FLAGS}" ) - endif() -endif() - -# configure exceptios -if( DEFINED ANDROID_EXCEPTIONS AND ANDROID_STL_FORCE_FEATURES ) - if( ANDROID_EXCEPTIONS ) - set( CMAKE_CXX_FLAGS "-fexceptions ${CMAKE_CXX_FLAGS}" ) - set( CMAKE_C_FLAGS "-fexceptions ${CMAKE_C_FLAGS}" ) - else() - set( CMAKE_CXX_FLAGS "-fno-exceptions ${CMAKE_CXX_FLAGS}" ) - set( CMAKE_C_FLAGS "-fno-exceptions ${CMAKE_C_FLAGS}" ) - endif() -endif() - -# global includes and link directories -include_directories( SYSTEM "${ANDROID_SYSROOT}/usr/include" ${ANDROID_STL_INCLUDE_DIRS} ) -get_filename_component(__android_install_path "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ABSOLUTE) # avoid CMP0015 policy warning -link_directories( "${__android_install_path}" ) - -# detect if need link crtbegin_so.o explicitly -if( NOT DEFINED ANDROID_EXPLICIT_CRT_LINK ) - set( __cmd "${CMAKE_CXX_CREATE_SHARED_LIBRARY}" ) - string( REPLACE "" "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_CXX_FLAGS}" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_SHARED_LINKER_FLAGS}" __cmd "${__cmd}" ) - string( REPLACE "" "-shared" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - string( REPLACE "" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain_crtlink_test.so" __cmd "${__cmd}" ) - string( REPLACE "" "\"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" __cmd "${__cmd}" ) - string( REPLACE "" "" __cmd "${__cmd}" ) - separate_arguments( __cmd ) - foreach( __var ANDROID_NDK ANDROID_NDK_TOOLCHAINS_PATH ANDROID_STANDALONE_TOOLCHAIN ) - if( ${__var} ) - set( __tmp "${${__var}}" ) - separate_arguments( __tmp ) - string( REPLACE "${__tmp}" "${${__var}}" __cmd "${__cmd}") - endif() - endforeach() - string( REPLACE "'" "" __cmd "${__cmd}" ) - string( REPLACE "\"" "" __cmd "${__cmd}" ) - execute_process( COMMAND ${__cmd} RESULT_VARIABLE __cmd_result OUTPUT_QUIET ERROR_QUIET ) - if( __cmd_result EQUAL 0 ) - set( ANDROID_EXPLICIT_CRT_LINK ON ) - else() - set( ANDROID_EXPLICIT_CRT_LINK OFF ) - endif() -endif() - -if( ANDROID_EXPLICIT_CRT_LINK ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) - set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) -endif() - -# setup output directories -set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" ) - -if( DEFINED LIBRARY_OUTPUT_PATH_ROOT - OR EXISTS "${CMAKE_SOURCE_DIR}/AndroidManifest.xml" - OR (EXISTS "${CMAKE_SOURCE_DIR}/../AndroidManifest.xml" AND EXISTS "${CMAKE_SOURCE_DIR}/../jni/") ) - set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "Root for binaries output, set this to change where Android libs are installed to" ) - if( NOT _CMAKE_IN_TRY_COMPILE ) - if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) - else() - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) - endif() - set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for Android libs" ) - endif() -endif() - -# copy shaed stl library to build directory -if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" AND DEFINED LIBRARY_OUTPUT_PATH ) - get_filename_component( __libstlname "${__libstl}" NAME ) - execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) - if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") - message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) - endif() - unset( __fileCopyProcess ) - unset( __libstlname ) -endif() - - -# set these global flags for cmake client scripts to change behavior -set( ANDROID True ) -set( BUILD_ANDROID True ) - -# where is the target environment -set( CMAKE_FIND_ROOT_PATH "${ANDROID_TOOLCHAIN_ROOT}/bin" "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" "${ANDROID_SYSROOT}" "${CMAKE_INSTALL_PREFIX}" "${CMAKE_INSTALL_PREFIX}/share" ) - -# only search for libraries and includes in the ndk toolchain -set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) -set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) -set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) - - -# macro to find packages on the host OS -macro( find_host_package ) - set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) - set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER ) - set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER ) - if( CMAKE_HOST_WIN32 ) - SET( WIN32 1 ) - SET( UNIX ) - elseif( CMAKE_HOST_APPLE ) - SET( APPLE 1 ) - SET( UNIX ) - endif() - find_package( ${ARGN} ) - SET( WIN32 ) - SET( APPLE ) - SET( UNIX 1 ) - set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) - set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) - set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) -endmacro() - - -# macro to find programs on the host OS -macro( find_host_program ) - set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) - set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER ) - set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER ) - if( CMAKE_HOST_WIN32 ) - SET( WIN32 1 ) - SET( UNIX ) - elseif( CMAKE_HOST_APPLE ) - SET( APPLE 1 ) - SET( UNIX ) - endif() - find_program( ${ARGN} ) - SET( WIN32 ) - SET( APPLE ) - SET( UNIX 1 ) - set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) - set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) - set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) -endmacro() - - -# export toolchain settings for the try_compile() command -if( NOT _CMAKE_IN_TRY_COMPILE ) - set( __toolchain_config "") - foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN - ANDROID_NDK_HOST_X64 - ANDROID_NDK - ANDROID_NDK_LAYOUT - ANDROID_STANDALONE_TOOLCHAIN - ANDROID_TOOLCHAIN_NAME - ANDROID_ABI - ANDROID_NATIVE_API_LEVEL - ANDROID_STL - ANDROID_STL_FORCE_FEATURES - ANDROID_FORCE_ARM_BUILD - ANDROID_NO_UNDEFINED - ANDROID_SO_UNDEFINED - ANDROID_FUNCTION_LEVEL_LINKING - ANDROID_GOLD_LINKER - ANDROID_NOEXECSTACK - ANDROID_RELRO - ANDROID_LIBM_PATH - ANDROID_EXPLICIT_CRT_LINK - ANDROID_APP_PIE - ) - if( DEFINED ${__var} ) - if( ${__var} MATCHES " ") - set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" ) - else() - set( __toolchain_config "${__toolchain_config}set( ${__var} ${${__var}} CACHE INTERNAL \"\" )\n" ) - endif() - endif() - endforeach() - file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android.toolchain.config.cmake" "${__toolchain_config}" ) - unset( __toolchain_config ) -endif() - - -# force cmake to produce / instead of \ in build commands for Ninja generator -if( CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_HOST_WIN32 ) - # it is a bad hack after all - # CMake generates Ninja makefiles with UNIX paths only if it thinks that we are going to build with MinGW - set( CMAKE_COMPILER_IS_MINGW TRUE ) # tell CMake that we are MinGW - set( CMAKE_CROSSCOMPILING TRUE ) # stop recursion - enable_language( C ) - enable_language( CXX ) - # unset( CMAKE_COMPILER_IS_MINGW ) # can't unset because CMake does not convert back-slashes in response files without it - unset( MINGW ) -endif() - - -# Variables controlling behavior or set by cmake toolchain: -# ANDROID_ABI : "armeabi-v7a" (default), "armeabi", "armeabi-v7a with NEON", "armeabi-v7a with VFPV3", "armeabi-v6 with VFP", "x86", "mips", "arm64-v8a", "x86_64", "mips64" -# ANDROID_NATIVE_API_LEVEL : 3,4,5,8,9,14,15,16,17,18,19,21 (depends on NDK version) -# ANDROID_STL : gnustl_static/gnustl_shared/stlport_static/stlport_shared/gabi++_static/gabi++_shared/system_re/system/none -# ANDROID_FORBID_SYGWIN : ON/OFF -# ANDROID_NO_UNDEFINED : ON/OFF -# ANDROID_SO_UNDEFINED : OFF/ON (default depends on NDK version) -# ANDROID_FUNCTION_LEVEL_LINKING : ON/OFF -# ANDROID_GOLD_LINKER : ON/OFF -# ANDROID_NOEXECSTACK : ON/OFF -# ANDROID_RELRO : ON/OFF -# ANDROID_FORCE_ARM_BUILD : ON/OFF -# ANDROID_STL_FORCE_FEATURES : ON/OFF -# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product//obj/lib/libm.so) to workaround unresolved `sincos` -# Can be set only at the first run: -# ANDROID_NDK : path to your NDK install -# NDK_CCACHE : path to your ccache executable -# ANDROID_TOOLCHAIN_NAME : the NDK name of compiler toolchain -# ANDROID_NDK_HOST_X64 : try to use x86_64 toolchain (default for x64 host systems) -# ANDROID_NDK_LAYOUT : the inner NDK structure (RELEASE, LINARO, ANDROID) -# LIBRARY_OUTPUT_PATH_ROOT : -# ANDROID_STANDALONE_TOOLCHAIN -# -# Primary read-only variables: -# ANDROID : always TRUE -# ARMEABI : TRUE for arm v6 and older devices -# ARMEABI_V6 : TRUE for arm v6 -# ARMEABI_V7A : TRUE for arm v7a -# ARM64_V8A : TRUE for arm64-v8a -# NEON : TRUE if NEON unit is enabled -# VFPV3 : TRUE if VFP version 3 is enabled -# X86 : TRUE if configured for x86 -# X86_64 : TRUE if configured for x86_64 -# MIPS : TRUE if configured for mips -# MIPS64 : TRUE if configured for mips64 -# BUILD_WITH_ANDROID_NDK : TRUE if NDK is used -# BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used -# ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform -# ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a", "x86", "mips", "arm64-v8a", "x86_64", "mips64" depending on ANDROID_ABI -# ANDROID_NDK_RELEASE : from r5 to r10d; set only for NDK -# ANDROID_NDK_RELEASE_NUM : numeric ANDROID_NDK_RELEASE version (1000*major+minor) -# ANDROID_ARCH_NAME : "arm", "x86", "mips", "arm64", "x86_64", "mips64" depending on ANDROID_ABI -# ANDROID_SYSROOT : path to the compiler sysroot -# TOOL_OS_SUFFIX : "" or ".exe" depending on host platform -# ANDROID_COMPILER_IS_CLANG : TRUE if clang compiler is used -# -# Secondary (less stable) read-only variables: -# ANDROID_COMPILER_VERSION : GCC version used (not Clang version) -# ANDROID_CLANG_VERSION : version of clang compiler if clang is used -# ANDROID_CXX_FLAGS : C/C++ compiler flags required by Android platform -# ANDROID_SUPPORTED_ABIS : list of currently allowed values for ANDROID_ABI -# ANDROID_TOOLCHAIN_MACHINE_NAME : "arm-linux-androideabi", "arm-eabi" or "i686-android-linux" -# ANDROID_TOOLCHAIN_ROOT : path to the top level of toolchain (standalone or placed inside NDK) -# ANDROID_CLANG_TOOLCHAIN_ROOT : path to clang tools -# ANDROID_SUPPORTED_NATIVE_API_LEVELS : list of native API levels found inside NDK -# ANDROID_STL_INCLUDE_DIRS : stl include paths -# ANDROID_RTTI : if rtti is enabled by the runtime -# ANDROID_EXCEPTIONS : if exceptions are enabled by the runtime -# ANDROID_GCC_TOOLCHAIN_NAME : read-only, differs from ANDROID_TOOLCHAIN_NAME only if clang is used -# -# Defaults: -# ANDROID_DEFAULT_NDK_API_LEVEL -# ANDROID_DEFAULT_NDK_API_LEVEL_${ARCH} -# ANDROID_NDK_SEARCH_PATHS -# ANDROID_SUPPORTED_ABIS_${ARCH} -# ANDROID_SUPPORTED_NDK_VERSIONS diff --git a/cmake/android/deployment-file.json.in b/cmake/android/deployment-file.json.in deleted file mode 100644 index 81ed8a6ecc..0000000000 --- a/cmake/android/deployment-file.json.in +++ /dev/null @@ -1,13 +0,0 @@ -{ - "qt": "@QT_DIR@", - "sdk": "@ANDROID_SDK_ROOT@", - "ndk": "@ANDROID_NDK@", - "toolchain-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@", - "tool-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@", - "toolchain-version": "@ANDROID_COMPILER_VERSION@", - "ndk-host": "@ANDROID_NDK_HOST_SYSTEM_NAME@", - "target-architecture": "@ANDROID_ABI@", - "application-binary": "@EXECUTABLE_DESTINATION_PATH@", - "android-extra-libs": "@_DEPS@", - "android-package-source-directory": "@ANDROID_APK_BUILD_DIR@" -} diff --git a/cmake/android/strings.xml.in b/cmake/android/strings.xml.in deleted file mode 100644 index 6e6ce7b12e..0000000000 --- a/cmake/android/strings.xml.in +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - ${ANDROID_APP_DISPLAY_NAME} - - Can\'t find Ministro service.\nThe application can\'t start. - This application requires Ministro service. Would you like to install it? - Your application encountered a fatal error and cannot continue. - diff --git a/cmake/externals/glm/CMakeLists.txt b/cmake/externals/glm/CMakeLists.txt index 79a44fa48e..bc8089074f 100644 --- a/cmake/externals/glm/CMakeLists.txt +++ b/cmake/externals/glm/CMakeLists.txt @@ -6,7 +6,7 @@ ExternalProject_Add( URL https://hifi-public.s3.amazonaws.com/dependencies/glm-0.9.8.zip URL_MD5 579ac77a3110befa3244d68c0ceb7281 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= ${EXTERNAL_ARGS} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 diff --git a/cmake/externals/tbb/CMakeLists.txt b/cmake/externals/tbb/CMakeLists.txt index 71d7b94597..9664fe7250 100644 --- a/cmake/externals/tbb/CMakeLists.txt +++ b/cmake/externals/tbb/CMakeLists.txt @@ -8,9 +8,6 @@ if (WIN32) elseif (APPLE) set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb2017_20170604oss_mac_slim.tar.gz) set(DOWNLOAD_MD5 62bde626b396f8e1a85c6a8ded1d8105) -elseif (ANDROID) - set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_and_slim.tar.gz) - set(DOWNLOAD_MD5 04d50b64e1d81245a1be5f75f34d64c7) else () set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz) set(DOWNLOAD_MD5 2a5c721f40fa3503ffc12c18dd00011c) @@ -107,3 +104,4 @@ endif () if (DEFINED ${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE "List of tbb include directories") endif () + diff --git a/cmake/init.cmake b/cmake/init.cmake index 75fb3a4b52..9d7b0fd94c 100644 --- a/cmake/init.cmake +++ b/cmake/init.cmake @@ -34,10 +34,23 @@ file(GLOB HIFI_CUSTOM_MACROS "cmake/macros/*.cmake") foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS}) include(${CUSTOM_MACRO}) endforeach() +unset(HIFI_CUSTOM_MACROS) if (ANDROID) - file(GLOB ANDROID_CUSTOM_MACROS "cmake/android/*.cmake") - foreach(CUSTOM_MACRO ${ANDROID_CUSTOM_MACROS}) - include(${CUSTOM_MACRO}) - endforeach() + set(BUILD_SHARED_LIBS ON) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) + + string(REGEX REPLACE "\\\\" "/" ANDROID_NDK ${ANDROID_NDK}) + string(REGEX REPLACE "\\\\" "/" CMAKE_TOOLCHAIN_FILE ${CMAKE_TOOLCHAIN_FILE}) + string(REGEX REPLACE "\\\\" "/" ANDROID_TOOLCHAIN ${ANDROID_TOOLCHAIN}) + string(REGEX REPLACE "\\\\" "/" CMAKE_MAKE_PROGRAM ${CMAKE_MAKE_PROGRAM}) + list(APPEND EXTERNAL_ARGS -DANDROID_ABI=${ANDROID_ABI}) + list(APPEND EXTERNAL_ARGS -DANDROID_NDK=${ANDROID_NDK}) + list(APPEND EXTERNAL_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) + list(APPEND EXTERNAL_ARGS -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}) + list(APPEND EXTERNAL_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) + list(APPEND EXTERNAL_ARGS -DHIFI_ANDROID=${HIFI_ANDROID}) + list(APPEND EXTERNAL_ARGS -DANDROID_PLATFORM=${ANDROID_PLATFORM}) + list(APPEND EXTERNAL_ARGS -DANDROID_TOOLCHAIN=${ANDROID_TOOLCHAIN}) + list(APPEND EXTERNAL_ARGS -DANDROID_STL=${ANDROID_STL}) endif () diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index c43ade45d2..c5b35b7e90 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -62,7 +62,9 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) # since it's unrunnable by the cross-compiling build machine # so, we require the compiling user to point us at a compiled executable version for their native toolchain - find_program(NATIVE_SCRIBE scribe PATHS ${SCRIBE_PATH} ENV SCRIBE_PATH) + if (NOT NATIVE_SCRIBE) + find_program(NATIVE_SCRIBE scribe PATHS ${SCRIBE_PATH} ENV SCRIBE_PATH) + endif() if (NOT NATIVE_SCRIBE) message(FATAL_ERROR "The High Fidelity scribe tool is required for shader pre-processing. \ diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 8458d53f68..8faa4e6d96 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -162,5 +162,6 @@ macro(SET_PACKAGING_PARAMETERS) # create a header file our targets can use to find out the application version file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/includes") configure_file("${HF_CMAKE_DIR}/templates/BuildInfo.h.in" "${CMAKE_BINARY_DIR}/includes/BuildInfo.h") + include_directories("${CMAKE_BINARY_DIR}/includes") endmacro(SET_PACKAGING_PARAMETERS) diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index d0fc58af0c..04687e2c84 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -12,7 +12,7 @@ macro(SETUP_HIFI_LIBRARY) project(${TARGET_NAME}) # grab the implementation and header files - file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c") + file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c" "src/*.qrc") list(APPEND ${TARGET_NAME}_SRCS ${LIB_SRCS}) # add compiler flags to AVX source files @@ -65,7 +65,7 @@ macro(SETUP_HIFI_LIBRARY) list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core) # find these Qt modules and link them to our own target - find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED) + find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED CMAKE_FIND_ROOT_PATH_BOTH) foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES}) target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) diff --git a/cmake/macros/SetupQt.cmake b/cmake/macros/SetupQt.cmake index b2a89f81e5..ece8607b9b 100644 --- a/cmake/macros/SetupQt.cmake +++ b/cmake/macros/SetupQt.cmake @@ -28,7 +28,7 @@ function(calculate_default_qt_dir _RESULT_NAME) set(QT_DEFAULT_ARCH "gcc_64") endif() - if (WIN32) + if (WIN32 OR (ANDROID AND ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows"))) set(QT_DEFAULT_ROOT "c:/Qt") else() set(QT_DEFAULT_ROOT "$ENV{HOME}/Qt") diff --git a/cmake/macros/TargetGlew.cmake b/cmake/macros/TargetGlew.cmake index 5f71f021ec..bc4d5cb033 100644 --- a/cmake/macros/TargetGlew.cmake +++ b/cmake/macros/TargetGlew.cmake @@ -6,9 +6,11 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # macro(TARGET_GLEW) - add_dependency_external_projects(glew) - find_package(GLEW REQUIRED) - add_definitions(-DGLEW_STATIC) - target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARY}) + if (NOT ANDROID) + add_definitions(-DGLEW_STATIC) + add_dependency_external_projects(glew) + find_package(GLEW REQUIRED) + target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARY}) + endif() endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetOpenGL.cmake b/cmake/macros/TargetOpenGL.cmake index 73c92e651a..6ad92259bb 100644 --- a/cmake/macros/TargetOpenGL.cmake +++ b/cmake/macros/TargetOpenGL.cmake @@ -6,15 +6,13 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # macro(TARGET_OPENGL) - add_definitions(-DGLEW_STATIC) if (APPLE) # link in required OS X frameworks and include the right GL headers find_library(OpenGL OpenGL) target_link_libraries(${TARGET_NAME} ${OpenGL}) elseif(ANDROID) - target_link_libraries(${TARGET_NAME} "-lGLESv3" "-lEGL") + target_link_libraries(${TARGET_NAME} GLESv3 EGL) else() - target_nsight() find_package(OpenGL REQUIRED) if (${OPENGL_INCLUDE_DIR}) include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") @@ -22,4 +20,6 @@ macro(TARGET_OPENGL) target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) endif() + target_nsight() + target_glew() endmacro() diff --git a/cmake/macros/TargetOpenSSL.cmake b/cmake/macros/TargetOpenSSL.cmake new file mode 100644 index 0000000000..7ee0283a48 --- /dev/null +++ b/cmake/macros/TargetOpenSSL.cmake @@ -0,0 +1,32 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Bradley Austin Davis on 2015/10/10 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_OPENSSL) + + if (ANDROID) + + # FIXME use a distributable binary + set(OPENSSL_INSTALL_DIR C:/Android/openssl) + set(OPENSSL_INCLUDE_DIR "${OPENSSL_INSTALL_DIR}/include" CACHE TYPE INTERNAL) + set(OPENSSL_LIBRARIES "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a;${OPENSSL_INSTALL_DIR}/lib/libssl.a" CACHE TYPE INTERNAL) + + else() + + find_package(OpenSSL REQUIRED) + + if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") + # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto + message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." + "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") + endif() + + endif() + + include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") + target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) + +endmacro() diff --git a/cmake/macros/TargetTBB.cmake b/cmake/macros/TargetTBB.cmake new file mode 100644 index 0000000000..e9c4639c3d --- /dev/null +++ b/cmake/macros/TargetTBB.cmake @@ -0,0 +1,24 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Bradley Austin Davis on 2015/10/10 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_TBB) + +if (ANDROID) + set(TBB_INSTALL_DIR C:/tbb-2018/built) + set(TBB_LIBRARY ${HIFI_ANDROID_PRECOMPILED}/libtbb.so CACHE FILEPATH "TBB library location") + set(TBB_MALLOC_LIBRARY ${HIFI_ANDROID_PRECOMPILED}/libtbbmalloc.so CACHE FILEPATH "TBB malloc library location") + set(TBB_INCLUDE_DIRS ${TBB_INSTALL_DIR}/include CACHE TYPE "List of tbb include directories" CACHE FILEPATH "TBB includes location") + set(TBB_LIBRARIES ${TBB_LIBRARY} ${TBB_MALLOC_LIBRARY}) +else() + add_dependency_external_projects(tbb) + find_package(TBB REQUIRED) +endif() + +target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES}) +target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) + +endmacro() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 43e50c6d33..305a6475f6 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -171,8 +171,6 @@ else () add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) endif () -target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes") - if (WIN32) # These are external plugins, but we need to do the 'add dependency' here so that their # binary directories get added to the fixup path @@ -214,10 +212,6 @@ target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries target_bullet() target_opengl() -if (NOT ANDROID) - target_glew() -endif () - # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 4e7cc08919..621b1a3e72 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1503,6 +1503,11 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes); _inputRingBuffer.resizeForFrameSize(numFrameSamples); +#if defined(Q_OS_ANDROID) + if (_audioInput) { + connect(_audioInput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(audioInputStateChanged(QAudio::State))); + } +#endif _inputDevice = _audioInput->start(); if (_inputDevice) { @@ -1541,6 +1546,31 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn return supportedFormat; } +#if defined(Q_OS_ANDROID) +void AudioClient::audioInputStateChanged(QAudio::State state) { + switch (state) { + case QAudio::StoppedState: + if (!_audioInput) { + break; + } + // Stopped on purpose + if (_shouldRestartInputSetup) { + Lock lock(_deviceMutex); + _inputDevice = _audioInput->start(); + lock.unlock(); + if (_inputDevice) { + connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); + } + } + break; + case QAudio::ActiveState: + break; + default: + break; + } +} +#endif + void AudioClient::outputNotify() { int recentUnfulfilled = _audioOutputIODevice.getRecentUnfulfilledReads(); if (recentUnfulfilled > 0) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index ff0ea968a8..01a487455c 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include @@ -173,6 +173,9 @@ public slots: void sendDownstreamAudioStatsPacket() { _stats.publish(); } void handleMicAudioInput(); +#if defined(Q_OS_ANDROID) + void audioInputStateChanged(QAudio::State state); +#endif void handleDummyAudioInput(); void handleRecordedAudioInput(const QByteArray& audio); void reset(); @@ -403,6 +406,10 @@ private: RateCounter<> _silentInbound; RateCounter<> _audioInbound; +#if defined(Q_OS_ANDROID) + bool _shouldRestartInputSetup { true }; // Should we restart the input device because of an unintended stop? +#endif + QTimer* _checkDevicesTimer { nullptr }; QTimer* _checkPeakValuesTimer { nullptr }; }; diff --git a/libraries/audio/src/AudioGate.cpp b/libraries/audio/src/AudioGate.cpp index a4d731a447..5b2561da07 100644 --- a/libraries/audio/src/AudioGate.cpp +++ b/libraries/audio/src/AudioGate.cpp @@ -6,11 +6,12 @@ // Copyright 2017 High Fidelity, Inc. // +#include "AudioGate.h" + #include #include - +#include #include "AudioDynamics.h" -#include "AudioGate.h" // log2 domain headroom bits above 0dB (int32_t) static const int LOG2_HEADROOM_Q30 = 1; @@ -417,7 +418,7 @@ void GateMono::process(int16_t* input, int16_t* output, int numFrames) { _dc.process(x); // peak detect - int32_t peak = abs(x); + int32_t peak = std::abs(x); // convert to log2 domain peak = fixlog2(peak); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 1baf649e64..278ef25445 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -103,7 +103,7 @@ AvatarData::~AvatarData() { QUrl AvatarData::_defaultFullAvatarModelUrl = {}; // In C++, if this initialization were in the AvatarInfo, every file would have it's own copy, even for class vars. const QUrl& AvatarData::defaultFullAvatarModelUrl() { if (_defaultFullAvatarModelUrl.isEmpty()) { - _defaultFullAvatarModelUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full.fst"); + _defaultFullAvatarModelUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "/meshes/defaultAvatar_full.fst"); } return _defaultFullAvatarModelUrl; } diff --git a/libraries/controllers/CMakeLists.txt b/libraries/controllers/CMakeLists.txt index 6b1ab72c60..9c6bbf4aae 100644 --- a/libraries/controllers/CMakeLists.txt +++ b/libraries/controllers/CMakeLists.txt @@ -8,7 +8,3 @@ link_hifi_libraries(shared) include_hifi_library_headers(networking) GroupSources("src/controllers") - -add_dependency_external_projects(glm) -find_package(GLM REQUIRED) -target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS} "${CMAKE_BINARY_DIR}/includes") diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 3e090dc7b3..da226d146b 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -28,11 +28,14 @@ DisplayPluginList getDisplayPlugins() { #ifdef DEBUG new NullDisplayPlugin(), #endif + +#if !defined(Q_OS_ANDROID) // Stereo modes // SBS left/right new SideBySideStereoDisplayPlugin(), // Interleaved left/right new InterleavedStereoDisplayPlugin(), +#endif nullptr }; diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp index 1e0e7e6c1f..6e397efbe5 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp @@ -40,14 +40,6 @@ bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) { return Parent::beginFrameRender(frameIndex); } -// DLL based display plugins MUST initialize GLEW inside the DLL code. -void DebugHmdDisplayPlugin::customizeContext() { - glewExperimental = true; - glewInit(); - glGetError(); // clear the potential error from glewExperimental - Parent::customizeContext(); -} - bool DebugHmdDisplayPlugin::internalActivate() { _ipd = 0.0327499993f * 2.0f; _eyeProjections[0][0] = vec4{ 0.759056330, 0.000000000, 0.000000000, 0.000000000 }; @@ -61,7 +53,7 @@ bool DebugHmdDisplayPlugin::internalActivate() { _eyeInverseProjections[0] = glm::inverse(_eyeProjections[0]); _eyeInverseProjections[1] = glm::inverse(_eyeProjections[1]); _eyeOffsets[0][3] = vec4{ -0.0327499993, 0.0, 0.0149999997, 1.0 }; - _eyeOffsets[0][3] = vec4{ 0.0327499993, 0.0, 0.0149999997, 1.0 }; + _eyeOffsets[1][3] = vec4{ 0.0327499993, 0.0, 0.0149999997, 1.0 }; _renderTargetSize = { 3024, 1680 }; _cullingProjection = _eyeProjections[0]; // This must come after the initialization, so that the values calculated diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h index 9bb82b1836..cd6fdd44b9 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h @@ -26,7 +26,6 @@ protected: void updatePresentPose() override; void hmdPresent() override {} bool isHmdMounted() const override { return true; } - void customizeContext() override; bool internalActivate() override; private: static const QString NAME; diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index bfbe6d7e5a..ccf2057e09 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -38,7 +38,7 @@ void main(void) { int frontCondition = 1 -int(gl_FrontFacing) * 2; vec3 color = varColor.rgb; packDeferredFragmentTranslucent( - interpolatedNormal * frontCondition, + float(frontCondition) * interpolatedNormal, texel.a * varColor.a, polyline.color * texel.rgb, vec3(0.01, 0.01, 0.01), diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f91bc14fe4..c6b5bc953b 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -659,22 +659,22 @@ QVector EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frust } QVector EntityScriptingInterface::findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const { - EntityTypes::EntityType type = EntityTypes::getEntityTypeFromName(entityType); + EntityTypes::EntityType type = EntityTypes::getEntityTypeFromName(entityType); - QVector result; - if (_entityTree) { - QVector entities; - _entityTree->withReadLock([&] { - _entityTree->findEntities(center, radius, entities); - }); + QVector result; + if (_entityTree) { + QVector entities; + _entityTree->withReadLock([&] { + _entityTree->findEntities(center, radius, entities); + }); - foreach(EntityItemPointer entity, entities) { - if (entity->getType() == type) { - result << entity->getEntityItemID(); - } - } - } - return result; + foreach(EntityItemPointer entity, entities) { + if (entity->getType() == type) { + result << entity->getEntityItemID(); + } + } + } + return result; } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 9b2b6360f3..7248c1f851 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -215,12 +215,12 @@ public slots: /// this function will not find any models in script engine contexts which don't have access to entities Q_INVOKABLE QVector findEntitiesInFrustum(QVariantMap frustum) const; - /// finds entities of the indicated type within a sphere given by the center point and radius - /// @param {QString} string representation of entity type - /// @param {vec3} center point - /// @param {float} radius to search - /// this function will not find any entities in script engine contexts which don't have access to entities - Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const; + /// finds entities of the indicated type within a sphere given by the center point and radius + /// @param {QString} string representation of entity type + /// @param {vec3} center point + /// @param {float} radius to search + /// this function will not find any entities in script engine contexts which don't have access to entities + Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const; /// If the scripting context has visible entities, this will determine a ray intersection, the results /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index cf5babdb1a..08acf9b058 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -590,7 +590,7 @@ bool EntityTree::findNearPointOperation(const OctreeElementPointer& element, voi bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) { RayArgs* args = static_cast(extraData); bool keepSearching = true; - EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast(element); + EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); if (entityTreeElementPointer->findRayIntersection(args->origin, args->direction, keepSearching, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->intersectedObject, args->precisionPicking)) { diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 4860e3d4a4..3e482fcd60 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -89,17 +89,17 @@ class PolyLineEntityItem : public EntityItem { BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const override { return false; } - // disable these external interfaces as PolyLineEntities caculate their own dimensions based on the points they contain - virtual void setRegistrationPoint(const glm::vec3& value) override {}; - virtual void setScale(const glm::vec3& scale) override {}; - virtual void setScale(float value) override {}; + // disable these external interfaces as PolyLineEntities caculate their own dimensions based on the points they contain + virtual void setRegistrationPoint(const glm::vec3& value) override {}; + virtual void setScale(const glm::vec3& scale) override {}; + virtual void setScale(float value) override {}; virtual void debugDump() const override; static const float DEFAULT_LINE_WIDTH; static const int MAX_POINTS_PER_LINE; private: - void calculateScaleAndRegistrationPoint(); - + void calculateScaleAndRegistrationPoint(); + protected: rgbColor _color; float _lineWidth { DEFAULT_LINE_WIDTH }; diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 9d74662e7c..586344ee81 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -223,7 +223,7 @@ void ShapeEntityItem::debugDump() const { qCDebug(entities) << " position:" << debugTreeVector(getPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << "SHAPE EntityItem Ptr:" << this; + qCDebug(entities) << "SHAPE EntityItem Ptr:" << this; } void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 3099782588..e0c2efd72e 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -28,6 +28,7 @@ #include "FBXReader.h" #include "ModelFormatLogging.h" +#include QHash COMMENT_SCALE_HINTS = {{"This file uses centimeters as units", 1.0f / 100.0f}, {"This file uses millimeters as units", 1.0f / 1000.0f}}; @@ -51,6 +52,10 @@ const QByteArray OBJTokenizer::getLineAsDatum() { return _device->readLine().trimmed(); } +float OBJTokenizer::getFloat() { + return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); +} + int OBJTokenizer::nextToken() { if (_pushedBackToken != NO_PUSHBACKED_TOKEN) { int token = _pushedBackToken; @@ -125,7 +130,7 @@ glm::vec3 OBJTokenizer::getVec3() { } bool OBJTokenizer::getVertex(glm::vec3& vertex, glm::vec3& vertexColor) { // Used for vertices which may also have a vertex color (RGB [0,1]) to follow. - // NOTE: Returns true if there is a vertex color. + // NOTE: Returns true if there is a vertex color. auto x = getFloat(); // N.B.: getFloat() has side-effect auto y = getFloat(); // And order of arguments is different on Windows/Linux. auto z = getFloat(); @@ -168,7 +173,7 @@ void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) { } // OBJFace -// NOTE (trent, 7/20/17): The vertexColors vector being passed-in isn't necessary here, but I'm just +// NOTE (trent, 7/20/17): The vertexColors vector being passed-in isn't necessary here, but I'm just // pairing it with the vertices vector for consistency. bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices, const QVector& vertexColors) { bool ok; @@ -544,9 +549,9 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, fbxMeshParts.append(FBXMeshPart()); FBXMeshPart& meshPartNew = fbxMeshParts.last(); - meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. + meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. meshPartNew.quadTrianglesIndices = QVector(meshPart.quadTrianglesIndices); // Copy over quad triangulated indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. - meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. + meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. // Do some of the material logic (which previously lived below) now. // All the faces in the same group will have the same name and material. diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 9a32871590..fb250833cf 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -22,7 +22,7 @@ public: glm::vec3 getVec3(); bool getVertex(glm::vec3& vertex, glm::vec3& vertexColor); glm::vec2 getVec2(); - float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } + float getFloat(); private: QIODevice* _device; diff --git a/libraries/gl/CMakeLists.txt b/libraries/gl/CMakeLists.txt index fd3197410b..9fc7a0c10f 100644 --- a/libraries/gl/CMakeLists.txt +++ b/libraries/gl/CMakeLists.txt @@ -1,9 +1,5 @@ set(TARGET_NAME gl) setup_hifi_library(OpenGL Qml Quick) -link_hifi_libraries(shared networking) - +link_hifi_libraries(shared) target_opengl() -if (NOT ANDROID) - target_glew() -endif () diff --git a/libraries/gl/src/gl/Config.cpp b/libraries/gl/src/gl/Config.cpp new file mode 100644 index 0000000000..1f29fe21b1 --- /dev/null +++ b/libraries/gl/src/gl/Config.cpp @@ -0,0 +1,35 @@ +// +// GPUConfig.h +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 12/4/14. +// Copyright 2013 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 "Config.h" + +#include + +#if defined(Q_OS_ANDROID) +PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT = NULL; +PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT = NULL; +PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT = NULL; +#endif + +void gl::initModuleGl() { + static std::once_flag once; + std::call_once(once, [] { +#if defined(Q_OS_ANDROID) + glQueryCounterEXT = (PFNGLQUERYCOUNTEREXTPROC)eglGetProcAddress("glQueryCounterEXT"); + glGetQueryObjectui64vEXT = (PFNGLGETQUERYOBJECTUI64VEXTPROC)eglGetProcAddress("glGetQueryObjectui64vEXT"); + glFramebufferTextureEXT = (PFNGLFRAMEBUFFERTEXTUREEXTPROC)eglGetProcAddress("glFramebufferTextureEXT"); +#else + glewExperimental = true; + glewInit(); +#endif + }); +} + diff --git a/libraries/gl/src/gl/Config.h b/libraries/gl/src/gl/Config.h index 9efae96f2a..ff282a1ca0 100644 --- a/libraries/gl/src/gl/Config.h +++ b/libraries/gl/src/gl/Config.h @@ -12,25 +12,67 @@ #ifndef hifi_gpu_GPUConfig_h #define hifi_gpu_GPUConfig_h -#define GL_GLEXT_PROTOTYPES 1 +#include +#if defined(QT_OPENGL_ES_3_1) +// Minimum GL ES version required is 3.1 +#define GL_MIN_VERSION_MAJOR 0x03 +#define GL_MIN_VERSION_MINOR 0x01 +#define GL_DEFAULT_VERSION_MAJOR GL_MIN_VERSION_MAJOR +#define GL_DEFAULT_VERSION_MINOR GL_MIN_VERSION_MINOR +#else +// Minimum desktop GL version required is 4.1 +#define GL_MIN_VERSION_MAJOR 0x04 +#define GL_MIN_VERSION_MINOR 0x01 +#define GL_DEFAULT_VERSION_MAJOR 0x04 +#define GL_DEFAULT_VERSION_MINOR 0x05 +#endif + +#define MINIMUM_GL_VERSION ((GL_MIN_VERSION_MAJOR << 8) | GL_MIN_VERSION_MINOR) + +#if defined(Q_OS_ANDROID) + +#include +#include + +#define GL_DEPTH_COMPONENT32_OES 0x81A7 +#define GL_TIME_ELAPSED_EXT 0x88BF +#define GL_TIMESTAMP_EXT 0x8E28 +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_TEXTURE_BORDER_COLOR_EXT 0x1004 +#define GL_CLAMP_TO_BORDER_EXT 0x812D +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + + +// Add some additional extensions missing from GLES 3.1 +extern "C" { + typedef void (GL_APIENTRYP PFNGLQUERYCOUNTEREXTPROC) (GLuint id, GLenum target); + typedef void (GL_APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64 *params); + typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); + extern PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT; + extern PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT; + extern PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT; +} + +#else // !defined(Q_OS_ANDROID) + +#define GL_GLEXT_PROTOTYPES 1 #include -#if defined(__APPLE__) - +#if defined(Q_OS_DARWIN) #include #include #include - -#endif - -#if defined(WIN32) - +#elif defined(Q_OS_WIN64) #include - -// Uncomment this define and recompile to be able to avoid code path preventing to be able to run nsight graphics debug -//#define HIFI_ENABLE_NSIGHT_DEBUG 1 - #endif +#endif // !defined(Q_OS_ANDROID) + +// Platform specific code to load the GL functions +namespace gl { + void initModuleGl(); +} + #endif // hifi_gpu_GPUConfig_h diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 28982703dd..ed0594135a 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -28,6 +28,13 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { static QSurfaceFormat format; static std::once_flag once; std::call_once(once, [] { +#if defined(QT_OPENGL_ES_3_1) + format.setRenderableType(QSurfaceFormat::OpenGLES); + format.setRedBufferSize(8); + format.setGreenBufferSize(8); + format.setBlueBufferSize(8); + format.setAlphaBufferSize(8); +#endif // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index 84229b97d2..80fc2c5f70 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -25,7 +25,12 @@ class QSurfaceFormat; class QGLFormat; template -void setGLFormatVersion(F& format, int major = 4, int minor = 5) { format.setVersion(major, minor); } +#if defined(QT_OPENGL_ES_3_1) +void setGLFormatVersion(F& format, int major = 3, int minor = 1) +#else +void setGLFormatVersion(F& format, int major = 4, int minor = 5) +#endif + { format.setVersion(major, minor); } size_t evalGLFormatSwapchainPixelSize(const QSurfaceFormat& format); diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 3f1d629638..b974564705 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -59,7 +59,7 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { bool OffscreenGLCanvas::makeCurrent() { bool result = _context->makeCurrent(_offscreenSurface); - std::call_once(_reportOnce, [this]{ + std::call_once(_reportOnce, []{ qCDebug(glLogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); qCDebug(glLogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); qCDebug(glLogging) << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR)); diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp index f24a9bb932..771a8b9a75 100644 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ b/libraries/gl/src/gl/OpenGLVersionChecker.cpp @@ -21,9 +21,6 @@ #include "GLHelpers.h" -// Minimum gl version required is 4.1 -#define MINIMUM_GL_VERSION 0x0401 - OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) : QApplication(argc, argv) { diff --git a/libraries/gpu-gl/CMakeLists.txt b/libraries/gpu-gl/CMakeLists.txt index 65130d6d07..dc744e73f2 100644 --- a/libraries/gpu-gl/CMakeLists.txt +++ b/libraries/gpu-gl/CMakeLists.txt @@ -5,10 +5,5 @@ if (UNIX) target_link_libraries(${TARGET_NAME} pthread) endif(UNIX) GroupSources("src") - target_opengl() -target_nsight() -if (NOT ANDROID) - target_glew() -endif () diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp index b5a8dcb7a9..ff9ddaae63 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp @@ -7,9 +7,9 @@ // #include "GL41Backend.h" #include "../gl/GLShader.h" -//#include using namespace gpu; +using namespace gpu::gl; using namespace gpu::gl41; // GLSL version @@ -84,7 +84,7 @@ int GL41Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::Binding return ssboCount; } -void GL41Backend::makeProgramBindings(gl::ShaderObject& shaderObject) { +void GL41Backend::makeProgramBindings(ShaderObject& shaderObject) { if (!shaderObject.glprogram) { return; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp index 8a5e8d0064..c2490524ae 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp @@ -10,6 +10,7 @@ //#include using namespace gpu; +using namespace gpu::gl; using namespace gpu::gl45; // GLSL version @@ -132,7 +133,7 @@ int GL45Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::Binding return ssboCount;*/ } -void GL45Backend::makeProgramBindings(gl::ShaderObject& shaderObject) { +void GL45Backend::makeProgramBindings(ShaderObject& shaderObject) { if (!shaderObject.glprogram) { return; } diff --git a/libraries/gpu-gles/CMakeLists.txt b/libraries/gpu-gles/CMakeLists.txt new file mode 100644 index 0000000000..55ec53b184 --- /dev/null +++ b/libraries/gpu-gles/CMakeLists.txt @@ -0,0 +1,11 @@ +set(TARGET_NAME gpu-gles) +setup_hifi_library(OpenGL) +link_hifi_libraries(shared gl gpu) +GroupSources("src") + +target_opengl() +target_nsight() + +if (NOT ANDROID) + target_glew() +endif () diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp new file mode 100644 index 0000000000..1d66618703 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp @@ -0,0 +1,722 @@ +// +// GLBackend.cpp +// libraries/gpu-gl-android/src/gpu/gl +// +// Created by Cristian Duarte & Gabriel Calero on 9/21/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" + +#include +#include +#include +#include +#include + +#include "../gles/GLESBackend.h" + +#if defined(NSIGHT_FOUND) +#include "nvToolsExt.h" +#endif + +#include +#include +#include + +#include "GLTexture.h" +#include "GLShader.h" +using namespace gpu; +using namespace gpu::gl; + +static GLBackend* INSTANCE{ nullptr }; +static const char* GL_BACKEND_PROPERTY_NAME = "com.highfidelity.gl.backend"; + +BackendPointer GLBackend::createBackend() { + // FIXME provide a mechanism to override the backend for testing + // Where the gpuContext is initialized and where the TRUE Backend is created and assigned + auto version = QOpenGLContextWrapper::currentContextVersion(); + std::shared_ptr result; + + qDebug() << "Using OpenGL ES backend"; + result = std::make_shared(); + + result->initInput(); + result->initTransform(); + + INSTANCE = result.get(); + void* voidInstance = &(*result); + qApp->setProperty(GL_BACKEND_PROPERTY_NAME, QVariant::fromValue(voidInstance)); + + gl::GLTexture::initTextureTransferHelper(); + return result; +} + +GLBackend& getBackend() { + if (!INSTANCE) { + INSTANCE = static_cast(qApp->property(GL_BACKEND_PROPERTY_NAME).value()); + } + return *INSTANCE; +} + +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) { + return GLShader::makeProgram(getBackend(), shader, slotBindings); +} + +std::array commandNames = { + {QString("draw"),QString("drawIndexed"),QString("drawInstanced"),QString("drawIndexedInstanced"),QString("multiDrawIndirect"),QString("multiDrawIndexedIndirect"),QString("setInputFormat"),QString("setInputBuffer"),QString("setIndexBuffer"),QString("setIndirectBuffer"),QString("setModelTransform"),QString("setViewTransform"),QString("setProjectionTransform"),QString("setViewportTransform"),QString("setDepthRangeTransform"),QString("setPipeline"),QString("setStateBlendFactor"),QString("setStateScissorRect"),QString("setUniformBuffer"),QString("setResourceTexture"),QString("setFramebuffer"),QString("clearFramebuffer"),QString("blit"),QString("generateTextureMips"),QString("beginQuery"),QString("endQuery"),QString("getQuery"),QString("resetStages"),QString("runLambda"),QString("startNamedCall"),QString("stopNamedCall"),QString("glUniform1i"),QString("glUniform1f"),QString("glUniform2f"),QString("glUniform3f"),QString("glUniform4f"),QString("glUniform3fv"),QString("glUniform4fv"),QString("glUniform4iv"),QString("glUniformMatrix3fv"),QString("glUniformMatrix4fv"),QString("glColor4f"),QString("pushProfileRange"),QString("popProfileRange"),QString("NUM_COMMANDS")} +}; + +GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = +{ + (&::gpu::gl::GLBackend::do_draw), + (&::gpu::gl::GLBackend::do_drawIndexed), + (&::gpu::gl::GLBackend::do_drawInstanced), + (&::gpu::gl::GLBackend::do_drawIndexedInstanced), + (&::gpu::gl::GLBackend::do_multiDrawIndirect), + (&::gpu::gl::GLBackend::do_multiDrawIndexedIndirect), + + (&::gpu::gl::GLBackend::do_setInputFormat), + (&::gpu::gl::GLBackend::do_setInputBuffer), + (&::gpu::gl::GLBackend::do_setIndexBuffer), + (&::gpu::gl::GLBackend::do_setIndirectBuffer), + + (&::gpu::gl::GLBackend::do_setModelTransform), + (&::gpu::gl::GLBackend::do_setViewTransform), + (&::gpu::gl::GLBackend::do_setProjectionTransform), + (&::gpu::gl::GLBackend::do_setViewportTransform), + (&::gpu::gl::GLBackend::do_setDepthRangeTransform), + + (&::gpu::gl::GLBackend::do_setPipeline), + (&::gpu::gl::GLBackend::do_setStateBlendFactor), + (&::gpu::gl::GLBackend::do_setStateScissorRect), + + (&::gpu::gl::GLBackend::do_setUniformBuffer), + (&::gpu::gl::GLBackend::do_setResourceTexture), + + (&::gpu::gl::GLBackend::do_setFramebuffer), + (&::gpu::gl::GLBackend::do_clearFramebuffer), + (&::gpu::gl::GLBackend::do_blit), + (&::gpu::gl::GLBackend::do_generateTextureMips), + + (&::gpu::gl::GLBackend::do_beginQuery), + (&::gpu::gl::GLBackend::do_endQuery), + (&::gpu::gl::GLBackend::do_getQuery), + + (&::gpu::gl::GLBackend::do_resetStages), + + (&::gpu::gl::GLBackend::do_runLambda), + + (&::gpu::gl::GLBackend::do_startNamedCall), + (&::gpu::gl::GLBackend::do_stopNamedCall), + + (&::gpu::gl::GLBackend::do_glUniform1i), + (&::gpu::gl::GLBackend::do_glUniform1f), + (&::gpu::gl::GLBackend::do_glUniform2f), + (&::gpu::gl::GLBackend::do_glUniform3f), + (&::gpu::gl::GLBackend::do_glUniform4f), + (&::gpu::gl::GLBackend::do_glUniform3fv), + (&::gpu::gl::GLBackend::do_glUniform4fv), + (&::gpu::gl::GLBackend::do_glUniform4iv), + (&::gpu::gl::GLBackend::do_glUniformMatrix3fv), + (&::gpu::gl::GLBackend::do_glUniformMatrix4fv), + + (&::gpu::gl::GLBackend::do_glColor4f), + + (&::gpu::gl::GLBackend::do_pushProfileRange), + (&::gpu::gl::GLBackend::do_popProfileRange), +}; + +void GLBackend::init() { + static std::once_flag once; + std::call_once(once, [] { + QString vendor{ (const char*)glGetString(GL_VENDOR) }; + QString renderer{ (const char*)glGetString(GL_RENDERER) }; + qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); + qCDebug(gpugllogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); + qCDebug(gpugllogging) << "GL Vendor: " << vendor; + qCDebug(gpugllogging) << "GL Renderer: " << renderer; + GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer); + // From here on, GPUIdent::getInstance()->getMumble() should efficiently give the same answers. + qCDebug(gpugllogging) << "GPU:"; + 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)); + */ + + }); +} + +GLBackend::GLBackend() { + _pipeline._cameraCorrectionBuffer._buffer->flush(); + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment); +} + + +GLBackend::~GLBackend() { + resetStages(); + + killInput(); + killTransform(); +} + +void GLBackend::renderPassTransfer(const Batch& batch) { + const size_t numCommands = batch.getCommands().size(); + const Batch::Commands::value_type* command = batch.getCommands().data(); + const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); + + _inRenderTransferPass = true; + { // Sync all the buffers + ANDROID_PROFILE(render, "syncGPUBuffer", 0xffaaffaa, 1) + + for (auto& cached : batch._buffers._items) { + if (cached._data) { + syncGPUObject(*cached._data); + } + } + } + + { // Sync all the buffers + ANDROID_PROFILE(render, "syncCPUTransform", 0xffaaaaff, 1) + _transform._cameras.clear(); + _transform._cameraOffsets.clear(); + + for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { + switch (*command) { + case Batch::COMMAND_draw: + case Batch::COMMAND_drawIndexed: + case Batch::COMMAND_drawInstanced: + case Batch::COMMAND_drawIndexedInstanced: + case Batch::COMMAND_multiDrawIndirect: + case Batch::COMMAND_multiDrawIndexedIndirect: + _transform.preUpdate(_commandIndex, _stereo); + break; + + case Batch::COMMAND_setViewportTransform: + case Batch::COMMAND_setViewTransform: + case Batch::COMMAND_setProjectionTransform: { + ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xffeeaaff, 1) + CommandCall call = _commandCalls[(*command)]; + (this->*(call))(batch, *offset); + break; + } + + default: + break; + } + command++; + offset++; + } + } + + { // Sync the transform buffers + //PROFILE_RANGE(render_gpu_gl, "transferTransformState"); + ANDROID_PROFILE(render, "transferTransformState", 0xff0000ff, 1) + transferTransformState(batch); + } + + _inRenderTransferPass = false; +} + +void GLBackend::renderPassDraw(const Batch& batch) { + _currentDraw = -1; + _transform._camerasItr = _transform._cameraOffsets.begin(); + const size_t numCommands = batch.getCommands().size(); + const Batch::Commands::value_type* command = batch.getCommands().data(); + const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); + for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { + switch (*command) { + // Ignore these commands on this pass, taken care of in the transfer pass + // Note we allow COMMAND_setViewportTransform to occur in both passes + // as it both updates the transform object (and thus the uniforms in the + // UBO) as well as executes the actual viewport call + case Batch::COMMAND_setModelTransform: + case Batch::COMMAND_setViewTransform: + case Batch::COMMAND_setProjectionTransform: + break; + + case Batch::COMMAND_draw: + case Batch::COMMAND_drawIndexed: + case Batch::COMMAND_drawInstanced: + case Batch::COMMAND_drawIndexedInstanced: + case Batch::COMMAND_multiDrawIndirect: + case Batch::COMMAND_multiDrawIndexedIndirect: { + // updates for draw calls + ++_currentDraw; + updateInput(); + updateTransform(batch); + updatePipeline(); + {ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xff0000ff, 1) + CommandCall call = _commandCalls[(*command)]; + (this->*(call))(batch, *offset); + } + break; + } + default: { + ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xffff00ff, 1) + CommandCall call = _commandCalls[(*command)]; + (this->*(call))(batch, *offset); + break; + } + } + + command++; + offset++; + } +} + +void GLBackend::render(const Batch& batch) { + ANDROID_PROFILE(render, "GLBackendRender", 0xffff00ff, 1) + _transform._skybox = _stereo._skybox = batch.isSkyboxEnabled(); + // Allow the batch to override the rendering stereo settings + // for things like full framebuffer copy operations (deferred lighting passes) + bool savedStereo = _stereo._enable; + if (!batch.isStereoEnabled()) { + _stereo._enable = false; + } + + { + //PROFILE_RANGE(render_gpu_gl, "Transfer"); + ANDROID_PROFILE(render, "Transfer", 0xff0000ff, 1) + renderPassTransfer(batch); + } + + { + //PROFILE_RANGE(render_gpu_gl, _stereo._enable ? "Render Stereo" : "Render"); + ANDROID_PROFILE(render, "RenderPassDraw", 0xff00ddff, 1) + renderPassDraw(batch); + } + + // Restore the saved stereo state for the next batch + _stereo._enable = savedStereo; +} + + +void GLBackend::syncCache() { + syncTransformStateCache(); + syncPipelineStateCache(); + syncInputStateCache(); + syncOutputStateCache(); + + //glEnable(GL_LINE_SMOOTH); + qDebug() << "TODO: GLBackend.cpp:syncCache GL_LINE_SMOOTH"; +} + +void GLBackend::setupStereoSide(int side) { + ivec4 vp = _transform._viewport; + vp.z /= 2; + glViewport(vp.x + side * vp.z, vp.y, vp.z, vp.w); + +#ifdef GPU_STEREO_CAMERA_BUFFER +#ifdef GPU_STEREO_DRAWCALL_DOUBLED + //glVertexAttribI1i(14, side); + glVertexAttribI4i(14, side, 0, 0, 0); + +#endif +#else + _transform.bindCurrentCamera(side); +#endif +} + +void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { + resetStages(); +} + +void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) { + std::function f = batch._lambdas.get(batch._params[paramOffset]._uint); + f(); +} + +void GLBackend::do_startNamedCall(const Batch& batch, size_t paramOffset) { + batch._currentNamedCall = batch._names.get(batch._params[paramOffset]._uint); + _currentDraw = -1; +} + +void GLBackend::do_stopNamedCall(const Batch& batch, size_t paramOffset) { + batch._currentNamedCall.clear(); +} + +void GLBackend::resetStages() { + resetInputStage(); + resetPipelineStage(); + resetTransformStage(); + resetUniformStage(); + resetResourceStage(); + resetOutputStage(); + resetQueryStage(); + + (void) CHECK_GL_ERROR(); +} + + +void GLBackend::do_pushProfileRange(const Batch& batch, size_t paramOffset) { + auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); + profileRanges.push_back(name); +#if defined(NSIGHT_FOUND) + nvtxRangePush(name.c_str()); +#endif +} + +void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) { + profileRanges.pop_back(); +#if defined(NSIGHT_FOUND) + nvtxRangePop(); +#endif +} + +// 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 + +// As long as we don;t use several versions of shaders we can avoid this more complex code path +// #define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, isStereo()); +#define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc + +void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + + glUniform1f( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int), + batch._params[paramOffset + 0]._int); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform1f(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + + glUniform1f( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int), + batch._params[paramOffset + 0]._float); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform2f(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform2f( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform3f(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform3f( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform4f(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform4f( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 4]._int), + batch._params[paramOffset + 3]._float, + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform3fv(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform3fv( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), + batch._params[paramOffset + 1]._uint, + (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); + + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform4fv(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + + GLint location = GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int); + GLsizei count = batch._params[paramOffset + 1]._uint; + const GLfloat* value = (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint); + glUniform4fv(location, count, value); + + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniform4iv(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform4iv( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), + batch._params[paramOffset + 1]._uint, + (const GLint*)batch.readData(batch._params[paramOffset + 0]._uint)); + + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + + glUniformMatrix3fv( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), + batch._params[paramOffset + 2]._uint, + batch._params[paramOffset + 1]._uint, + (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + + glUniformMatrix4fv( + GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), + batch._params[paramOffset + 2]._uint, + batch._params[paramOffset + 1]._uint, + (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { + + glm::vec4 newColor( + batch._params[paramOffset + 3]._float, + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + + if (_input._colorAttribute != newColor) { + _input._colorAttribute = newColor; + glVertexAttrib4fv(gpu::Stream::COLOR, &_input._colorAttribute.r); + } + (void)CHECK_GL_ERROR(); +} + +void GLBackend::releaseBuffer(GLuint id, Size size) const { + Lock lock(_trashMutex); + _buffersTrash.push_back({ id, size }); +} + +void GLBackend::releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const { + Lock lock(_trashMutex); + _externalTexturesTrash.push_back({ id, recycler }); +} + +void GLBackend::releaseTexture(GLuint id, Size size) const { + Lock lock(_trashMutex); + _texturesTrash.push_back({ id, size }); +} + +void GLBackend::releaseFramebuffer(GLuint id) const { + Lock lock(_trashMutex); + _framebuffersTrash.push_back(id); +} + +void GLBackend::releaseShader(GLuint id) const { + Lock lock(_trashMutex); + _shadersTrash.push_back(id); +} + +void GLBackend::releaseProgram(GLuint id) const { + Lock lock(_trashMutex); + _programsTrash.push_back(id); +} + +void GLBackend::releaseQuery(GLuint id) const { + Lock lock(_trashMutex); + _queriesTrash.push_back(id); +} + +void GLBackend::queueLambda(const std::function lambda) const { + Lock lock(_trashMutex); + _lambdaQueue.push_back(lambda); +} + +void GLBackend::recycle() const { + { + std::list> lamdbasTrash; + { + Lock lock(_trashMutex); + std::swap(_lambdaQueue, lamdbasTrash); + } + for (auto lambda : lamdbasTrash) { + lambda(); + } + } + + { + std::vector ids; + std::list> buffersTrash; + { + Lock lock(_trashMutex); + std::swap(_buffersTrash, buffersTrash); + } + ids.reserve(buffersTrash.size()); + for (auto pair : buffersTrash) { + ids.push_back(pair.first); + } + if (!ids.empty()) { + glDeleteBuffers((GLsizei)ids.size(), ids.data()); + } + } + + { + std::vector ids; + std::list framebuffersTrash; + { + Lock lock(_trashMutex); + std::swap(_framebuffersTrash, framebuffersTrash); + } + ids.reserve(framebuffersTrash.size()); + for (auto id : framebuffersTrash) { + ids.push_back(id); + } + if (!ids.empty()) { + glDeleteFramebuffers((GLsizei)ids.size(), ids.data()); + } + } + + { + std::vector ids; + std::list> texturesTrash; + { + Lock lock(_trashMutex); + std::swap(_texturesTrash, texturesTrash); + } + ids.reserve(texturesTrash.size()); + for (auto pair : texturesTrash) { + ids.push_back(pair.first); + } + if (!ids.empty()) { + glDeleteTextures((GLsizei)ids.size(), ids.data()); + } + } + + { + std::list> externalTexturesTrash; + { + Lock lock(_trashMutex); + std::swap(_externalTexturesTrash, externalTexturesTrash); + } + if (!externalTexturesTrash.empty()) { + std::vector fences; + fences.resize(externalTexturesTrash.size()); + for (size_t i = 0; i < externalTexturesTrash.size(); ++i) { + fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + // External texture fences will be read in another thread/context, so we need a flush + glFlush(); + size_t index = 0; + for (auto pair : externalTexturesTrash) { + auto fence = fences[index++]; + pair.second(pair.first, fence); + } + } + } + + { + std::list programsTrash; + { + Lock lock(_trashMutex); + std::swap(_programsTrash, programsTrash); + } + for (auto id : programsTrash) { + glDeleteProgram(id); + } + } + + { + std::list shadersTrash; + { + Lock lock(_trashMutex); + std::swap(_shadersTrash, shadersTrash); + } + for (auto id : shadersTrash) { + glDeleteShader(id); + } + } + + { + std::vector ids; + std::list queriesTrash; + { + Lock lock(_trashMutex); + std::swap(_queriesTrash, queriesTrash); + } + ids.reserve(queriesTrash.size()); + for (auto id : queriesTrash) { + ids.push_back(id); + } + if (!ids.empty()) { + glDeleteQueries((GLsizei)ids.size(), ids.data()); + } + } + +#ifndef THREADED_TEXTURE_TRANSFER + gl::GLTexture::_textureTransferHelper->process(); +#endif +} + +void GLBackend::setCameraCorrection(const Mat4& correction) { + _transform._correction.correction = correction; + _transform._correction.correctionInverse = glm::inverse(correction); + _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); + _pipeline._cameraCorrectionBuffer._buffer->flush(); +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.h b/libraries/gpu-gles/src/gpu/gl/GLBackend.h new file mode 100644 index 0000000000..f8f307bc17 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.h @@ -0,0 +1,427 @@ +// +// Created by Cristian Duarte & Gabriel Calero on 09/21/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gles_Backend_h +#define hifi_gpu_gles_Backend_h + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "GLShared.h" + + +// Different versions for the stereo drawcall +// Current preferred is "instanced" which draw the shape twice but instanced and rely on clipping plane to draw left/right side only +//#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER +//#define GPU_STEREO_TECHNIQUE_INSTANCED + +#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER +#define GPU_STEREO_DRAWCALL_DOUBLED +#define GPU_STEREO_CAMERA_BUFFER +#endif + +#ifdef GPU_STEREO_TECHNIQUE_INSTANCED +#define GPU_STEREO_DRAWCALL_INSTANCED +#define GPU_STEREO_CAMERA_BUFFER +#endif + +//#define ANDROID_INTENSIVE_INSTRUMENTATION 1 + +#ifdef ANDROID_INTENSIVE_INSTRUMENTATION +#define ANDROID_PROFILE_COMMAND(category, commandIndex, argbColor, payload, ...) PROFILE_RANGE_EX(category, commandNames[commandIndex], argbColor, payload, ##__VA_ARGS__); +#define ANDROID_PROFILE(category, name, argbColor, payload, ...) PROFILE_RANGE_EX(category, name, argbColor, payload, ##__VA_ARGS__); +#else +#define ANDROID_PROFILE_COMMAND(category, commandIndex, argbColor, payload, ...) +#define ANDROID_PROFILE(category, name, argbColor, payload, ...) +#endif +namespace gpu { namespace gl { + + class GLBackend : public Backend, public std::enable_shared_from_this { + // Context Backend static interface required + friend class gpu::Context; + static void init(); + static BackendPointer createBackend(); + + protected: + explicit GLBackend(bool syncCache); + GLBackend(); + public: + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); + + ~GLBackend(); + + 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 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 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; } + + // 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; + + // 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; + + // Resource Stage + virtual void do_setResourceTexture(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; + + // 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; + + virtual void do_runLambda(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; + + 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_pushProfileRange(const Batch& batch, size_t paramOffset) final; + virtual void do_popProfileRange(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 void do_glColor4f(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; + + 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 releaseBuffer(GLuint id, Size size) const; + virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const; + virtual void releaseTexture(GLuint id, Size size) const; + virtual void releaseFramebuffer(GLuint id) const; + virtual void releaseShader(GLuint id) const; + virtual void releaseProgram(GLuint id) const; + virtual void releaseQuery(GLuint id) const; + virtual void queueLambda(const std::function lambda) const; + + bool isTextureManagementSparseEnabled() const override { return (_textureManagement._sparseCapable && Texture::getEnableSparseTextures()); } + + protected: + + 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; + + static const size_t INVALID_OFFSET = (size_t)-1; + bool _inRenderTransferPass { false }; + int32_t _uboAlignment { 0 }; + int _currentDraw { -1 }; + + std::list profileRanges; + mutable Mutex _trashMutex; + mutable std::list> _buffersTrash; + mutable std::list> _texturesTrash; + mutable std::list> _externalTexturesTrash; + mutable std::list _framebuffersTrash; + mutable std::list _shadersTrash; + mutable std::list _programsTrash; + mutable std::list _queriesTrash; + mutable std::list> _lambdaQueue; + + void renderPassTransfer(const Batch& batch); + void renderPassDraw(const Batch& batch); + void setupStereoSide(int side); + + virtual void initInput() final; + virtual void killInput() final; + virtual void syncInputStateCache() final; + virtual void resetInputStage(); + virtual void updateInput(); + + struct InputStageState { + bool _invalidFormat { true }; + Stream::FormatPointer _format; + std::string _formatKey; + + typedef std::bitset ActivationCache; + ActivationCache _attributeActivation { 0 }; + + typedef std::bitset BuffersState; + + BuffersState _invalidBuffers{ 0 }; + BuffersState _attribBindingBuffers{ 0 }; + + Buffers _buffers; + Offsets _bufferOffsets; + Offsets _bufferStrides; + std::vector _bufferVBOs; + + glm::vec4 _colorAttribute{ 0.0f }; + + BufferPointer _indexBuffer; + Offset _indexBufferOffset { 0 }; + Type _indexBufferType { UINT32 }; + + BufferPointer _indirectBuffer; + Offset _indirectBufferOffset{ 0 }; + Offset _indirectBufferStride{ 0 }; + + GLuint _defaultVAO { 0 }; + + InputStageState() : + _invalidFormat(true), + _format(0), + _formatKey(), + _attributeActivation(0), + _buffers(_invalidBuffers.size(), BufferPointer(0)), + _bufferOffsets(_invalidBuffers.size(), 0), + _bufferStrides(_invalidBuffers.size(), 0), + _bufferVBOs(_invalidBuffers.size(), 0) {} + } _input; + + virtual void initTransform() = 0; + void killTransform(); + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncTransformStateCache(); + void updateTransform(const Batch& batch); + 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 { + TransformCamera _cams[2]; + + Cameras() {}; + Cameras(const TransformCamera& cam) { memcpy(_cams, &cam, sizeof(TransformCamera)); }; + Cameras(const TransformCamera& camL, const TransformCamera& camR) { memcpy(_cams, &camL, sizeof(TransformCamera)); memcpy(_cams + 1, &camR, sizeof(TransformCamera)); }; + }; + + using CameraBufferElement = Cameras; +#else + using CameraBufferElement = TransformCamera; +#endif + using TransformCameras = std::vector; + + TransformCamera _camera; + TransformCameras _cameras; + + mutable std::map _drawCallInfoOffsets; + + GLuint _objectBuffer { 0 }; + GLuint _cameraBuffer { 0 }; + GLuint _drawCallInfoBuffer { 0 }; + GLuint _objectBufferTexture { 0 }; + size_t _cameraUboSize { 0 }; + bool _viewIsCamera{ false }; + bool _skybox { false }; + Transform _view; + CameraCorrection _correction; + + 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 }; + + using Pair = std::pair; + using List = std::list; + List _cameraOffsets; + mutable List::const_iterator _camerasItr; + mutable size_t _currentCameraOffset{ INVALID_OFFSET }; + + void preUpdate(size_t commandIndex, const StereoState& stereo); + void update(size_t commandIndex, const StereoState& stereo) const; + void bindCurrentCamera(int stereoSide) const; + } _transform; + + virtual void transferTransformState(const Batch& batch) const = 0; + + struct UniformStageState { + std::array _buffers; + //Buffers _buffers { }; + } _uniform; + + void releaseUniformBuffer(uint32_t slot); + void resetUniformStage(); + + // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s + void releaseResourceTexture(uint32_t slot); + + void resetResourceStage(); + + struct ResourceStageState { + std::array _textures; + //Textures _textures { { MAX_NUM_RESOURCE_TEXTURES } }; + int findEmptyTextureSlot() const; + } _resource; + + size_t _commandIndex{ 0 }; + + // 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 PipelineStageState { + PipelinePointer _pipeline; + + GLuint _program { 0 }; + GLint _cameraCorrectionLocation { -1 }; + GLShader* _programShader { nullptr }; + bool _invalidProgram { false }; + + BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; + + State::Data _stateCache{ State::DEFAULT }; + State::Signature _stateSignatureCache { 0 }; + + GLState* _state { nullptr }; + bool _invalidState { false }; + + PipelineStageState() { + _cameraCorrectionBuffer.edit() = CameraCorrection(); + } + } _pipeline; + + // 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; + + 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; + }; + + } } + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp new file mode 100644 index 0000000000..057682584d --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp @@ -0,0 +1,338 @@ +// +// GLBackendInput.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLShared.h" +#include "GLInputFormat.h" + +using namespace gpu; +using namespace gpu::gl; + +void GLBackend::do_setInputFormat(const Batch& batch, size_t paramOffset) { + Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint); + if (format != _input._format) { + _input._format = format; + if (format) { + auto inputFormat = GLInputFormat::sync((*format)); + assert(inputFormat); + if (_input._formatKey != inputFormat->key) { + _input._formatKey = inputFormat->key; + _input._invalidFormat = true; + } + } else { + _input._formatKey.clear(); + _input._invalidFormat = true; + } + } +} + +void GLBackend::do_setInputBuffer(const Batch& batch, size_t paramOffset) { + Offset stride = batch._params[paramOffset + 0]._uint; + Offset offset = batch._params[paramOffset + 1]._uint; + BufferPointer buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); + uint32 channel = batch._params[paramOffset + 3]._uint; + + if (channel < getNumInputBuffers()) { + bool isModified = false; + if (_input._buffers[channel] != buffer) { + _input._buffers[channel] = buffer; + + GLuint vbo = 0; + if (buffer) { + vbo = getBufferID((*buffer)); + } + _input._bufferVBOs[channel] = vbo; + + isModified = true; + } + + if (_input._bufferOffsets[channel] != offset) { + _input._bufferOffsets[channel] = offset; + isModified = true; + } + + if (_input._bufferStrides[channel] != stride) { + _input._bufferStrides[channel] = stride; + isModified = true; + } + + if (isModified) { + _input._invalidBuffers.set(channel); + } + } +} + +void GLBackend::initInput() { + if(!_input._defaultVAO) { + glGenVertexArrays(1, &_input._defaultVAO); + } + qDebug() << "glBindVertexArray(" << _input._defaultVAO << ")"; + glBindVertexArray(_input._defaultVAO); + (void) CHECK_GL_ERROR(); +} + +void GLBackend::killInput() { + qDebug() << "glBindVertexArray(0)"; + glBindVertexArray(0); + if(_input._defaultVAO) { + glDeleteVertexArrays(1, &_input._defaultVAO); + } + (void) CHECK_GL_ERROR(); +} + +void GLBackend::syncInputStateCache() { + for (uint32_t i = 0; i < _input._attributeActivation.size(); i++) { + GLint active = 0; + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &active); + _input._attributeActivation[i] = active; + } + //_input._defaultVAO + qDebug() << "glBindVertexArray("<<_input._defaultVAO<< ")"; + glBindVertexArray(_input._defaultVAO); +} + +void GLBackend::resetInputStage() { + // Reset index buffer + _input._indexBufferType = UINT32; + _input._indexBufferOffset = 0; + _input._indexBuffer.reset(); + //qDebug() << "GLBackend::resetInputStage glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);"; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + (void) CHECK_GL_ERROR(); + + // Reset vertex buffer and format + _input._format.reset(); + _input._formatKey.clear(); + _input._invalidFormat = false; + _input._attributeActivation.reset(); + + for (uint32_t i = 0; i < _input._buffers.size(); i++) { + _input._buffers[i].reset(); + _input._bufferOffsets[i] = 0; + _input._bufferStrides[i] = 0; + _input._bufferVBOs[i] = 0; + } + _input._invalidBuffers.reset(); + + // THe vertex array binding MUST be reset in the specific Backend versions as they use different techniques +} + +void GLBackend::do_setIndexBuffer(const Batch& batch, size_t paramOffset) { + _input._indexBufferType = (Type)batch._params[paramOffset + 2]._uint; + _input._indexBufferOffset = batch._params[paramOffset + 0]._uint; + + BufferPointer indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint); + if (indexBuffer != _input._indexBuffer) { + _input._indexBuffer = indexBuffer; + if (indexBuffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferID(*indexBuffer)); + } else { + // FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null? + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + } + (void) CHECK_GL_ERROR(); +} + +void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) { + _input._indirectBufferOffset = batch._params[paramOffset + 1]._uint; + _input._indirectBufferStride = batch._params[paramOffset + 2]._uint; + + BufferPointer buffer = batch._buffers.get(batch._params[paramOffset]._uint); + if (buffer != _input._indirectBuffer) { + _input._indirectBuffer = buffer; + if (buffer) { + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, getBufferID(*buffer)); + } else { + // FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null? + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); + } + } + + (void)CHECK_GL_ERROR(); +} + + +// Core 41 doesn't expose the features to really separate the vertex format from the vertex buffers binding +// Core 43 does :) +// FIXME crashing problem with glVertexBindingDivisor / glVertexAttribFormat +// Once resolved, break this up into the GL 4.1 and 4.5 backends +#if 1 || (GPU_INPUT_PROFILE == GPU_CORE_41) +#define NO_SUPPORT_VERTEX_ATTRIB_FORMAT +#else +#define SUPPORT_VERTEX_ATTRIB_FORMAT +#endif + +void GLBackend::updateInput() { +#if defined(SUPPORT_VERTEX_ATTRIB_FORMAT) + if (_input._invalidFormat) { + + InputStageState::ActivationCache newActivation; + + // Assign the vertex format required + if (_input._format) { + for (auto& it : _input._format->getAttributes()) { + const Stream::Attribute& attrib = (it).second; + + GLuint slot = attrib._slot; + GLuint count = attrib._element.getLocationScalarCount(); + uint8_t locationCount = attrib._element.getLocationCount(); + GLenum type = _elementTypeToGL41Type[attrib._element.getType()]; + GLuint offset = attrib._offset;; + GLboolean isNormalized = attrib._element.isNormalized(); + + GLenum perLocationSize = attrib._element.getLocationSize(); + + for (size_t locNum = 0; locNum < locationCount; ++locNum) { + newActivation.set(slot + locNum); + glVertexAttribFormat(slot + locNum, count, type, isNormalized, offset + locNum * perLocationSize); + glVertexAttribBinding(slot + locNum, attrib._channel); + } + glVertexBindingDivisor(attrib._channel, attrib._frequency); + } + (void)CHECK_GL_ERROR(); + } + + // Manage Activation what was and what is expected now + for (size_t i = 0; i < newActivation.size(); i++) { + bool newState = newActivation[i]; + if (newState != _input._attributeActivation[i]) { + if (newState) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + } + _input._attributeActivation.flip(i); + } + } + (void)CHECK_GL_ERROR(); + + _input._invalidFormat = false; + _stats._ISNumFormatChanges++; + } + + if (_input._invalidBuffers.any()) { + int numBuffers = _input._buffers.size(); + auto buffer = _input._buffers.data(); + auto vbo = _input._bufferVBOs.data(); + auto offset = _input._bufferOffsets.data(); + auto stride = _input._bufferStrides.data(); + + for (int bufferNum = 0; bufferNum < numBuffers; bufferNum++) { + if (_input._invalidBuffers.test(bufferNum)) { + glBindVertexBuffer(bufferNum, (*vbo), (*offset), (*stride)); + } + buffer++; + vbo++; + offset++; + stride++; + } + _input._invalidBuffers.reset(); + (void)CHECK_GL_ERROR(); + } +#else + if (_input._invalidFormat || _input._invalidBuffers.any()) { + + if (_input._invalidFormat) { + InputStageState::ActivationCache newActivation; + + _stats._ISNumFormatChanges++; + + // Check expected activation + if (_input._format) { + for (auto& it : _input._format->getAttributes()) { + const Stream::Attribute& attrib = (it).second; + uint8_t locationCount = attrib._element.getLocationCount(); + for (int i = 0; i < locationCount; ++i) { + newActivation.set(attrib._slot + i); + } + } + } + + // Manage Activation what was and what is expected now + for (unsigned int i = 0; i < newActivation.size(); i++) { + bool newState = newActivation[i]; + if (newState != _input._attributeActivation[i]) { + + if (newState) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + } + (void)CHECK_GL_ERROR(); + + _input._attributeActivation.flip(i); + } + } + } + + // now we need to bind the buffers and assign the attrib pointers + if (_input._format) { + const Buffers& buffers = _input._buffers; + const Offsets& offsets = _input._bufferOffsets; + const Offsets& strides = _input._bufferStrides; + + const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); + auto& inputChannels = _input._format->getChannels(); + _stats._ISNumInputBufferChanges++; + + GLuint boundVBO = 0; + for (auto& channelIt : inputChannels) { + const Stream::Format::ChannelMap::value_type::second_type& channel = (channelIt).second; + if ((channelIt).first < buffers.size()) { + int bufferNum = (channelIt).first; + + if (_input._invalidBuffers.test(bufferNum) || _input._invalidFormat) { + // GLuint vbo = gpu::GL41Backend::getBufferID((*buffers[bufferNum])); + GLuint vbo = _input._bufferVBOs[bufferNum]; + if (boundVBO != vbo) { + //qDebug() << "GLBackend::updateInput glBindBuffer(GL_ARRAY_BUFFER, " << vbo <<")"; + glBindBuffer(GL_ARRAY_BUFFER, vbo); + (void)CHECK_GL_ERROR(); + boundVBO = vbo; + } + _input._invalidBuffers[bufferNum] = false; + + for (unsigned int i = 0; i < channel._slots.size(); i++) { + const Stream::Attribute& attrib = attributes.at(channel._slots[i]); + GLuint slot = attrib._slot; + GLuint count = attrib._element.getLocationScalarCount(); + uint8_t locationCount = attrib._element.getLocationCount(); + GLenum type = gl::ELEMENT_TYPE_TO_GL[attrib._element.getType()]; + // GLenum perLocationStride = strides[bufferNum]; + GLenum perLocationStride = attrib._element.getLocationSize(); + GLuint stride = (GLuint)strides[bufferNum]; + GLuint pointer = (GLuint)(attrib._offset + offsets[bufferNum]); + GLboolean isNormalized = attrib._element.isNormalized(); + + for (size_t locNum = 0; locNum < locationCount; ++locNum) { + glVertexAttribPointer(slot + (GLuint)locNum, count, type, isNormalized, stride, + reinterpret_cast(pointer + perLocationStride * (GLuint)locNum)); +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency * (isStereo() ? 2 : 1)); +#else + glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency); +#endif + } + + // TODO: Support properly the IAttrib version + + (void)CHECK_GL_ERROR(); + } + } + } + } + } + // everything format related should be in sync now + _input._invalidFormat = false; + } +#endif +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp new file mode 100644 index 0000000000..6fddb810ee --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp @@ -0,0 +1,169 @@ +// +// GLBackendTexture.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 1/19/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLShared.h" +#include "GLFramebuffer.h" + +#include + +using namespace gpu; +using namespace gpu::gl; + +void GLBackend::syncOutputStateCache() { + GLint currentFBO; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO); + + _output._drawFBO = currentFBO; + _output._framebuffer.reset(); +} + +void GLBackend::resetOutputStage() { + if (_output._framebuffer) { + _output._framebuffer.reset(); + _output._drawFBO = 0; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + } + + glEnable(GL_FRAMEBUFFER_SRGB_EXT); +} + +void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) { + auto framebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); + if (_output._framebuffer != framebuffer) { + auto newFBO = getFramebufferID(framebuffer); + if (_output._drawFBO != newFBO) { + _output._drawFBO = newFBO; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, newFBO); + } + _output._framebuffer = framebuffer; + } +} + +void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { + if (_stereo._enable && !_pipeline._stateCache.scissorEnable) { + //qWarning("Clear without scissor in stereo mode"); + } + + uint32 masks = batch._params[paramOffset + 7]._uint; + Vec4 color; + color.x = batch._params[paramOffset + 6]._float; + color.y = batch._params[paramOffset + 5]._float; + color.z = batch._params[paramOffset + 4]._float; + color.w = batch._params[paramOffset + 3]._float; + float depth = batch._params[paramOffset + 2]._float; + int stencil = batch._params[paramOffset + 1]._int; + int useScissor = batch._params[paramOffset + 0]._int; + + GLuint glmask = 0; + if (masks & Framebuffer::BUFFER_STENCIL) { + glClearStencil(stencil); + glmask |= GL_STENCIL_BUFFER_BIT; + // TODO: we will probably need to also check the write mask of stencil like we do + // for depth buffer, but as would say a famous Fez owner "We'll cross that bridge when we come to it" + } + + bool restoreDepthMask = false; + if (masks & Framebuffer::BUFFER_DEPTH) { + glClearDepthf(depth); + + glmask |= GL_DEPTH_BUFFER_BIT; + + bool cacheDepthMask = _pipeline._stateCache.depthTest.getWriteMask(); + if (!cacheDepthMask) { + restoreDepthMask = true; + glDepthMask(GL_TRUE); + } + } + + std::vector drawBuffers; + if (masks & Framebuffer::BUFFER_COLORS) { + if (_output._framebuffer) { + for (unsigned int i = 0; i < Framebuffer::MAX_NUM_RENDER_BUFFERS; i++) { + if (masks & (1 << i)) { + drawBuffers.push_back(GL_COLOR_ATTACHMENT0 + i); + } + } + + if (!drawBuffers.empty()) { + glDrawBuffers((GLsizei)drawBuffers.size(), drawBuffers.data()); + glClearColor(color.x, color.y, color.z, color.w); + glmask |= GL_COLOR_BUFFER_BIT; + + (void) CHECK_GL_ERROR(); + } + } else { + glClearColor(color.x, color.y, color.z, color.w); + glmask |= GL_COLOR_BUFFER_BIT; + } + + // Force the color mask cache to WRITE_ALL if not the case + do_setStateColorWriteMask(State::ColorMask::WRITE_ALL); + } + + // Apply scissor if needed and if not already on + bool doEnableScissor = (useScissor && (!_pipeline._stateCache.scissorEnable)); + if (doEnableScissor) { + glEnable(GL_SCISSOR_TEST); + } + + // Clear! + glClear(glmask); + + // Restore scissor if needed + if (doEnableScissor) { + glDisable(GL_SCISSOR_TEST); + } + + // Restore write mask meaning turn back off + if (restoreDepthMask) { + glDepthMask(GL_FALSE); + } + + // Restore the color draw buffers only if a frmaebuffer is bound + if (_output._framebuffer && !drawBuffers.empty()) { + auto glFramebuffer = syncGPUObject(*_output._framebuffer); + if (glFramebuffer) { + glDrawBuffers((GLsizei)glFramebuffer->_colorBuffers.size(), glFramebuffer->_colorBuffers.data()); + } + } + + (void) CHECK_GL_ERROR(); +} + +void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) { + auto readFBO = getFramebufferID(srcFramebuffer); + if (srcFramebuffer && readFBO) { + if ((srcFramebuffer->getWidth() < (region.x + region.z)) || (srcFramebuffer->getHeight() < (region.y + region.w))) { + qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : srcFramebuffer is too small to provide the region queried"; + return; + } + } + + if ((destImage.width() < region.z) || (destImage.height() < region.w)) { + qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : destImage is too small to receive the region of the framebuffer"; + return; + } + + GLenum format = GL_RGBA; + //GLenum format = GL_BGRA; + qDebug() << "TODO: GLBackendOutput.cpp:do_clearFramebuffer GL_BGRA"; + + if (destImage.format() != QImage::Format_ARGB32) { + qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer"; + return; + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, getFramebufferID(srcFramebuffer)); + glReadPixels(region.x, region.y, region.z, region.w, format, GL_UNSIGNED_BYTE, destImage.bits()); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + (void) CHECK_GL_ERROR(); +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp new file mode 100644 index 0000000000..c35966d440 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp @@ -0,0 +1,250 @@ +// +// GLBackendPipeline.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLShared.h" +#include "GLPipeline.h" +#include "GLShader.h" +#include "GLState.h" +#include "GLBuffer.h" +#include "GLTexture.h" + +using namespace gpu; +using namespace gpu::gl; + +void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { + PipelinePointer pipeline = batch._pipelines.get(batch._params[paramOffset + 0]._uint); + + if (_pipeline._pipeline == pipeline) { + return; + } + + // A true new Pipeline + _stats._PSNumSetPipelines++; + + // null pipeline == reset + if (!pipeline) { + qDebug() << " null pipeline"; + _pipeline._pipeline.reset(); + + _pipeline._program = 0; + _pipeline._cameraCorrectionLocation = -1; + _pipeline._programShader = nullptr; + _pipeline._invalidProgram = true; + + _pipeline._state = nullptr; + _pipeline._invalidState = true; + } else { + auto pipelineObject = GLPipeline::sync(*this, *pipeline); + if (!pipelineObject) { + return; + } + + // check the program cache + // pick the program version + // check the program cache + // pick the program version +#ifdef GPU_STEREO_CAMERA_BUFFER + GLuint glprogram = pipelineObject->_program->getProgram((GLShader::Version) isStereo()); +#else + GLuint glprogram = pipelineObject->_program->getProgram(); +#endif + + if (_pipeline._program != glprogram) { + _pipeline._program = glprogram; + _pipeline._programShader = pipelineObject->_program; + _pipeline._invalidProgram = true; + _pipeline._cameraCorrectionLocation = pipelineObject->_cameraCorrection; + } + + // Now for the state + if (_pipeline._state != pipelineObject->_state) { + _pipeline._state = pipelineObject->_state; + _pipeline._invalidState = true; + } + + // Remember the new pipeline + _pipeline._pipeline = pipeline; + } + + // THis should be done on Pipeline::update... + if (_pipeline._invalidProgram) { + glUseProgram(_pipeline._program); + if (_pipeline._cameraCorrectionLocation != -1) { + auto cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); + glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection)); + } + (void) CHECK_GL_ERROR(); + _pipeline._invalidProgram = false; + } +} + +void GLBackend::updatePipeline() { + if (_pipeline._invalidProgram) { + // doing it here is aproblem for calls to glUniform.... so will do it on assing... + glUseProgram(_pipeline._program); + (void) CHECK_GL_ERROR(); + _pipeline._invalidProgram = false; + } + + if (_pipeline._invalidState) { + if (_pipeline._state) { + // first reset to default what should be + // the fields which were not to default and are default now + resetPipelineState(_pipeline._state->_signature); + + // Update the signature cache with what's going to be touched + _pipeline._stateSignatureCache |= _pipeline._state->_signature; + + // And perform + for (auto command: _pipeline._state->_commands) { + command->run(this); + } + } else { + // No state ? anyway just reset everything + resetPipelineState(0); + } + _pipeline._invalidState = false; + } +} + +void GLBackend::resetPipelineStage() { + // First reset State to default + State::Signature resetSignature(0); + resetPipelineState(resetSignature); + _pipeline._state = nullptr; + _pipeline._invalidState = false; + + // Second the shader side + _pipeline._invalidProgram = false; + _pipeline._program = 0; + _pipeline._programShader = nullptr; + _pipeline._pipeline.reset(); + glUseProgram(0); +} + +void GLBackend::releaseUniformBuffer(uint32_t slot) { + auto& buf = _uniform._buffers[slot]; + if (buf) { + auto* object = Backend::getGPUObject(*buf); + if (object) { + glBindBufferBase(GL_UNIFORM_BUFFER, slot, 0); // RELEASE + (void) CHECK_GL_ERROR(); + } + buf.reset(); + } +} + +void GLBackend::resetUniformStage() { + for (uint32_t i = 0; i < _uniform._buffers.size(); i++) { + releaseUniformBuffer(i); + } +} + +void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { + GLuint slot = batch._params[paramOffset + 3]._uint; + BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); + GLintptr rangeStart = batch._params[paramOffset + 1]._uint; + GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; + + if (!uniformBuffer) { + releaseUniformBuffer(slot); + return; + } + + // check cache before thinking + if (_uniform._buffers[slot] == uniformBuffer) { + return; + } + + // Sync BufferObject + auto* object = syncGPUObject(*uniformBuffer); + if (object) { + glBindBufferRange(GL_UNIFORM_BUFFER, slot, object->_buffer, rangeStart, rangeSize); + + _uniform._buffers[slot] = uniformBuffer; + (void) CHECK_GL_ERROR(); + } else { + releaseUniformBuffer(slot); + return; + } +} + +void GLBackend::releaseResourceTexture(uint32_t slot) { + auto& tex = _resource._textures[slot]; + if (tex) { + auto* object = Backend::getGPUObject(*tex); + if (object) { + GLuint target = object->_target; + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(target, 0); // RELEASE + (void) CHECK_GL_ERROR(); + } + tex.reset(); + } +} + +void GLBackend::resetResourceStage() { + for (uint32_t i = 0; i < _resource._textures.size(); i++) { + releaseResourceTexture(i); + } +} + +void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { + GLuint slot = batch._params[paramOffset + 1]._uint; + if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) { + // "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" + slot + " which doesn't exist. MaxNumResourceTextures = " + getMaxNumResourceTextures()); + return; + } + + TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + + if (!resourceTexture) { + releaseResourceTexture(slot); + return; + } + // check cache before thinking + if (_resource._textures[slot] == resourceTexture) { + return; + } + + // One more True texture bound + _stats._RSNumTextureBounded++; + + // Always make sure the GLObject is in sync + GLTexture* object = syncGPUObject(resourceTexture); + if (object) { + GLuint to = object->_texture; + GLuint target = object->_target; + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(target, to); + + (void) CHECK_GL_ERROR(); + + _resource._textures[slot] = resourceTexture; + + _stats._RSAmountTextureMemoryBounded += object->size(); + + } else { + releaseResourceTexture(slot); + return; + } +} + +int GLBackend::ResourceStageState::findEmptyTextureSlot() const { + // start from the end of the slots, try to find an empty one that can be used + for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) { + if (!_textures[i]) { + return i; + } + } + return -1; +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp new file mode 100644 index 0000000000..530e01d8ff --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp @@ -0,0 +1,93 @@ +// +// GLBackendQuery.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 7/7/2015. +// 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 +// +#include "GLBackend.h" +#include "GLQuery.h" +#include "GLShared.h" + +using namespace gpu; +using namespace gpu::gl; + +// Eventually, we want to test with TIME_ELAPSED instead of TIMESTAMP +#ifdef Q_OS_MAC +const uint32_t MAX_RANGE_QUERY_DEPTH = 1; +static bool timeElapsed = true; +#else +const uint32_t MAX_RANGE_QUERY_DEPTH = 10000; +static bool timeElapsed = false; +#endif + +void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { + auto query = batch._queries.get(batch._params[paramOffset]._uint); + GLQuery* glquery = syncGPUObject(*query); + if (glquery) { + //glGetInteger64v(GL_TIMESTAMP_EXT, (GLint64*)&glquery->_batchElapsedTime); + glquery->_batchElapsedTime = 1; + if (timeElapsed) { + glBeginQuery(GL_TIME_ELAPSED_EXT, glquery->_endqo); + } else { + if (glQueryCounterEXT != NULL) { + glQueryCounterEXT(glquery->_beginqo, GL_TIMESTAMP_EXT); + } + } + glquery->_rangeQueryDepth = _queryStage._rangeQueryDepth; + (void)CHECK_GL_ERROR(); + } +} + +void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { + auto query = batch._queries.get(batch._params[paramOffset]._uint); + GLQuery* glquery = syncGPUObject(*query); + if (glquery) { + if (timeElapsed) { + glEndQuery(GL_TIME_ELAPSED_EXT); + } else { + if (glQueryCounterEXT != NULL) { + glQueryCounterEXT(glquery->_endqo, GL_TIMESTAMP_EXT); + } + } + + --_queryStage._rangeQueryDepth; + GLint64 now; + //glGetInteger64v(GL_TIMESTAMP_EXT, &now); + //glquery->_batchElapsedTime = now - glquery->_batchElapsedTime; + now = 1; + glquery->_batchElapsedTime = 1; + + PROFILE_RANGE_END(render_gpu_gl, glquery->_profileRangeId); + + (void)CHECK_GL_ERROR(); + } +} + +void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { + auto query = batch._queries.get(batch._params[paramOffset]._uint); + if (glGetQueryObjectui64vEXT == NULL) + return; + GLQuery* glquery = syncGPUObject(*query); + if (glquery) { + glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT_AVAILABLE, &glquery->_result); + if (glquery->_result == GL_TRUE) { + if (timeElapsed) { + glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT, &glquery->_result); + } else { + GLuint64 start, end; + glGetQueryObjectui64vEXT(glquery->_beginqo, GL_QUERY_RESULT, &start); + glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT, &end); + glquery->_result = end - start; + } + query->triggerReturnHandler(glquery->_result, glquery->_batchElapsedTime); + } + (void)CHECK_GL_ERROR(); + } +} + +void GLBackend::resetQueryStage() { +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp new file mode 100644 index 0000000000..27b8d23bf3 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp @@ -0,0 +1,334 @@ +// +// GLBackendState.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/22/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLState.h" + +using namespace gpu; +using namespace gpu::gl; + +void GLBackend::resetPipelineState(State::Signature nextSignature) { + auto currentNotSignature = ~_pipeline._stateSignatureCache; + auto nextNotSignature = ~nextSignature; + auto fieldsToBeReset = currentNotSignature ^ (currentNotSignature | nextNotSignature); + if (fieldsToBeReset.any()) { + for (auto i = 0; i < State::NUM_FIELDS; i++) { + if (fieldsToBeReset[i]) { + GLState::_resetStateCommands[i]->run(this); + _pipeline._stateSignatureCache.reset(i); + } + } + } +} + +void GLBackend::syncPipelineStateCache() { + State::Data state; + + //glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + qDebug() << "TODO: GLBackendState.cpp:syncPipelineStateCache GL_TEXTURE_CUBE_MAP_SEAMLESS"; + + // Point size is always on + // FIXME CORE + //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + //glEnable(GL_PROGRAM_POINT_SIZE_EXT); + qDebug() << "TODO: GLBackendState.cpp:syncPipelineStateCache GL_PROGRAM_POINT_SIZE_EXT"; + + //glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + qDebug() << "TODO: GLBackendState.cpp:syncPipelineStateCache GL_VERTEX_PROGRAM_POINT_SIZE"; + + // Default line width accross the board + glLineWidth(1.0f); + + getCurrentGLState(state); + State::Signature signature = State::evalSignature(state); + + _pipeline._stateCache = state; + _pipeline._stateSignatureCache = signature; +} + + +void GLBackend::do_setStateFillMode(int32 mode) { + if (_pipeline._stateCache.fillMode != mode) { + static GLenum GL_FILL_MODES[] = { /*GL_POINT, GL_LINE, GL_FILL*/ }; + qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_POINT"; + qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_LINE"; + qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_FILL"; + //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL_MODES[mode]); + qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode glPolygonMode"; + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.fillMode = State::FillMode(mode); + } +} + +void GLBackend::do_setStateCullMode(int32 mode) { + if (_pipeline._stateCache.cullMode != mode) { + static GLenum GL_CULL_MODES[] = { GL_FRONT_AND_BACK, GL_FRONT, GL_BACK }; + if (mode == State::CULL_NONE) { + glDisable(GL_CULL_FACE); + glCullFace(GL_FRONT_AND_BACK); + } else { + glEnable(GL_CULL_FACE); + glCullFace(GL_CULL_MODES[mode]); + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.cullMode = State::CullMode(mode); + } +} + +void GLBackend::do_setStateFrontFaceClockwise(bool isClockwise) { + if (_pipeline._stateCache.frontFaceClockwise != isClockwise) { + static GLenum GL_FRONT_FACES[] = { GL_CCW, GL_CW }; + glFrontFace(GL_FRONT_FACES[isClockwise]); + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.frontFaceClockwise = isClockwise; + } +} + +void GLBackend::do_setStateDepthClampEnable(bool enable) { + if (_pipeline._stateCache.depthClampEnable != enable) { + if (enable) { + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthClampEnable GL_DEPTH_CLAMP"; + //glEnable(GL_DEPTH_CLAMP); + } else { + //glDisable(GL_DEPTH_CLAMP); + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthClampEnable GL_DEPTH_CLAMP"; + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.depthClampEnable = enable; + } +} + +void GLBackend::do_setStateScissorEnable(bool enable) { + if (_pipeline._stateCache.scissorEnable != enable) { + if (enable) { + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.scissorEnable = enable; + } +} + +void GLBackend::do_setStateMultisampleEnable(bool enable) { + if (_pipeline._stateCache.multisampleEnable != enable) { + if (enable) { + //glEnable(GL_MULTISAMPLE); + qDebug() << "TODO: GLBackendState.cpp:do_setStateMultisampleEnable GL_MULTISAMPLE"; + } else { + //glDisable(GL_MULTISAMPLE); + qDebug() << "TODO: GLBackendState.cpp:do_setStateMultisampleEnable GL_MULTISAMPLE"; + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.multisampleEnable = enable; + } +} + +void GLBackend::do_setStateAntialiasedLineEnable(bool enable) { + if (_pipeline._stateCache.antialisedLineEnable != enable) { + if (enable) { + //glEnable(GL_LINE_SMOOTH); + qDebug() << "TODO: GLBackendState.cpp:do_setStateAntialiasedLineEnable GL_LINE_SMOOTH"; + } else { + //glDisable(GL_LINE_SMOOTH); + qDebug() << "TODO: GLBackendState.cpp:do_setStateAntialiasedLineEnable GL_LINE_SMOOTH"; + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.antialisedLineEnable = enable; + } +} + +void GLBackend::do_setStateDepthBias(Vec2 bias) { + if ((bias.x != _pipeline._stateCache.depthBias) || (bias.y != _pipeline._stateCache.depthBiasSlopeScale)) { + if ((bias.x != 0.0f) || (bias.y != 0.0f)) { + glEnable(GL_POLYGON_OFFSET_FILL); + //glEnable(GL_POLYGON_OFFSET_LINE); + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_LINE"; + //glEnable(GL_POLYGON_OFFSET_POINT); + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_POINT"; + glPolygonOffset(bias.x, bias.y); + } else { + glDisable(GL_POLYGON_OFFSET_FILL); + //glDisable(GL_POLYGON_OFFSET_LINE); + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_LINE"; + //glDisable(GL_POLYGON_OFFSET_POINT); + qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_POINT"; + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.depthBias = bias.x; + _pipeline._stateCache.depthBiasSlopeScale = bias.y; + } +} + +void GLBackend::do_setStateDepthTest(State::DepthTest test) { + const auto& current = _pipeline._stateCache.depthTest; + if (current != test) { + if (test.isEnabled()) { + glEnable(GL_DEPTH_TEST); + } else { + glDisable(GL_DEPTH_TEST); + } + if (test.getWriteMask() != current.getWriteMask()) { + glDepthMask(test.getWriteMask()); + } + if (test.getFunction() != current.getFunction()) { + glDepthFunc(COMPARISON_TO_GL[test.getFunction()]); + } + if (CHECK_GL_ERROR()) { + qDebug() << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled") + << "Mask=" << (test.getWriteMask() ? "Write" : "no Write") + << "Func=" << test.getFunction() + << "Raw=" << test.getRaw(); + } + _pipeline._stateCache.depthTest = test; + } +} + +void GLBackend::do_setStateStencil(State::StencilActivation activation, State::StencilTest testFront, State::StencilTest testBack) { + const auto& currentActivation = _pipeline._stateCache.stencilActivation; + const auto& currentTestFront = _pipeline._stateCache.stencilTestFront; + const auto& currentTestBack = _pipeline._stateCache.stencilTestBack; + if ((currentActivation != activation) + || (currentTestFront != testFront) + || (currentTestBack != testBack)) { + + if (activation.isEnabled()) { + glEnable(GL_STENCIL_TEST); + } else { + glDisable(GL_STENCIL_TEST); + } + + if (activation.getWriteMaskFront() != activation.getWriteMaskBack()) { + glStencilMaskSeparate(GL_FRONT, activation.getWriteMaskFront()); + glStencilMaskSeparate(GL_BACK, activation.getWriteMaskBack()); + } else { + glStencilMask(activation.getWriteMaskFront()); + } + + static GLenum STENCIL_OPS[State::NUM_STENCIL_OPS] = { + GL_KEEP, + GL_ZERO, + GL_REPLACE, + GL_INCR_WRAP, + GL_DECR_WRAP, + GL_INVERT, + GL_INCR, + GL_DECR }; + + if (testFront != testBack) { + glStencilOpSeparate(GL_FRONT, STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getDepthFailOp()], STENCIL_OPS[testFront.getPassOp()]); + glStencilFuncSeparate(GL_FRONT, COMPARISON_TO_GL[testFront.getFunction()], testFront.getReference(), testFront.getReadMask()); + + glStencilOpSeparate(GL_BACK, STENCIL_OPS[testBack.getFailOp()], STENCIL_OPS[testBack.getDepthFailOp()], STENCIL_OPS[testBack.getPassOp()]); + glStencilFuncSeparate(GL_BACK, COMPARISON_TO_GL[testBack.getFunction()], testBack.getReference(), testBack.getReadMask()); + } else { + glStencilOp(STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getDepthFailOp()], STENCIL_OPS[testFront.getPassOp()]); + glStencilFunc(COMPARISON_TO_GL[testFront.getFunction()], testFront.getReference(), testFront.getReadMask()); + } + + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.stencilActivation = activation; + _pipeline._stateCache.stencilTestFront = testFront; + _pipeline._stateCache.stencilTestBack = testBack; + } +} + +void GLBackend::do_setStateAlphaToCoverageEnable(bool enable) { + if (_pipeline._stateCache.alphaToCoverageEnable != enable) { + if (enable) { + glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } else { + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.alphaToCoverageEnable = enable; + } +} + +void GLBackend::do_setStateSampleMask(uint32 mask) { + if (_pipeline._stateCache.sampleMask != mask) { + if (mask == 0xFFFFFFFF) { + glDisable(GL_SAMPLE_MASK); + } else { + glEnable(GL_SAMPLE_MASK); + glSampleMaski(0, mask); + } + (void)CHECK_GL_ERROR(); + _pipeline._stateCache.sampleMask = mask; + } +} + +void GLBackend::do_setStateBlend(State::BlendFunction function) { + if (_pipeline._stateCache.blendFunction != function) { + if (function.isEnabled()) { + glEnable(GL_BLEND); + + glBlendEquationSeparate(BLEND_OPS_TO_GL[function.getOperationColor()], BLEND_OPS_TO_GL[function.getOperationAlpha()]); + (void)CHECK_GL_ERROR(); + + + glBlendFuncSeparate(BLEND_ARGS_TO_GL[function.getSourceColor()], BLEND_ARGS_TO_GL[function.getDestinationColor()], + BLEND_ARGS_TO_GL[function.getSourceAlpha()], BLEND_ARGS_TO_GL[function.getDestinationAlpha()]); + } else { + glDisable(GL_BLEND); + } + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.blendFunction = function; + } +} + +void GLBackend::do_setStateColorWriteMask(uint32 mask) { + if (_pipeline._stateCache.colorWriteMask != mask) { + glColorMask(mask & State::ColorMask::WRITE_RED, + mask & State::ColorMask::WRITE_GREEN, + mask & State::ColorMask::WRITE_BLUE, + mask & State::ColorMask::WRITE_ALPHA); + (void)CHECK_GL_ERROR(); + + _pipeline._stateCache.colorWriteMask = mask; + } +} + + +void GLBackend::do_setStateBlendFactor(const Batch& batch, size_t paramOffset) { + Vec4 factor(batch._params[paramOffset + 0]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 3]._float); + + glBlendColor(factor.x, factor.y, factor.z, factor.w); + (void)CHECK_GL_ERROR(); +} + +void GLBackend::do_setStateScissorRect(const Batch& batch, size_t paramOffset) { + Vec4i rect; + memcpy(&rect, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); + + if (_stereo._enable) { + rect.z /= 2; + if (_stereo._pass) { + rect.x += rect.z; + } + } + glScissor(rect.x, rect.y, rect.z, rect.w); + (void)CHECK_GL_ERROR(); +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp new file mode 100644 index 0000000000..4be7682a4f --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp @@ -0,0 +1,40 @@ +// +// +// GLBackendTexture.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 1/19/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLTexture.h" + +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(); +} + + +void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { + TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + if (!resourceTexture) { + return; + } + + // DO not transfer the texture, this call is expected for rendering texture + GLTexture* object = syncGPUObject(resourceTexture, false); + qDebug() << "GLBackendTexture do_generateTextureMips syncGPUObject"; + if (!object) { + return; + } + + object->generateMips(); +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp new file mode 100644 index 0000000000..3068e24dac --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp @@ -0,0 +1,212 @@ +// +// GLBackendTransform.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +// Transform Stage +void GLBackend::do_setModelTransform(const Batch& batch, size_t paramOffset) { + qDebug() << "do_setModelTransform"; +} + +void GLBackend::do_setViewTransform(const Batch& batch, size_t paramOffset) { + _transform._view = batch._transforms.get(batch._params[paramOffset]._uint); + _transform._viewIsCamera = batch._params[paramOffset + 1]._uint != 0; + _transform._invalidView = true; +} + +void GLBackend::do_setProjectionTransform(const Batch& batch, size_t paramOffset) { + memcpy(&_transform._projection, batch.readData(batch._params[paramOffset]._uint), sizeof(Mat4)); + _transform._invalidProj = true; +} + +void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) { + memcpy(&_transform._viewport, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); + +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + { + ivec4& vp = _transform._viewport; + glViewport(vp.x, vp.y, vp.z, vp.w); + + // Where we assign the GL viewport + if (_stereo._enable) { + vp.z /= 2; + if (_stereo._pass) { + vp.x += vp.z; + } + } + } +#else + if (!_inRenderTransferPass && !isStereo()) { + ivec4& vp = _transform._viewport; + glViewport(vp.x, vp.y, vp.z, vp.w); + } +#endif + + // The Viewport is tagged invalid because the CameraTransformUBO is not up to date and will need update on next drawcall + _transform._invalidViewport = true; +} + +void GLBackend::do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) { + + Vec2 depthRange(batch._params[paramOffset + 1]._float, batch._params[paramOffset + 0]._float); + + if ((depthRange.x != _transform._depthRange.x) || (depthRange.y != _transform._depthRange.y)) { + _transform._depthRange = depthRange; + + glDepthRangef(depthRange.x, depthRange.y); + } +} + +void GLBackend::killTransform() { + glDeleteBuffers(1, &_transform._objectBuffer); + glDeleteBuffers(1, &_transform._cameraBuffer); + glDeleteBuffers(1, &_transform._drawCallInfoBuffer); + glDeleteTextures(1, &_transform._objectBufferTexture); +} + +void GLBackend::syncTransformStateCache() { + _transform._invalidViewport = true; + _transform._invalidProj = true; + _transform._invalidView = true; + + glGetIntegerv(GL_VIEWPORT, (GLint*) &_transform._viewport); + + glGetFloatv(GL_DEPTH_RANGE, (GLfloat*)&_transform._depthRange); + + Mat4 modelView; + auto modelViewInv = glm::inverse(modelView); + _transform._view.evalFromRawMatrix(modelViewInv); + + glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); + _transform._enabledDrawcallInfoBuffer = false; +} + +void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo) { + // Check all the dirty flags and update the state accordingly + if (_invalidViewport) { + _camera._viewport = glm::vec4(_viewport); + } + + if (_invalidProj) { + _camera._projection = _projection; + } + + if (_invalidView) { + // Apply the correction + if (_viewIsCamera && _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); + if (_skybox) { + result.setTranslation(vec3()); + } + _view = result; + } + // This is when the _view matrix gets assigned + _view.getInverseMatrix(_camera._view); + } + + if (_invalidView || _invalidProj || _invalidViewport) { + size_t offset = _cameraUboSize * _cameras.size(); + _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); + + if (stereo._enable) { +#ifdef GPU_STEREO_CAMERA_BUFFER + _cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view))); +#else + _cameras.push_back((_camera.getEyeCamera(0, stereo, _view))); + _cameras.push_back((_camera.getEyeCamera(1, stereo, _view))); +#endif + } else { +#ifdef GPU_STEREO_CAMERA_BUFFER + _cameras.push_back(CameraBufferElement(_camera.recomputeDerived(_view))); +#else + _cameras.push_back((_camera.recomputeDerived(_view))); +#endif + } + } + + // Flags are clean + _invalidView = _invalidProj = _invalidViewport = false; +} + +void GLBackend::TransformStageState::update(size_t commandIndex, const StereoState& stereo) const { + size_t offset = INVALID_OFFSET; + while ((_camerasItr != _cameraOffsets.end()) && (commandIndex >= (*_camerasItr).first)) { + offset = (*_camerasItr).second; + _currentCameraOffset = offset; + ++_camerasItr; + } + + if (offset != INVALID_OFFSET) { +#ifdef GPU_STEREO_CAMERA_BUFFER + bindCurrentCamera(0); +#else + if (!stereo._enable) { + bindCurrentCamera(0); + } +#endif + } + (void)CHECK_GL_ERROR(); +} + +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() { + +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp b/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp new file mode 100644 index 0000000000..4f7d0a8632 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp @@ -0,0 +1,35 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 +// 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 "GLBuffer.h" +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +GLBuffer::~GLBuffer() { + Backend::bufferCount.decrement(); + Backend::bufferGPUMemSize.update(_size, 0); + + if (_id) { + auto backend = _backend.lock(); + if (backend) { + backend->releaseBuffer(_id, _size); + } + } +} + +GLBuffer::GLBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLuint id) : + GLObject(backend, buffer, id), + _size((GLuint)buffer._renderSysmem.getSize()), + _stamp(buffer._renderSysmem.getStamp()) +{ + Backend::bufferCount.increment(); + Backend::bufferGPUMemSize.update(0, _size); +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLBuffer.h b/libraries/gpu-gles/src/gpu/gl/GLBuffer.h new file mode 100644 index 0000000000..182014e764 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBuffer.h @@ -0,0 +1,66 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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_GLBuffer_h +#define hifi_gpu_gl_GLBuffer_h + +#include "GLShared.h" +#include "GLBackend.h" + +namespace gpu { namespace gl { + +class GLBuffer : public GLObject { +public: + template + static GLBufferType* sync(GLBackend& backend, const Buffer& buffer) { + if (buffer.getSysmem().getSize() != 0) { + if (buffer._getUpdateCount == 0) { + qWarning() << "Unsynced buffer"; + } + if (buffer._getUpdateCount < buffer._applyUpdateCount) { + qWarning() << "Unsynced buffer " << buffer._getUpdateCount << " " << buffer._applyUpdateCount; + } + } + GLBufferType* object = Backend::getGPUObject(buffer); + + // Has the storage size changed? + if (!object || object->_stamp != buffer._renderSysmem.getStamp()) { + object = new GLBufferType(backend.shared_from_this(), buffer, object); + } + + if (0 != (buffer._renderPages._flags & PageManager::DIRTY)) { + object->transfer(); + } + + return object; + } + + template + static GLuint getId(GLBackend& backend, const Buffer& buffer) { + GLBuffer* bo = sync(backend, buffer); + if (bo) { + return bo->_buffer; + } else { + return 0; + } + } + + const GLuint& _buffer { _id }; + const GLuint _size; + const Stamp _stamp; + + ~GLBuffer(); + + virtual void transfer() = 0; + +protected: + GLBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLuint id); +}; + +} } + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp new file mode 100644 index 0000000000..150bb2be70 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp @@ -0,0 +1,48 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GLFramebuffer.h" +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +GLFramebuffer::~GLFramebuffer() { + if (_id) { + auto backend = _backend.lock(); + if (backend) { + backend->releaseFramebuffer(_id); + } + } +} + +bool GLFramebuffer::checkStatus(GLenum target) const { + bool result = false; + switch (_status) { + case GL_FRAMEBUFFER_COMPLETE: + // Success ! + result = true; + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT."; + break; +/* TODO: case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER."; + break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER."; + break; */ + case GL_FRAMEBUFFER_UNSUPPORTED: + qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; + break; + } + return result; +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h new file mode 100644 index 0000000000..a2fd0999f3 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h @@ -0,0 +1,77 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLFramebuffer_h +#define hifi_gpu_gl_GLFramebuffer_h + +#include "GLShared.h" +#include "GLBackend.h" + +namespace gpu { namespace gl { + +class GLFramebuffer : public GLObject { +public: + template + static GLFramebufferType* sync(GLBackend& backend, const Framebuffer& framebuffer) { + GLFramebufferType* object = Backend::getGPUObject(framebuffer); + + bool needsUpate { false }; + if (!object || + framebuffer.getDepthStamp() != object->_depthStamp || + framebuffer.getColorStamps() != object->_colorStamps) { + needsUpate = true; + } + + // If GPU object already created and in sync + if (!needsUpate) { + return object; + } else if (framebuffer.isEmpty()) { + // NO framebuffer definition yet so let's avoid thinking + return nullptr; + } + + // need to have a gpu object? + if (!object) { + // All is green, assign the gpuobject to the Framebuffer + object = new GLFramebufferType(backend.shared_from_this(), framebuffer); + Backend::setGPUObject(framebuffer, object); + (void)CHECK_GL_ERROR(); + } + + object->update(); + return object; + } + + template + static GLuint getId(GLBackend& backend, const Framebuffer& framebuffer) { + GLFramebufferType* fbo = sync(backend, framebuffer); + if (fbo) { + return fbo->_id; + } else { + return 0; + } + } + + const GLuint& _fbo { _id }; + std::vector _colorBuffers; + Stamp _depthStamp { 0 }; + std::vector _colorStamps; + +protected: + GLenum _status { GL_FRAMEBUFFER_COMPLETE }; + virtual void update() = 0; + bool checkStatus(GLenum target) const; + + GLFramebuffer(const std::weak_ptr& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {} + ~GLFramebuffer(); + +}; + +} } + + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp b/libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp new file mode 100644 index 0000000000..7f42350c3b --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp @@ -0,0 +1,33 @@ +// +// Created by Sam Gateau on 2016/07/21 +// 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 "GLInputFormat.h" +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + + +GLInputFormat::GLInputFormat() { +} + +GLInputFormat:: ~GLInputFormat() { + +} + +GLInputFormat* GLInputFormat::sync(const Stream::Format& inputFormat) { + GLInputFormat* object = Backend::getGPUObject(inputFormat); + + if (!object) { + object = new GLInputFormat(); + object->key = inputFormat.getKey(); + Backend::setGPUObject(inputFormat, object); + } + + return object; +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLInputFormat.h b/libraries/gpu-gles/src/gpu/gl/GLInputFormat.h new file mode 100644 index 0000000000..a14e3d4d91 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLInputFormat.h @@ -0,0 +1,29 @@ +// +// Created by Sam Gateau on 2016/07/21 +// 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_GLInputFormat_h +#define hifi_gpu_gl_GLInputFormat_h + +#include "GLShared.h" + +namespace gpu { +namespace gl { + +class GLInputFormat : public GPUObject { + public: + static GLInputFormat* sync(const Stream::Format& inputFormat); + + GLInputFormat(); + ~GLInputFormat(); + + std::string key; +}; + +} +} + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp new file mode 100644 index 0000000000..09c09de353 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp @@ -0,0 +1,62 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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 "GLPipeline.h" + +#include "GLShader.h" +#include "GLState.h" + +using namespace gpu; +using namespace gpu::gl; + +GLPipeline* GLPipeline::sync(GLBackend& backend, const Pipeline& pipeline) { + GLPipeline* object = Backend::getGPUObject(pipeline); + + // If GPU object already created then good + if (object) { + return object; + } + + // No object allocated yet, let's see if it's worth it... + ShaderPointer shader = pipeline.getProgram(); + + // If this pipeline's shader has already failed to compile, don't try again + if (shader->compilationHasFailed()) { + return nullptr; + } + + GLShader* programObject = GLShader::sync(backend, *shader); + if (programObject == nullptr) { + shader->setCompilationHasFailed(true); + return nullptr; + } + + StatePointer state = pipeline.getState(); + GLState* stateObject = GLState::sync(*state); + if (stateObject == nullptr) { + return nullptr; + } + + // Program and state are valid, we can create the pipeline object + if (!object) { + object = new GLPipeline(); + Backend::setGPUObject(pipeline, object); + } + + // Special case for view correction matrices, any pipeline that declares the correction buffer + // uniform will automatically have it provided without any client code necessary. + // Required for stable lighting in the HMD. + //CLIMAX_MERGE_START + //getbuffers() doesnt exist anymore.. use get uniformbuffers()? + object->_cameraCorrection = shader->getUniformBuffers().findLocation("cameraCorrectionBuffer"); + //CLIMAX_MERGE_END + object->_program = programObject; + object->_state = stateObject; + + return object; +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLPipeline.h b/libraries/gpu-gles/src/gpu/gl/GLPipeline.h new file mode 100644 index 0000000000..a298f149d9 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLPipeline.h @@ -0,0 +1,29 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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_GLPipeline_h +#define hifi_gpu_gl_GLPipeline_h + +#include "GLShared.h" + +namespace gpu { namespace gl { + +class GLPipeline : public GPUObject { +public: + static GLPipeline* sync(GLBackend& backend, const Pipeline& pipeline); + + GLShader* _program { nullptr }; + GLState* _state { nullptr }; + // Bit of a hack, any pipeline can need the camera correction buffer at execution time, so + // we store whether a given pipeline has declared the uniform buffer for it. + int32 _cameraCorrection { -1 }; +}; + +} } + + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLQuery.h b/libraries/gpu-gles/src/gpu/gl/GLQuery.h new file mode 100644 index 0000000000..23b1f38621 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLQuery.h @@ -0,0 +1,67 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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_GLQuery_h +#define hifi_gpu_gl_GLQuery_h + +#include "GLShared.h" +#include "GLBackend.h" + +namespace gpu { namespace gl { + +class GLQuery : public GLObject { + using Parent = gpu::gl::GLObject; +public: + template + static GLQueryType* sync(GLBackend& backend, const Query& query) { + GLQueryType* object = Backend::getGPUObject(query); + + // need to have a gpu object? + if (!object) { + // All is green, assign the gpuobject to the Query + object = new GLQueryType(backend.shared_from_this(), query); + (void)CHECK_GL_ERROR(); + Backend::setGPUObject(query, object); + } + + return object; + } + + template + static GLuint getId(GLBackend& backend, const QueryPointer& query) { + if (!query) { + return 0; + } + + GLQuery* object = sync(backend, *query); + if (!object) { + return 0; + } + + return object->_endqo; + } + + const GLuint& _endqo = { _id }; + const GLuint _beginqo = { 0 }; + GLuint64 _result { (GLuint64)-1 }; + GLuint64 _batchElapsedTime { (GLuint64) 0 }; + uint64_t _profileRangeId { 0 }; + uint32_t _rangeQueryDepth { 0 }; + +protected: + GLQuery(const std::weak_ptr& backend, const Query& query, GLuint endId, GLuint beginId) : Parent(backend, query, endId), _beginqo(beginId) {} + ~GLQuery() { + if (_id) { + GLuint ids[2] = { _endqo, _beginqo }; + glDeleteQueries(2, ids); + } + } +}; + +} } + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp new file mode 100644 index 0000000000..b728010470 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp @@ -0,0 +1,224 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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 "GLShader.h" +#include + +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +GLShader::GLShader(const std::weak_ptr& backend) : _backend(backend) { +} + +GLShader::~GLShader() { + for (auto& so : _shaderObjects) { + auto backend = _backend.lock(); + if (backend) { + if (so.glshader != 0) { + backend->releaseShader(so.glshader); + } + if (so.glprogram != 0) { + backend->releaseProgram(so.glprogram); + } + } + } +} + +// GLSL version +static const std::string glslVersion { + "#version 310 es" +}; + +// Shader domain +static const size_t NUM_SHADER_DOMAINS = 3; + +// GL Shader type enums +// Must match the order of type specified in gpu::Shader::Type +static const std::array SHADER_DOMAINS { { + GL_VERTEX_SHADER, + GL_FRAGMENT_SHADER, + //GL_GEOMETRY_SHADER, +} }; + +// Domain specific defines +// Must match the order of type specified in gpu::Shader::Type +static const std::array DOMAIN_DEFINES { { + "#define GPU_VERTEX_SHADER", + "#define GPU_PIXEL_SHADER", + "#define GPU_GEOMETRY_SHADER", +} }; + +// Stereo specific defines +static const std::string stereoVersion { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN" +#endif +#ifdef GPU_STEREO_DRAWCALL_DOUBLED +#ifdef GPU_STEREO_CAMERA_BUFFER + "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED" +#else + "#define GPU_TRANSFORM_IS_STEREO" +#endif +#endif +}; + +// Versions specific of the shader +static const std::array VERSION_DEFINES { { + "", + stereoVersion +} }; + +GLShader* compileBackendShader(GLBackend& backend, const Shader& shader) { + // Any GLSLprogram ? normally yes... + const std::string& shaderSource = shader.getSource().getCode(); + GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; + GLShader::ShaderObjects shaderObjects; + + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& shaderObject = shaderObjects[version]; + std::string shaderDefines = glslVersion + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version] + + "\n" + "#extension GL_EXT_texture_buffer : enable" + + "\nprecision lowp float; // check precision 2" + + "\nprecision lowp samplerBuffer;" + + "\nprecision lowp sampler2DShadow;"; + // TODO Delete bool result = compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram); + std::string error; + + +#ifdef SEPARATE_PROGRAM + bool result = ::gl::compileShader(shaderDomain, shaderSource.c_str(), shaderDefines.c_str(), shaderObject.glshader, shaderObject.glprogram, error); +#else + bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error); +#endif + if (!result) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str(); + return nullptr; + } + } + + // So far so good, the shader is created successfully + GLShader* object = new GLShader(backend.shared_from_this()); + object->_shaderObjects = shaderObjects; + + return object; +} + +GLShader* compileBackendProgram(GLBackend& backend, const Shader& program) { + if (!program.isProgram()) { + return nullptr; + } + + GLShader::ShaderObjects programObjects; + + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& programObject = programObjects[version]; + + // Let's go through every shaders and make sure they are ready to go + std::vector< GLuint > shaderGLObjects; + for (auto subShader : program.getShaders()) { + auto object = GLShader::sync(backend, *subShader); + if (object) { + shaderGLObjects.push_back(object->_shaderObjects[version].glshader); + } else { + qCDebug(gpugllogging) << "GLShader::compileBackendProgram - One of the shaders of the program is not compiled?"; + return nullptr; + } + } + + std::string error; + GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error); + if (glprogram == 0) { + qCWarning(gpugllogging) << error.c_str(); + return nullptr; + } + + programObject.glprogram = glprogram; + + makeProgramBindings(programObject); + } + + // So far so good, the program versions have all been created successfully + GLShader* object = new GLShader(backend.shared_from_this()); + object->_shaderObjects = programObjects; + + return object; +} + +GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { + GLShader* object = Backend::getGPUObject(shader); + + // If GPU object already created then good + if (object) { + return object; + } + // need to have a gpu object? + if (shader.isProgram()) { + GLShader* tempObject = compileBackendProgram(backend, shader); + if (tempObject) { + object = tempObject; + Backend::setGPUObject(shader, object); + } + } else if (shader.isDomain()) { + GLShader* tempObject = compileBackendShader(backend, shader); + if (tempObject) { + object = tempObject; + Backend::setGPUObject(shader, object); + } + } + + glFinish(); + return object; +} + +bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings) { + + // First make sure the Shader has been compiled + GLShader* object = sync(backend, shader); + if (!object) { + return false; + } + + // Apply bindings to all program versions and generate list of slots from default version + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& shaderObject = object->_shaderObjects[version]; + if (shaderObject.glprogram) { + Shader::SlotSet buffers; + makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers); + + Shader::SlotSet uniforms; + Shader::SlotSet textures; + Shader::SlotSet samplers; + makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); + + Shader::SlotSet resourceBuffers; + makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); + + Shader::SlotSet inputs; + makeInputSlots(shaderObject.glprogram, slotBindings, inputs); + + Shader::SlotSet outputs; + makeOutputSlots(shaderObject.glprogram, slotBindings, outputs); + + // Define the public slots only from the default version + if (version == 0) { + shader.defineSlots(uniforms, buffers, resourceBuffers, textures, samplers, inputs, outputs); + } // else + { + GLShader::UniformMapping mapping; + for (auto srcUniform : shader.getUniforms()) { + mapping[srcUniform._location] = uniforms.findLocation(srcUniform._name); + } + object->_uniformMappings.push_back(mapping); + } + } + } + + return true; +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.h b/libraries/gpu-gles/src/gpu/gl/GLShader.h new file mode 100644 index 0000000000..e03b487a60 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.h @@ -0,0 +1,59 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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_GLShader_h +#define hifi_gpu_gl_GLShader_h + +#include "GLShared.h" + +namespace gpu { namespace gl { + +class GLShader : public GPUObject { +public: + static GLShader* sync(GLBackend& backend, const Shader& shader); + static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings); + + enum Version { + Mono = 0, + Stereo, + + NumVersions + }; + + using ShaderObject = gpu::gl::ShaderObject; + using ShaderObjects = std::array< ShaderObject, NumVersions >; + + using UniformMapping = std::map; + using UniformMappingVersions = std::vector; + + GLShader(const std::weak_ptr& backend); + ~GLShader(); + + ShaderObjects _shaderObjects; + UniformMappingVersions _uniformMappings; + + GLuint getProgram(Version version = Mono) const { + return _shaderObjects[version].glprogram; + } + + GLint getUniformLocation(GLint srcLoc, Version version = Mono) const { + // This check protect against potential invalid src location for this shader, if unknown then return -1. + const auto& mapping = _uniformMappings[version]; + auto found = mapping.find(srcLoc); + if (found == mapping.end()) { + return -1; + } + return found->second; + } + + const std::weak_ptr _backend; +}; + +} } + + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.cpp b/libraries/gpu-gles/src/gpu/gl/GLShared.cpp new file mode 100644 index 0000000000..5d340889a6 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLShared.cpp @@ -0,0 +1,879 @@ +// +// Created by Bradley Austin Davis on 2016/05/14 +// 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 "GLShared.h" + +#include + +#include + +#include +#include +#include + +Q_LOGGING_CATEGORY(gpugllogging, "hifi.gpu.gl") +Q_LOGGING_CATEGORY(trace_render_gpu_gl, "trace.render.gpu.gl") + +namespace gpu { namespace 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) { +#ifdef DEBUG + return checkGLError(name); +#else + Q_UNUSED(name); + return false; +#endif +} + +gpu::Size getFreeDedicatedMemory() { + Size result { 0 }; + static bool nvidiaMemorySupported { false }; + static bool atiMemorySupported { false }; + if (nvidiaMemorySupported) { + + GLint nvGpuMemory { 0 }; + qDebug() << "TODO: GLShared.cpp getFreeDedicatedMemory GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX"; + //glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &nvGpuMemory); + if (GL_NO_ERROR == glGetError()) { + result = KB_TO_BYTES(nvGpuMemory); + } else { + nvidiaMemorySupported = false; + } + } else if (atiMemorySupported) { + GLint atiGpuMemory[4]; + qDebug() << "TODO: GLShared.cpp getFreeDedicatedMemory GL_TEXTURE_FREE_MEMORY_ATI"; + // not really total memory, but close enough if called early enough in the application lifecycle + //glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, atiGpuMemory); + if (GL_NO_ERROR == glGetError()) { + result = KB_TO_BYTES(atiGpuMemory[0]); + } else { + atiMemorySupported = false; + } + } + 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; + } else if (func == GL_LESS) { + return LESS; + } else if (func == GL_EQUAL) { + return EQUAL; + } else if (func == GL_LEQUAL) { + return LESS_EQUAL; + } else if (func == GL_GREATER) { + return GREATER; + } else if (func == GL_NOTEQUAL) { + return NOT_EQUAL; + } else if (func == GL_GEQUAL) { + return GREATER_EQUAL; + } else if (func == GL_ALWAYS) { + return ALWAYS; + } + + return ALWAYS; +} + +State::StencilOp stencilOpFromGL(GLenum stencilOp) { + if (stencilOp == GL_KEEP) { + return State::STENCIL_OP_KEEP; + } else if (stencilOp == GL_ZERO) { + return State::STENCIL_OP_ZERO; + } else if (stencilOp == GL_REPLACE) { + return State::STENCIL_OP_REPLACE; + } else if (stencilOp == GL_INCR_WRAP) { + return State::STENCIL_OP_INCR_SAT; + } else if (stencilOp == GL_DECR_WRAP) { + return State::STENCIL_OP_DECR_SAT; + } else if (stencilOp == GL_INVERT) { + return State::STENCIL_OP_INVERT; + } else if (stencilOp == GL_INCR) { + return State::STENCIL_OP_INCR; + } else if (stencilOp == GL_DECR) { + return State::STENCIL_OP_DECR; + } + + return State::STENCIL_OP_KEEP; +} + +State::BlendOp blendOpFromGL(GLenum blendOp) { + if (blendOp == GL_FUNC_ADD) { + return State::BLEND_OP_ADD; + } else if (blendOp == GL_FUNC_SUBTRACT) { + return State::BLEND_OP_SUBTRACT; + } else if (blendOp == GL_FUNC_REVERSE_SUBTRACT) { + return State::BLEND_OP_REV_SUBTRACT; + } else if (blendOp == GL_MIN) { + return State::BLEND_OP_MIN; + } else if (blendOp == GL_MAX) { + return State::BLEND_OP_MAX; + } + + return State::BLEND_OP_ADD; +} + +State::BlendArg blendArgFromGL(GLenum blendArg) { + if (blendArg == GL_ZERO) { + return State::ZERO; + } else if (blendArg == GL_ONE) { + return State::ONE; + } else if (blendArg == GL_SRC_COLOR) { + return State::SRC_COLOR; + } else if (blendArg == GL_ONE_MINUS_SRC_COLOR) { + return State::INV_SRC_COLOR; + } else if (blendArg == GL_DST_COLOR) { + return State::DEST_COLOR; + } else if (blendArg == GL_ONE_MINUS_DST_COLOR) { + return State::INV_DEST_COLOR; + } else if (blendArg == GL_SRC_ALPHA) { + return State::SRC_ALPHA; + } else if (blendArg == GL_ONE_MINUS_SRC_ALPHA) { + return State::INV_SRC_ALPHA; + } else if (blendArg == GL_DST_ALPHA) { + return State::DEST_ALPHA; + } else if (blendArg == GL_ONE_MINUS_DST_ALPHA) { + return State::INV_DEST_ALPHA; + } else if (blendArg == GL_CONSTANT_COLOR) { + return State::FACTOR_COLOR; + } else if (blendArg == GL_ONE_MINUS_CONSTANT_COLOR) { + return State::INV_FACTOR_COLOR; + } else if (blendArg == GL_CONSTANT_ALPHA) { + return State::FACTOR_ALPHA; + } else if (blendArg == GL_ONE_MINUS_CONSTANT_ALPHA) { + return State::INV_FACTOR_ALPHA; + } + + return State::ONE; +} + +void getCurrentGLState(State::Data& state) { + { + GLint modes[2]; + //glGetIntegerv(GL_POLYGON_MODE, modes); + qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_POLYGON_MODE"; + qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_FILL"; + qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_LINE"; + + if (modes[0] == 0 /*GL_FILL*/) { + state.fillMode = State::FILL_FACE; + } else { + if (modes[0] == 0 /*GL_LINE*/) { + state.fillMode = State::FILL_LINE; + } else { + state.fillMode = State::FILL_POINT; + } + } + } + { + if (glIsEnabled(GL_CULL_FACE)) { + GLint mode; + glGetIntegerv(GL_CULL_FACE_MODE, &mode); + state.cullMode = (mode == GL_FRONT ? State::CULL_FRONT : State::CULL_BACK); + } else { + state.cullMode = State::CULL_NONE; + } + } + { + GLint winding; + glGetIntegerv(GL_FRONT_FACE, &winding); + state.frontFaceClockwise = (winding == GL_CW); + //state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP); + qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_DEPTH_CLAMP"; + state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST); + //state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE); + qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_MULTISAMPLE"; + + //state.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH); + qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_LINE_SMOOTH"; + + } + { + if (glIsEnabled(GL_POLYGON_OFFSET_FILL)) { + glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &state.depthBiasSlopeScale); + glGetFloatv(GL_POLYGON_OFFSET_UNITS, &state.depthBias); + } + } + { + GLboolean isEnabled = glIsEnabled(GL_DEPTH_TEST); + GLboolean writeMask; + glGetBooleanv(GL_DEPTH_WRITEMASK, &writeMask); + GLint func; + glGetIntegerv(GL_DEPTH_FUNC, &func); + + state.depthTest = State::DepthTest(isEnabled, writeMask, comparisonFuncFromGL(func)); + } + { + GLboolean isEnabled = glIsEnabled(GL_STENCIL_TEST); + + GLint frontWriteMask; + GLint frontReadMask; + GLint frontRef; + GLint frontFail; + GLint frontDepthFail; + GLint frontPass; + GLint frontFunc; + glGetIntegerv(GL_STENCIL_WRITEMASK, &frontWriteMask); + glGetIntegerv(GL_STENCIL_VALUE_MASK, &frontReadMask); + glGetIntegerv(GL_STENCIL_REF, &frontRef); + glGetIntegerv(GL_STENCIL_FAIL, &frontFail); + glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &frontDepthFail); + glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &frontPass); + glGetIntegerv(GL_STENCIL_FUNC, &frontFunc); + + GLint backWriteMask; + GLint backReadMask; + GLint backRef; + GLint backFail; + GLint backDepthFail; + GLint backPass; + GLint backFunc; + glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &backWriteMask); + glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &backReadMask); + glGetIntegerv(GL_STENCIL_BACK_REF, &backRef); + glGetIntegerv(GL_STENCIL_BACK_FAIL, &backFail); + glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &backDepthFail); + glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &backPass); + glGetIntegerv(GL_STENCIL_BACK_FUNC, &backFunc); + + state.stencilActivation = State::StencilActivation(isEnabled, frontWriteMask, backWriteMask); + state.stencilTestFront = State::StencilTest(frontRef, frontReadMask, comparisonFuncFromGL(frontFunc), stencilOpFromGL(frontFail), stencilOpFromGL(frontDepthFail), stencilOpFromGL(frontPass)); + state.stencilTestBack = State::StencilTest(backRef, backReadMask, comparisonFuncFromGL(backFunc), stencilOpFromGL(backFail), stencilOpFromGL(backDepthFail), stencilOpFromGL(backPass)); + } + { + GLint mask = 0xFFFFFFFF; + if (glIsEnabled(GL_SAMPLE_MASK)) { + glGetIntegerv(GL_SAMPLE_MASK, &mask); + state.sampleMask = mask; + } + state.sampleMask = mask; + } + { + state.alphaToCoverageEnable = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + { + GLboolean isEnabled = glIsEnabled(GL_BLEND); + GLint srcRGB; + GLint srcA; + GLint dstRGB; + GLint dstA; + glGetIntegerv(GL_BLEND_SRC_RGB, &srcRGB); + glGetIntegerv(GL_BLEND_SRC_ALPHA, &srcA); + glGetIntegerv(GL_BLEND_DST_RGB, &dstRGB); + glGetIntegerv(GL_BLEND_DST_ALPHA, &dstA); + + GLint opRGB; + GLint opA; + glGetIntegerv(GL_BLEND_EQUATION_RGB, &opRGB); + glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &opA); + + state.blendFunction = State::BlendFunction(isEnabled, + blendArgFromGL(srcRGB), blendOpFromGL(opRGB), blendArgFromGL(dstRGB), + blendArgFromGL(srcA), blendOpFromGL(opA), blendArgFromGL(dstA)); + } + { + GLboolean mask[4]; + glGetBooleanv(GL_COLOR_WRITEMASK, mask); + state.colorWriteMask = (mask[0] ? State::WRITE_RED : 0) + | (mask[1] ? State::WRITE_GREEN : 0) + | (mask[2] ? State::WRITE_BLUE : 0) + | (mask[3] ? State::WRITE_ALPHA : 0); + } + + (void)CHECK_GL_ERROR(); +} + + +class ElementResource { +public: + gpu::Element _element; + uint16 _resource; + + ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} +}; + +ElementResource getFormatFromGLUniform(GLenum gltype) { + switch (gltype) { + case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + /* + case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + */ + case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); + + case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); +#if defined(Q_OS_WIN) + case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); +#endif + + case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); + + + case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + + /* {GL_FLOAT_MAT2x3 mat2x3}, + {GL_FLOAT_MAT2x4 mat2x4}, + {GL_FLOAT_MAT3x2 mat3x2}, + {GL_FLOAT_MAT3x4 mat3x4}, + {GL_FLOAT_MAT4x2 mat4x2}, + {GL_FLOAT_MAT4x3 mat4x3}, + {GL_DOUBLE_MAT2 dmat2}, + {GL_DOUBLE_MAT3 dmat3}, + {GL_DOUBLE_MAT4 dmat4}, + {GL_DOUBLE_MAT2x3 dmat2x3}, + {GL_DOUBLE_MAT2x4 dmat2x4}, + {GL_DOUBLE_MAT3x2 dmat3x2}, + {GL_DOUBLE_MAT3x4 dmat3x4}, + {GL_DOUBLE_MAT4x2 dmat4x2}, + {GL_DOUBLE_MAT4x3 dmat4x3}, + */ + + //qDebug() << "TODO: GLShared.cpp.cpp:ElementResource GL_SAMPLER_1D"; + //case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); + case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); + + case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); + case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); + +#if defined(Q_OS_WIN) + case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); +#endif + + case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); +#if defined(Q_OS_WIN) + case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); + + case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); +#endif + + // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, + // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, + + // {GL_SAMPLER_BUFFER samplerBuffer}, + // {GL_SAMPLER_2D_RECT sampler2DRect}, + // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, + +#if defined(Q_OS_WIN) + case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); + case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); + case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + + // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, + // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, + + case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); + case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); + case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); +#endif + // {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, + // {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, + /* + {GL_IMAGE_1D image1D}, + {GL_IMAGE_2D image2D}, + {GL_IMAGE_3D image3D}, + {GL_IMAGE_2D_RECT image2DRect}, + {GL_IMAGE_CUBE imageCube}, + {GL_IMAGE_BUFFER imageBuffer}, + {GL_IMAGE_1D_ARRAY image1DArray}, + {GL_IMAGE_2D_ARRAY image2DArray}, + {GL_IMAGE_2D_MULTISAMPLE image2DMS}, + {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, + {GL_INT_IMAGE_1D iimage1D}, + {GL_INT_IMAGE_2D iimage2D}, + {GL_INT_IMAGE_3D iimage3D}, + {GL_INT_IMAGE_2D_RECT iimage2DRect}, + {GL_INT_IMAGE_CUBE iimageCube}, + {GL_INT_IMAGE_BUFFER iimageBuffer}, + {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, + {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, + {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, + {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, + {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, + {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, + {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, + {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, + {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot + + {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, + {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, + {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, + {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, + {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, + {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} + */ + default: + return ElementResource(Element(), Resource::BUFFER); + } + +}; + +int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { + GLint uniformsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); + + for (int i = 0; i < uniformsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + GLint location = glGetUniformLocation(glprogram, name); + const GLint INVALID_UNIFORM_LOCATION = -1; + + // Try to make sense of the gltype + auto elementResource = getFormatFromGLUniform(type); + + // The uniform as a standard var type + if (location != INVALID_UNIFORM_LOCATION) { + // Let's make sure the name doesn't contains an array element + std::string sname(name); + auto foundBracket = sname.find_first_of('['); + if (foundBracket != std::string::npos) { + // std::string arrayname = sname.substr(0, foundBracket); + + if (sname[foundBracket + 1] == '0') { + sname = sname.substr(0, foundBracket); + } else { + // skip this uniform since it's not the first element of an array + continue; + } + } + + if (elementResource._resource == Resource::BUFFER) { + uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); + } else { + // For texture/Sampler, the location is the actual binding value + GLint binding = -1; + glGetUniformiv(glprogram, location, &binding); + + auto requestedBinding = slotBindings.find(std::string(sname)); + if (requestedBinding != slotBindings.end()) { + if (binding != (*requestedBinding)._location) { + binding = (*requestedBinding)._location; + glProgramUniform1i(glprogram, location, binding); + } + } + + textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + } + } + } + + return uniformsCount; +} + +const GLint UNUSED_SLOT = -1; +bool isUnusedSlot(GLint binding) { + return (binding == UNUSED_SLOT); +} + +int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { + GLint buffersCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); + + // fast exit + if (buffersCount == 0) { + return 0; + } + + GLint maxNumUniformBufferSlots = 0; + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); + std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); + + struct UniformBlockInfo { + using Vector = std::vector; + const GLuint index{ 0 }; + const std::string name; + GLint binding{ -1 }; + GLint size{ 0 }; + + static std::string getName(GLuint glprogram, GLuint i) { + static const GLint NAME_LENGTH = 256; + GLint length = 0; + GLchar nameBuffer[NAME_LENGTH]; + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); + glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); + return std::string(nameBuffer); + } + + UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); + } + }; + + UniformBlockInfo::Vector uniformBlocks; + uniformBlocks.reserve(buffersCount); + for (int i = 0; i < buffersCount; i++) { + uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); + } + + for (auto& info : uniformBlocks) { + auto requestedBinding = slotBindings.find(info.name); + if (requestedBinding != slotBindings.end()) { + info.binding = (*requestedBinding)._location; + glUniformBlockBinding(glprogram, info.index, info.binding); + uniformBufferSlotMap[info.binding] = info.index; + } + } + + for (auto& info : uniformBlocks) { + if (slotBindings.count(info.name)) { + continue; + } + + // If the binding is 0, or the binding maps to an already used binding + if (info.binding == 0 || uniformBufferSlotMap[info.binding] != UNUSED_SLOT) { + // If no binding was assigned then just do it finding a free slot + auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot); + if (slotIt != uniformBufferSlotMap.end()) { + info.binding = slotIt - uniformBufferSlotMap.begin(); + glUniformBlockBinding(glprogram, info.index, info.binding); + } else { + // This should neve happen, an active ubo cannot find an available slot among the max available?! + info.binding = -1; + } + } + + uniformBufferSlotMap[info.binding] = info.index; + } + + for (auto& info : uniformBlocks) { + static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); + buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); + } + return buffersCount; +} +//CLIMAX_MERGE_START +//This has been copied over from gl45backendshader.cpp +int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { + GLint buffersCount = 0; + glGetProgramInterfaceiv(glprogram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &buffersCount); + + // fast exit + if (buffersCount == 0) { + return 0; + } + + GLint maxNumResourceBufferSlots = 0; + glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxNumResourceBufferSlots); + std::vector resourceBufferSlotMap(maxNumResourceBufferSlots, -1); + + struct ResourceBlockInfo { + using Vector = std::vector; + const GLuint index{ 0 }; + const std::string name; + GLint binding{ -1 }; + GLint size{ 0 }; + + static std::string getName(GLuint glprogram, GLuint i) { + static const GLint NAME_LENGTH = 256; + GLint length = 0; + GLchar nameBuffer[NAME_LENGTH]; + glGetProgramResourceName(glprogram, GL_SHADER_STORAGE_BLOCK, i, NAME_LENGTH, &length, nameBuffer); + return std::string(nameBuffer); + } + + ResourceBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { + GLenum props[2] = { GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE}; + glGetProgramResourceiv(glprogram, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, nullptr, &binding); + } + }; + + ResourceBlockInfo::Vector resourceBlocks; + resourceBlocks.reserve(buffersCount); + for (int i = 0; i < buffersCount; i++) { + resourceBlocks.push_back(ResourceBlockInfo(glprogram, i)); + } + + for (auto& info : resourceBlocks) { + auto requestedBinding = slotBindings.find(info.name); + if (requestedBinding != slotBindings.end()) { + info.binding = (*requestedBinding)._location; + glUniformBlockBinding(glprogram, info.index, info.binding); + resourceBufferSlotMap[info.binding] = info.index; + } + } + + for (auto& info : resourceBlocks) { + if (slotBindings.count(info.name)) { + continue; + } + + // If the binding is -1, or the binding maps to an already used binding + if (info.binding == -1 || !isUnusedSlot(resourceBufferSlotMap[info.binding])) { + // If no binding was assigned then just do it finding a free slot + auto slotIt = std::find_if(resourceBufferSlotMap.begin(), resourceBufferSlotMap.end(), isUnusedSlot); + if (slotIt != resourceBufferSlotMap.end()) { + info.binding = slotIt - resourceBufferSlotMap.begin(); + glUniformBlockBinding(glprogram, info.index, info.binding); + } else { + // This should never happen, an active ssbo cannot find an available slot among the max available?! + info.binding = -1; + } + } + + resourceBufferSlotMap[info.binding] = info.index; + } + + for (auto& info : resourceBlocks) { + static const Element element(SCALAR, gpu::UINT32, gpu::RESOURCE_BUFFER); + resourceBuffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); + } + return buffersCount; +} +//CLIMAX_MERGE_END + +int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { + GLint inputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + GLint binding = glGetAttribLocation(glprogram, name); + + auto elementResource = getFormatFromGLUniform(type); + inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); + } + + return inputsCount; +} + +int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { + /* GLint outputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + auto element = getFormatFromGLUniform(type); + outputs.insert(Shader::Slot(name, i, element)); + } + */ + return 0; //inputsCount; +} + +void makeProgramBindings(ShaderObject& shaderObject) { + if (!shaderObject.glprogram) { + return; + } + GLuint glprogram = shaderObject.glprogram; + GLint loc = -1; + + //Check for gpu specific attribute slotBindings + loc = glGetAttribLocation(glprogram, "inPosition"); + if (loc >= 0 && loc != gpu::Stream::POSITION) { + glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); + } + + loc = glGetAttribLocation(glprogram, "inNormal"); + if (loc >= 0 && loc != gpu::Stream::NORMAL) { + glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); + } + + loc = glGetAttribLocation(glprogram, "inColor"); + if (loc >= 0 && loc != gpu::Stream::COLOR) { + glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); + } + + loc = glGetAttribLocation(glprogram, "inTexCoord0"); + if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); + } + + loc = glGetAttribLocation(glprogram, "inTangent"); + if (loc >= 0 && loc != gpu::Stream::TANGENT) { + glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); + } + + loc = glGetAttribLocation(glprogram, "inTexCoord1"); + if (loc >= 0 && loc != gpu::Stream::TEXCOORD1) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "inTexCoord1"); + } + + loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); + if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); + } + + loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); + if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); + } + + loc = glGetAttribLocation(glprogram, "_drawCallInfo"); + if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { + glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); + } + + // Link again to take into account the assigned attrib location + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + if (!linked) { + qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; + } + + // now assign the ubo binding, then DON't relink! + + //Check for gpu specific uniform slotBindings + loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer"); + if (loc >= 0) { + // FIXME GLES + // glShaderStorageBlockBinding(glprogram, loc, TRANSFORM_OBJECT_SLOT); + shaderObject.transformObjectSlot = TRANSFORM_OBJECT_SLOT; + } + + loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, TRANSFORM_CAMERA_SLOT); + shaderObject.transformCameraSlot = TRANSFORM_CAMERA_SLOT; + } + + (void)CHECK_GL_ERROR(); +} + +void serverWait() { + auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + assert(fence); + glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(fence); +} + +void clientWait() { + auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + assert(fence); + auto result = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0); + while (GL_TIMEOUT_EXPIRED == result || GL_WAIT_FAILED == result) { + // Minimum sleep + QThread::usleep(1); + result = glClientWaitSync(fence, 0, 0); + } + glDeleteSync(fence); +} + +} } + + +using namespace gpu; + + diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.h b/libraries/gpu-gles/src/gpu/gl/GLShared.h new file mode 100644 index 0000000000..54209b106d --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLShared.h @@ -0,0 +1,167 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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_GLShared_h +#define hifi_gpu_GLShared_h + +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(gpugllogging) +Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl) + +namespace gpu { namespace gl { + + static const GLint TRANSFORM_OBJECT_SLOT { 14 }; // SSBO binding slot + +// Create a fence and inject a GPU wait on the fence +void serverWait(); + +// Create a fence and synchronously wait on the fence +void clientWait(); + +gpu::Size getDedicatedMemory(); +gpu::Size getFreeDedicatedMemory(); +ComparisonFunction comparisonFuncFromGL(GLenum func); +State::StencilOp stencilOpFromGL(GLenum stencilOp); +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 + // owning GPU object + Idle, + // The object has been queued for transfer to the GPU + Pending, + // The object has been transferred to the GPU, but is awaiting + // any post transfer operations that may need to occur on the + // primary rendering thread + Transferred, +}; + +static const GLenum BLEND_OPS_TO_GL[State::NUM_BLEND_OPS] = { + GL_FUNC_ADD, + GL_FUNC_SUBTRACT, + GL_FUNC_REVERSE_SUBTRACT, + GL_MIN, + GL_MAX +}; + +static const GLenum BLEND_ARGS_TO_GL[State::NUM_BLEND_ARGS] = { + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA_SATURATE, + GL_CONSTANT_COLOR, + GL_ONE_MINUS_CONSTANT_COLOR, + GL_CONSTANT_ALPHA, + GL_ONE_MINUS_CONSTANT_ALPHA, +}; + +static const GLenum COMPARISON_TO_GL[gpu::NUM_COMPARISON_FUNCS] = { + GL_NEVER, + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS +}; + +static const GLenum PRIMITIVE_TO_GL[gpu::NUM_PRIMITIVES] = { + GL_POINTS, + GL_LINES, + GL_LINE_STRIP, + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN, +}; + +static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = { + GL_FLOAT, + GL_INT, + GL_UNSIGNED_INT, + GL_HALF_FLOAT, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_BYTE, + GL_UNSIGNED_BYTE, + // Normalized values + GL_INT, + GL_UNSIGNED_INT, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_BYTE, + GL_UNSIGNED_BYTE +}; + +bool checkGLError(const char* name = nullptr); +bool checkGLErrorDebug(const char* name = nullptr); + +class GLBackend; + +template +struct GLObject : public GPUObject { +public: + GLObject(const std::weak_ptr& backend, const GPUType& gpuObject, GLuint id) : _gpuObject(gpuObject), _id(id), _backend(backend) {} + + virtual ~GLObject() { } + + const GPUType& _gpuObject; + const GLuint _id; +protected: + const std::weak_ptr _backend; +}; + +class GlBuffer; +class GLFramebuffer; +class GLPipeline; +class GLQuery; +class GLState; +class GLShader; +class GLTexture; + +} } // namespace gpu::gl + +#define CHECK_GL_ERROR() gpu::gl::checkGLErrorDebug(__FUNCTION__) + +#endif + + + diff --git a/libraries/gpu-gles/src/gpu/gl/GLState.cpp b/libraries/gpu-gles/src/gpu/gl/GLState.cpp new file mode 100644 index 0000000000..b6d917b928 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLState.cpp @@ -0,0 +1,248 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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 +// + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif +#endif + + +#include "GLState.h" + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +typedef GLState::Command Command; +typedef GLState::CommandPointer CommandPointer; +typedef GLState::Command1 Command1U; +typedef GLState::Command1 Command1I; +typedef GLState::Command1 Command1B; +typedef GLState::Command1 CommandDepthBias; +typedef GLState::Command1 CommandDepthTest; +typedef GLState::Command3 CommandStencil; +typedef GLState::Command1 CommandBlend; + +const GLState::Commands makeResetStateCommands(); + +// NOTE: This must stay in sync with the ordering of the State::Field enum +const GLState::Commands makeResetStateCommands() { + // Since State::DEFAULT is a static defined in another .cpp the initialisation order is random + // and we have a 50/50 chance that State::DEFAULT is not yet initialized. + // Since State::DEFAULT = State::Data() it is much easier to not use the actual State::DEFAULT + // but another State::Data object with a default initialization. + const State::Data DEFAULT = State::Data(); + + auto depthBiasCommand = std::make_shared(&GLBackend::do_setStateDepthBias, + Vec2(DEFAULT.depthBias, DEFAULT.depthBiasSlopeScale)); + auto stencilCommand = std::make_shared(&GLBackend::do_setStateStencil, DEFAULT.stencilActivation, + DEFAULT.stencilTestFront, DEFAULT.stencilTestBack); + + // The state commands to reset to default, + // WARNING depending on the order of the State::Field enum + return { + std::make_shared(&GLBackend::do_setStateFillMode, DEFAULT.fillMode), + std::make_shared(&GLBackend::do_setStateCullMode, DEFAULT.cullMode), + std::make_shared(&GLBackend::do_setStateFrontFaceClockwise, DEFAULT.frontFaceClockwise), + std::make_shared(&GLBackend::do_setStateDepthClampEnable, DEFAULT.depthClampEnable), + std::make_shared(&GLBackend::do_setStateScissorEnable, DEFAULT.scissorEnable), + std::make_shared(&GLBackend::do_setStateMultisampleEnable, DEFAULT.multisampleEnable), + std::make_shared(&GLBackend::do_setStateAntialiasedLineEnable, DEFAULT.antialisedLineEnable), + + // Depth bias has 2 fields in State but really one call in GLBackend + CommandPointer(depthBiasCommand), + CommandPointer(depthBiasCommand), + + std::make_shared(&GLBackend::do_setStateDepthTest, DEFAULT.depthTest), + + // Depth bias has 3 fields in State but really one call in GLBackend + CommandPointer(stencilCommand), + CommandPointer(stencilCommand), + CommandPointer(stencilCommand), + + std::make_shared(&GLBackend::do_setStateSampleMask, DEFAULT.sampleMask), + + std::make_shared(&GLBackend::do_setStateAlphaToCoverageEnable, DEFAULT.alphaToCoverageEnable), + + std::make_shared(&GLBackend::do_setStateBlend, DEFAULT.blendFunction), + + std::make_shared(&GLBackend::do_setStateColorWriteMask, DEFAULT.colorWriteMask) + }; +} + +const GLState::Commands GLState::_resetStateCommands = makeResetStateCommands(); + + +void generateFillMode(GLState::Commands& commands, State::FillMode fillMode) { + commands.push_back(std::make_shared(&GLBackend::do_setStateFillMode, int32(fillMode))); +} + +void generateCullMode(GLState::Commands& commands, State::CullMode cullMode) { + commands.push_back(std::make_shared(&GLBackend::do_setStateCullMode, int32(cullMode))); +} + +void generateFrontFaceClockwise(GLState::Commands& commands, bool isClockwise) { + commands.push_back(std::make_shared(&GLBackend::do_setStateFrontFaceClockwise, isClockwise)); +} + +void generateDepthClampEnable(GLState::Commands& commands, bool enable) { + commands.push_back(std::make_shared(&GLBackend::do_setStateDepthClampEnable, enable)); +} + +void generateScissorEnable(GLState::Commands& commands, bool enable) { + commands.push_back(std::make_shared(&GLBackend::do_setStateScissorEnable, enable)); +} + +void generateMultisampleEnable(GLState::Commands& commands, bool enable) { + commands.push_back(std::make_shared(&GLBackend::do_setStateMultisampleEnable, enable)); +} + +void generateAntialiasedLineEnable(GLState::Commands& commands, bool enable) { + commands.push_back(std::make_shared(&GLBackend::do_setStateAntialiasedLineEnable, enable)); +} + +void generateDepthBias(GLState::Commands& commands, const State& state) { + commands.push_back(std::make_shared(&GLBackend::do_setStateDepthBias, Vec2(state.getDepthBias(), state.getDepthBiasSlopeScale()))); +} + +void generateDepthTest(GLState::Commands& commands, const State::DepthTest& test) { + commands.push_back(std::make_shared(&GLBackend::do_setStateDepthTest, int32(test.getRaw()))); +} + +void generateStencil(GLState::Commands& commands, const State& state) { + commands.push_back(std::make_shared(&GLBackend::do_setStateStencil, state.getStencilActivation(), state.getStencilTestFront(), state.getStencilTestBack())); +} + +void generateAlphaToCoverageEnable(GLState::Commands& commands, bool enable) { + commands.push_back(std::make_shared(&GLBackend::do_setStateAlphaToCoverageEnable, enable)); +} + +void generateSampleMask(GLState::Commands& commands, uint32 mask) { + commands.push_back(std::make_shared(&GLBackend::do_setStateSampleMask, mask)); +} + +void generateBlend(GLState::Commands& commands, const State& state) { + commands.push_back(std::make_shared(&GLBackend::do_setStateBlend, state.getBlendFunction())); +} + +void generateColorWriteMask(GLState::Commands& commands, uint32 mask) { + commands.push_back(std::make_shared(&GLBackend::do_setStateColorWriteMask, mask)); +} + +GLState* GLState::sync(const State& state) { + GLState* object = Backend::getGPUObject(state); + + // If GPU object already created then good + if (object) { + return object; + } + + // Else allocate and create the GLState + if (!object) { + object = new GLState(); + Backend::setGPUObject(state, object); + } + + // here, we need to regenerate something so let's do it all + object->_commands.clear(); + object->_stamp = state.getStamp(); + object->_signature = state.getSignature(); + + bool depthBias = false; + bool stencilState = false; + + // go thorugh the list of state fields in the State and record the corresponding gl command + for (int i = 0; i < State::NUM_FIELDS; i++) { + if (state.getSignature()[i]) { + switch (i) { + case State::FILL_MODE: { + generateFillMode(object->_commands, state.getFillMode()); + break; + } + case State::CULL_MODE: { + generateCullMode(object->_commands, state.getCullMode()); + break; + } + case State::DEPTH_BIAS: + case State::DEPTH_BIAS_SLOPE_SCALE: { + depthBias = true; + break; + } + case State::FRONT_FACE_CLOCKWISE: { + generateFrontFaceClockwise(object->_commands, state.isFrontFaceClockwise()); + break; + } + case State::DEPTH_CLAMP_ENABLE: { + generateDepthClampEnable(object->_commands, state.isDepthClampEnable()); + break; + } + case State::SCISSOR_ENABLE: { + generateScissorEnable(object->_commands, state.isScissorEnable()); + break; + } + case State::MULTISAMPLE_ENABLE: { + generateMultisampleEnable(object->_commands, state.isMultisampleEnable()); + break; + } + case State::ANTIALISED_LINE_ENABLE: { + generateAntialiasedLineEnable(object->_commands, state.isAntialiasedLineEnable()); + break; + } + case State::DEPTH_TEST: { + generateDepthTest(object->_commands, state.getDepthTest()); + break; + } + + case State::STENCIL_ACTIVATION: + case State::STENCIL_TEST_FRONT: + case State::STENCIL_TEST_BACK: { + stencilState = true; + break; + } + + case State::SAMPLE_MASK: { + generateSampleMask(object->_commands, state.getSampleMask()); + break; + } + case State::ALPHA_TO_COVERAGE_ENABLE: { + generateAlphaToCoverageEnable(object->_commands, state.isAlphaToCoverageEnabled()); + break; + } + + case State::BLEND_FUNCTION: { + generateBlend(object->_commands, state); + break; + } + + case State::COLOR_WRITE_MASK: { + generateColorWriteMask(object->_commands, state.getColorWriteMask()); + break; + } + } + } + } + + if (depthBias) { + generateDepthBias(object->_commands, state); + } + + if (stencilState) { + generateStencil(object->_commands, state); + } + + return object; +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLState.h b/libraries/gpu-gles/src/gpu/gl/GLState.h new file mode 100644 index 0000000000..82635db893 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLState.h @@ -0,0 +1,73 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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_GLState_h +#define hifi_gpu_gl_GLState_h + +#include "GLShared.h" + +#include + +namespace gpu { namespace gl { + +class GLBackend; +class GLState : public GPUObject { +public: + static GLState* sync(const State& state); + + class Command { + public: + virtual void run(GLBackend* backend) = 0; + Command() {} + virtual ~Command() {}; + }; + + template class Command1 : public Command { + public: + typedef void (GLBackend::*GLFunction)(T); + void run(GLBackend* backend) { (backend->*(_func))(_param); } + Command1(GLFunction func, T param) : _func(func), _param(param) {}; + GLFunction _func; + T _param; + }; + template class Command2 : public Command { + public: + typedef void (GLBackend::*GLFunction)(T, U); + void run(GLBackend* backend) { (backend->*(_func))(_param0, _param1); } + Command2(GLFunction func, T param0, U param1) : _func(func), _param0(param0), _param1(param1) {}; + GLFunction _func; + T _param0; + U _param1; + }; + + template class Command3 : public Command { + public: + typedef void (GLBackend::*GLFunction)(T, U, V); + void run(GLBackend* backend) { (backend->*(_func))(_param0, _param1, _param2); } + Command3(GLFunction func, T param0, U param1, V param2) : _func(func), _param0(param0), _param1(param1), _param2(param2) {}; + GLFunction _func; + T _param0; + U _param1; + V _param2; + }; + + typedef std::shared_ptr< Command > CommandPointer; + typedef std::vector< CommandPointer > Commands; + + Commands _commands; + Stamp _stamp; + State::Signature _signature; + + // The state commands to reset to default, + static const Commands _resetStateCommands; + + friend class GLBackend; +}; + +} } + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp new file mode 100644 index 0000000000..6eec4b5292 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp @@ -0,0 +1,648 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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 "GLTexelFormat.h" + +using namespace gpu; +using namespace gpu::gl; + + +GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { +// qDebug() << "GLTexelFormat::evalGLTexelFormatInternal " << dstFormat.getDimension() << ", " << dstFormat.getSemantic() << ", " << dstFormat.getType(); + GLenum result = GL_RGBA8; + switch (dstFormat.getDimension()) { + case gpu::SCALAR: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + case gpu::SRGB: + case gpu::SRGBA: + switch (dstFormat.getType()) { + case gpu::UINT32: + result = GL_R32UI; + break; + case gpu::INT32: + result = GL_R32I; + break; + case gpu::NUINT32: + result = GL_R8; + break; + case gpu::NINT32: + result = GL_R8_SNORM; + break; + case gpu::FLOAT: + result = GL_R32F; + break; + case gpu::UINT16: + result = GL_R16UI; + break; + case gpu::INT16: + result = GL_R16I; + break; + case gpu::HALF: + result = GL_R16F; + break; + case gpu::UINT8: + result = GL_R8UI; + break; + case gpu::INT8: + result = GL_R8I; + break; + case gpu::NUINT8: + if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { + //result = GL_SLUMINANCE8; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormatInternal GL_SLUMINANCE8"; + } else { + result = GL_R8; + } + break; + case gpu::NINT8: + result = GL_R8_SNORM; + break; + default: + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormatInternal " << dstFormat.getType(); + Q_UNREACHABLE(); + break; + } + break; + case gpu::R11G11B10: + // the type should be float + result = GL_R11F_G11F_B10F; + break; + + case gpu::DEPTH: + result = GL_DEPTH_COMPONENT16; + switch (dstFormat.getType()) { + case gpu::FLOAT: + result = GL_DEPTH_COMPONENT32F; + break; + case gpu::UINT16: + case gpu::INT16: + case gpu::NUINT16: + case gpu::NINT16: + case gpu::HALF: + result = GL_DEPTH_COMPONENT16; + break; + case gpu::UINT8: + case gpu::INT8: + case gpu::NUINT8: + case gpu::NINT8: + result = GL_DEPTH_COMPONENT24; + break; + default: + Q_UNREACHABLE(); + break; + } + break; + + case gpu::DEPTH_STENCIL: + result = GL_DEPTH24_STENCIL8; + break; + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + case gpu::VEC2: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + result = GL_RG8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC3: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + result = GL_RGB8; + break; + case gpu::SRGB: + case gpu::SRGBA: + //result = GL_SRGB8; // standard 2.2 gamma correction color + result = GL_RGB8; // standard 2.2 gamma correction color + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC4: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + result = GL_RGB8; + break; + case gpu::RGBA: + switch (dstFormat.getType()) { + case gpu::UINT32: + result = GL_RGBA32UI; + break; + case gpu::INT32: + result = GL_RGBA32I; + break; + case gpu::FLOAT: + result = GL_RGBA32F; + break; + case gpu::UINT16: + result = GL_RGBA16UI; + break; + case gpu::INT16: + result = GL_RGBA16I; + break; + case gpu::HALF: + result = GL_RGBA16F; + break; + case gpu::UINT8: + result = GL_RGBA8UI; + break; + case gpu::INT8: + result = GL_RGBA8I; + break; + case gpu::NUINT8: + result = GL_RGBA8; + break; + case gpu::NINT8: + result = GL_RGBA8_SNORM; + break; + case gpu::NUINT32: + case gpu::NINT32: + case gpu::NUM_TYPES: // quiet compiler + Q_UNREACHABLE(); + } + break; + case gpu::SRGB: + //result = GL_SRGB8; + result = GL_RGB8; + qDebug() << "SRGBA Here 2"; + break; + case gpu::SRGBA: + result = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + //qDebug() << "GLTexelFormat::evalGLTexelFormatInternal result " << result; + + return result; +} + +GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) { +// qDebug() << "GLTexelFormat::evalGLTexelFormat dst.getDimension=" << dstFormat.getDimension() << " dst.getType=" << dstFormat.getType() << " dst.getSemantic=" << dstFormat.getSemantic(); +// qDebug() << "GLTexelFormat::evalGLTexelFormat src.getDimension=" << srcFormat.getDimension() << " src.getType=" << srcFormat.getType() << " src.getSemantic=" << srcFormat.getSemantic(); + + if (dstFormat != srcFormat) { + GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }; + + switch (dstFormat.getDimension()) { + case gpu::SCALAR: { + texel.format = GL_RED; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_R8; + break; + + //CLIMAX_MERGE_START + // case gpu::COMPRESSED_R: + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RED_RGTC1"; + // //texel.internalFormat = GL_COMPRESSED_RED_RGTC1; + // break; + //CLIMAX_MERGE_END + + case gpu::DEPTH: + texel.internalFormat = GL_DEPTH_COMPONENT32_OES; + break; + case gpu::DEPTH_STENCIL: + texel.type = GL_UNSIGNED_INT_24_8; + texel.format = GL_DEPTH_STENCIL; + texel.internalFormat = GL_DEPTH24_STENCIL8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + case gpu::VEC2: { + texel.format = GL_RG; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RG8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC3: { + texel.format = GL_RGB; + + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RGB8; + break; + //CLIMAX_MERGE_START + //not needed? + // case gpu::COMPRESSED_RGB: + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGB"; + // //texel.internalFormat = GL_COMPRESSED_RGB; + // break; + // case gpu::COMPRESSED_SRGB: + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB"; + // //texel.internalFormat = GL_COMPRESSED_SRGB; + // break; + //CLIMAX_MERGE_END + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC4: { + texel.format = GL_RGBA; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (srcFormat.getSemantic()) { + case gpu::BGRA: + case gpu::SBGRA: + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_BGRA"; + //texel.format = GL_BGRA; + break; + case gpu::RGB: + case gpu::RGBA: + case gpu::SRGB: + case gpu::SRGBA: + default: + break; + }; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + texel.internalFormat = GL_RGB8; + break; + case gpu::RGBA: + texel.internalFormat = GL_RGBA8; + break; + case gpu::SRGB: + //texel.internalFormat = GL_SRGB8; + texel.internalFormat = GL_RGB8; + qDebug() << "SRGBA Here 3"; + break; + case gpu::SRGBA: + texel.internalFormat = GL_SRGB8_ALPHA8; + break; + + //CLIMAX_MERGE_START + // case gpu::COMPRESSED_RGBA: + // //texel.internalFormat = GL_COMPRESSED_RGBA; + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGBA"; + // break; + // case gpu::COMPRESSED_SRGBA: + // //texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA; + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB_ALPHA"; + // break; + //CLIMAX_MERGE_END + // FIXME: WE will want to support this later + /* + case gpu::COMPRESSED_BC3_RGBA: + texel.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + case gpu::COMPRESSED_BC3_SRGBA: + texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + break; + + case gpu::COMPRESSED_BC7_RGBA: + texel.internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + break; + case gpu::COMPRESSED_BC7_SRGBA: + texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; + break; + */ + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + return texel; + } else { + GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }; + + switch (dstFormat.getDimension()) { + case gpu::SCALAR: { + texel.format = GL_RED; + 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::RGB: + case gpu::RGBA: + case gpu::SRGB: + case gpu::SRGBA: + texel.internalFormat = GL_RED; + switch (dstFormat.getType()) { + case gpu::UINT32: { + texel.internalFormat = GL_R32UI; + break; + } + case gpu::INT32: { + texel.internalFormat = GL_R32I; + break; + } + case gpu::NUINT32: { + texel.internalFormat = GL_R8; + break; + } + case gpu::NINT32: { + texel.internalFormat = GL_R8_SNORM; + break; + } + case gpu::FLOAT: { + texel.internalFormat = GL_R32F; + break; + } + case gpu::UINT16: { + texel.internalFormat = GL_R16UI; + break; + } + case gpu::INT16: { + texel.internalFormat = GL_R16I; + break; + } + case gpu::NUINT16: { + //texel.internalFormat = GL_R16; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_R16"; + break; + } + case gpu::NINT16: { + //texel.internalFormat = GL_R16_SNORM; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_R16_SNORM"; + break; + } + case gpu::HALF: { + texel.internalFormat = GL_R16F; + break; + } + case gpu::UINT8: { + texel.internalFormat = GL_R8UI; + break; + } + case gpu::INT8: { + texel.internalFormat = GL_R8I; + break; + } + case gpu::NUINT8: { + if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { +// texel.internalFormat = GL_SLUMINANCE8; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_SLUMINANCE8"; + + } else { + texel.internalFormat = GL_R8; + } + break; + } + case gpu::NINT8: { + texel.internalFormat = GL_R8_SNORM; + break; + } + case gpu::NUM_TYPES: { // quiet compiler + Q_UNREACHABLE(); + } + + } + break; + + case gpu::R11G11B10: + texel.format = GL_RGB; + // the type should be float + texel.internalFormat = GL_R11F_G11F_B10F; + break; + + case gpu::DEPTH: + texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it + texel.internalFormat = GL_DEPTH_COMPONENT32_OES; + switch (dstFormat.getType()) { + case gpu::UINT32: + case gpu::INT32: + case gpu::NUINT32: + case gpu::NINT32: { + texel.internalFormat = GL_DEPTH_COMPONENT32_OES; + break; + } + case gpu::FLOAT: { + texel.internalFormat = GL_DEPTH_COMPONENT32F; + break; + } + case gpu::UINT16: + case gpu::INT16: + case gpu::NUINT16: + case gpu::NINT16: + case gpu::HALF: { + texel.internalFormat = GL_DEPTH_COMPONENT16; + break; + } + case gpu::UINT8: + case gpu::INT8: + case gpu::NUINT8: + case gpu::NINT8: { + texel.internalFormat = GL_DEPTH_COMPONENT24; + break; + } + case gpu::NUM_TYPES: { // quiet compiler + Q_UNREACHABLE(); + } + } + break; + case gpu::DEPTH_STENCIL: + texel.type = GL_UNSIGNED_INT_24_8; + texel.format = GL_DEPTH_STENCIL; + texel.internalFormat = GL_DEPTH24_STENCIL8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC2: { + texel.format = GL_RG; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RG8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC3: { + texel.format = GL_RGB; + + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + texel.internalFormat = GL_RGB8; + break; + case gpu::SRGB: + case gpu::SRGBA: + //texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color + texel.internalFormat = GL_RGB8; // standard 2.2 gamma correction color + break; + //CLIMAX_MERGE_START + // case gpu::COMPRESSED_RGB: + // //texel.internalFormat = GL_COMPRESSED_RGB; + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGB"; + // break; + // case gpu::COMPRESSED_SRGB: + // //texel.internalFormat = GL_COMPRESSED_SRGB; + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB"; + // break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + case gpu::VEC4: { + texel.format = GL_RGBA; + texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; + + switch (dstFormat.getSemantic()) { + case gpu::RGB: + texel.internalFormat = GL_RGB8; + break; + case gpu::RGBA: + texel.internalFormat = GL_RGBA8; + switch (dstFormat.getType()) { + case gpu::UINT32: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA32UI; + break; + case gpu::INT32: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA32I; + break; + case gpu::FLOAT: + texel.internalFormat = GL_RGBA32F; + break; + case gpu::UINT16: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA16UI; + break; + case gpu::INT16: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA16I; + break; + case gpu::NUINT16: + texel.format = GL_RGBA; + //texel.internalFormat = GL_RGBA16; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_RGBA16"; + break; + case gpu::NINT16: + texel.format = GL_RGBA; + qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_RGBA16_SNORM"; + //texel.internalFormat = GL_RGBA16_SNORM; + break; + case gpu::HALF: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA16F; + break; + case gpu::UINT8: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA8UI; + break; + case gpu::INT8: + texel.format = GL_RGBA_INTEGER; + texel.internalFormat = GL_RGBA8I; + break; + case gpu::NUINT8: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8; + break; + case gpu::NINT8: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8_SNORM; + break; + case gpu::NUINT32: + case gpu::NINT32: + case gpu::NUM_TYPES: // quiet compiler + Q_UNREACHABLE(); + } + break; + case gpu::SRGB: + //texel.internalFormat = GL_SRGB8; + texel.internalFormat = GL_RGB8; // standard 2.2 gamma correction color + break; + case gpu::SRGBA: + texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color + break; + //CLIMAX_MERGE_START + // case gpu::COMPRESSED_RGBA: + // //texel.internalFormat = GL_COMPRESSED_RGBA; + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGBA"; + // break; + // case gpu::COMPRESSED_SRGBA: + // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB_ALPHA"; + // //texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA; + // break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + //qDebug() << "GLTexelFormat::evalGLTexelFormat Texel.type " << texel.type << " - texel.format=" << texel.format << " texel.internalFormat=" << texel.internalFormat; + return texel; + } +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h new file mode 100644 index 0000000000..94ded3dc23 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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_GLTexelFormat_h +#define hifi_gpu_gl_GLTexelFormat_h + +#include "GLShared.h" + +namespace gpu { namespace gl { + +class GLTexelFormat { +public: + GLenum internalFormat; + GLenum format; + GLenum type; + + static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) { + return evalGLTexelFormat(dstFormat, dstFormat); + } + static GLenum evalGLTexelFormatInternal(const Element& dstFormat); + + static GLTexelFormat evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat); +}; + +} } + + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp new file mode 100644 index 0000000000..5f5e3a9be1 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp @@ -0,0 +1,323 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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 "GLTexture.h" + +#include + +#include "GLTextureTransfer.h" +#include "GLBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +std::shared_ptr GLTexture::_textureTransferHelper; + +// FIXME placeholder for texture memory over-use +#define DEFAULT_MAX_MEMORY_MB 256 +#define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f +#define OVER_MEMORY_PRESSURE 2.0f + +const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; + +const GLenum GLTexture::WRAP_MODES[Sampler::NUM_WRAP_MODES] = { + GL_REPEAT, // WRAP_REPEAT, + GL_MIRRORED_REPEAT, // WRAP_MIRROR, + GL_CLAMP_TO_EDGE, // WRAP_CLAMP, + GL_CLAMP_TO_BORDER_EXT, // WRAP_BORDER, + + //GL_MIRROR_CLAMP_TO_EDGE_EXT // WRAP_MIRROR_ONCE, +// qDebug() << "TODO: GLTexture.cpp:WRAP_MODES GL_MIRROR_CLAMP_TO_EDGE_EXT"; +}; + +const GLFilterMode GLTexture::FILTER_MODES[Sampler::NUM_FILTERS] = { + { GL_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_POINT, + { GL_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR, + { GL_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT, + { GL_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR, + + { GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT, + { GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_MAG_POINT_MIP_LINEAR, + { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, + { GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_POINT_MAG_MIP_LINEAR, + { GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_MIP_POINT, + { GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, + { GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR_MIP_POINT, + { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_MIP_LINEAR, + { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR } //FILTER_ANISOTROPIC, +}; + +GLenum GLTexture::getGLTextureType(const Texture& texture) { + switch (texture.getType()) { + case Texture::TEX_2D: + return GL_TEXTURE_2D; + break; + + case Texture::TEX_CUBE: + return GL_TEXTURE_CUBE_MAP; + break; + + default: + qFatal("Unsupported texture type"); + } + Q_UNREACHABLE(); + return GL_TEXTURE_2D; +} + + +const std::vector& GLTexture::getFaceTargets(GLenum target) { + static std::vector cubeFaceTargets { + 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 + }; + static std::vector faceTargets { + GL_TEXTURE_2D + }; + switch (target) { + case GL_TEXTURE_2D: + return faceTargets; + case GL_TEXTURE_CUBE_MAP: + return cubeFaceTargets; + default: + Q_UNREACHABLE(); + break; + } + Q_UNREACHABLE(); + return faceTargets; +} + +// Default texture memory = GPU total memory - 2GB +#define GPU_MEMORY_RESERVE_BYTES MB_TO_BYTES(2048) +// Minimum texture memory = 1GB +#define TEXTURE_MEMORY_MIN_BYTES MB_TO_BYTES(1024) + + +float GLTexture::getMemoryPressure() { + // Check for an explicit memory limit + auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage(); + + + // If no memory limit has been set, use a percentage of the total dedicated memory + if (!availableTextureMemory) { +#if 0 + auto totalMemory = getDedicatedMemory(); + if ((GPU_MEMORY_RESERVE_BYTES + TEXTURE_MEMORY_MIN_BYTES) > totalMemory) { + availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; + } else { + availableTextureMemory = totalMemory - GPU_MEMORY_RESERVE_BYTES; + } +#else + // Hardcode texture limit for sparse textures at 1 GB for now + availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; +#endif + } + + // Return the consumed texture memory divided by the available texture memory. + //CLIMAX_MERGE_START + //auto consumedGpuMemory = Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage(); + //float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory; + //static Context::Size lastConsumedGpuMemory = 0; + //if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) { + // lastConsumedGpuMemory = consumedGpuMemory; + // qCDebug(gpugllogging) << "Exceeded max allowed texture memory: " << consumedGpuMemory << " / " << availableTextureMemory; + //} + //return memoryPressure; + return 0; + +} + + +// Create the texture and allocate storage +GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : + GLObject(backend, texture, id), + _external(false), + _source(texture.source()), + _storageStamp(texture.getStamp()), + _target(getGLTextureType(texture)), + _internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())), + _maxMip(texture.getMaxMip()), + _minMip(texture.getMinMip()), + _virtualSize(texture.evalTotalSize()), + _transferrable(transferrable) +{ + //qDebug() << "GLTexture::GLTexture building GLTexture with _internalFormat" << _internalFormat; + auto strongBackend = _backend.lock(); + strongBackend->recycle(); + //CLIMAX_MERGE_START + //Backend::incrementTextureGPUCount(); + //Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize); + //CLIMAX_MERGE_END + Backend::setGPUObject(texture, this); +} + +GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : + GLObject(backend, texture, id), + _external(true), + _source(texture.source()), + _storageStamp(0), + _target(getGLTextureType(texture)), + _internalFormat(GL_RGBA8), + // FIXME force mips to 0? + _maxMip(texture.getMaxMip()), + _minMip(texture.getMinMip()), + _virtualSize(0), + _transferrable(false) +{ + 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); + //} + } + //Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); + //CLIMAX_MERGE_END +} + +void GLTexture::createTexture() { + withPreservedTexture([&] { + allocateStorage(); + (void)CHECK_GL_ERROR(); + syncSampler(); + (void)CHECK_GL_ERROR(); + }); +} + +void GLTexture::withPreservedTexture(std::function f) const { + GLint boundTex = -1; + switch (_target) { + case GL_TEXTURE_2D: + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); + break; + + case GL_TEXTURE_CUBE_MAP: + glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); + break; + + default: + qFatal("Unsupported texture type"); + } + (void)CHECK_GL_ERROR(); + + glBindTexture(_target, _texture); + f(); + glBindTexture(_target, boundTex); + (void)CHECK_GL_ERROR(); +} + +void GLTexture::setSize(GLuint size) const { + ////CLIMAX_MERGE_START + //if (!_external && !_transferrable) { + // Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0); + //} + //Backend::updateTextureGPUMemoryUsage(_size, size); + const_cast(_size) = size; +} + +bool GLTexture::isInvalid() const { + return _storageStamp < _gpuObject.getStamp(); +} + +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()) { + return false; + } + + auto syncState = _syncState.load(); + if (isOutdated() || Idle != syncState) { + return false; + } + + return true; +} + + +// 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; + + // // 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 +} + +void GLTexture::initTextureTransferHelper() { + _textureTransferHelper = std::make_shared(); +} + +void GLTexture::startTransfer() { + createTexture(); +} + +void GLTexture::finishTransfer() { + if (_gpuObject.isAutogenerateMips()) { + generateMips(); + } +} + diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.h b/libraries/gpu-gles/src/gpu/gl/GLTexture.h new file mode 100644 index 0000000000..03353ae67d --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTexture.h @@ -0,0 +1,233 @@ +// +// Created by Bradley Austin Davis on 2016/05/15 +// 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_GLTexture_h +#define hifi_gpu_gl_GLTexture_h + +#include "GLShared.h" +#include "GLTextureTransfer.h" +#include "GLBackend.h" +#include "GLTexelFormat.h" + +namespace gpu { namespace gl { + +struct GLFilterMode { + GLint minFilter; + GLint magFilter; +}; + + +class GLTexture : public GLObject { +public: + static const uint16_t INVALID_MIP { (uint16_t)-1 }; + static const uint8_t INVALID_FACE { (uint8_t)-1 }; + + static void initTextureTransferHelper(); + static std::shared_ptr _textureTransferHelper; + + template + static GLTexture* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) { + const Texture& texture = *texturePointer; + + // Special case external textures + //CLIMAX_MERGE_START + //Z:/HiFi_Android/HiFi_GIT/libraries/gpu-gl-android/src/gpu/gl/../gles/../gl/GLTexture.h:37:32: error: no member named 'isExternal' in 'gpu::Texture::Usage' + // The only instance of this being used again. replace. + // if (texture.getUsage().isExternal()) { + // Texture::ExternalUpdates updates = texture.getUpdates(); + // if (!updates.empty()) { + // Texture::ExternalRecycler recycler = texture.getExternalRecycler(); + // Q_ASSERT(recycler); + // // Discard any superfluous updates + // while (updates.size() > 1) { + // const auto& update = updates.front(); + // // Superfluous updates will never have been read, but we want to ensure the previous + // // writes to them are complete before they're written again, so return them with the + // // same fences they arrived with. This can happen on any thread because no GL context + // // work is involved + // recycler(update.first, update.second); + // updates.pop_front(); + // } + + // // The last texture remaining is the one we'll use to create the GLTexture + // const auto& update = updates.front(); + // // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence + // if (update.second) { + // GLsync fence = static_cast(update.second); + // glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + // glDeleteSync(fence); + // } + + // // Create the new texture object (replaces any previous texture object) + // new GLTextureType(backend.shared_from_this(), texture, update.first); + // } + + + // Return the texture object (if any) associated with the texture, without extensive logic + // (external textures are + //return Backend::getGPUObject(texture); + //} + //CLIMAX_MERGE_END + if (!texture.isDefined()) { + // NO texture definition yet so let's avoid thinking + return nullptr; + } + + // If the object hasn't been created, or the object definition is out of date, drop and re-create + GLTexture* object = Backend::getGPUObject(texture); + + // Create the texture if need be (force re-creation if the storage stamp changes + // for easier use of immutable storage) + if (!object || object->isInvalid()) { + // This automatically any previous texture + object = new GLTextureType(backend.shared_from_this(), texture, needTransfer); + if (!object->_transferrable) { + object->createTexture(); + object->_contentStamp = texture.getDataStamp(); + object->updateSize(); + object->postTransfer(); + } + } + + // Object maybe doens't neet to be tranasferred after creation + if (!object->_transferrable) { + return object; + } + + // If we just did a transfer, return the object after doing post-transfer work + if (GLSyncState::Transferred == object->getSyncState()) { + object->postTransfer(); + } + + if (object->isOutdated()) { + // Object might be outdated, if so, start the transfer + // (outdated objects that are already in transfer will have reported 'true' for ready() + _textureTransferHelper->transferTexture(texturePointer); + return nullptr; + } + + if (!object->isReady()) { + return nullptr; + } + + ((GLTexture*)object)->updateMips(); + + return object; + } + + template + static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) { + if (!texture) { + return 0; + } + GLTexture* object { nullptr }; + if (shouldSync) { + object = sync(backend, texture, shouldSync); + } else { + object = Backend::getGPUObject(*texture); + } + + if (!object) { + return 0; + } + + if (!shouldSync) { + return object->_id; + } + + // Don't return textures that are in transfer state + if ((object->getSyncState() != GLSyncState::Idle) || + // Don't return transferrable textures that have never completed transfer + (!object->_transferrable || 0 != object->_transferCount)) { + return 0; + } + + return object->_id; + } + + ~GLTexture(); + + // Is this texture generated outside the GPU library? + const bool _external; + const GLuint& _texture { _id }; + const std::string _source; + const Stamp _storageStamp; + const GLenum _target; + const GLenum _internalFormat; + const uint16 _maxMip; + uint16 _minMip; + const GLuint _virtualSize; // theoretical size as expected + Stamp _contentStamp { 0 }; + const bool _transferrable; + Size _transferCount { 0 }; + GLuint size() const { return _size; } + GLSyncState getSyncState() const { return _syncState; } + + // Is the storage out of date relative to the gpu texture? + bool isInvalid() const; + + // 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 GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS]; + static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; + + // Return a floating point value indicating how much of the allowed + // texture memory we are currently consuming. A value of 0 indicates + // no texture memory usage, while a value of 1 indicates all available / allowed memory + // is consumed. A value above 1 indicates that there is a problem. + static float getMemoryPressure(); +protected: + + static const std::vector& getFaceTargets(GLenum textureType); + + static GLenum getGLTextureType(const Texture& texture); + + + const GLuint _size { 0 }; // true size as reported by the gl api + std::atomic _syncState { GLSyncState::Idle }; + + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable); + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); + + void setSyncState(GLSyncState syncState) { _syncState = syncState; } + + void createTexture(); + + virtual void updateMips() {} + virtual void allocateStorage() const = 0; + virtual void updateSize() const = 0; + virtual void syncSampler() const = 0; + virtual void generateMips() const = 0; + virtual void withPreservedTexture(std::function f) const; + +protected: + void setSize(GLuint size) const; + + 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; +}; + +} } + +#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp new file mode 100644 index 0000000000..cec46cb90d --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp @@ -0,0 +1,207 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLTextureTransfer.h" + +#include +#include + +#include "GLShared.h" +#include "GLTexture.h" + +#ifdef HAVE_NSIGHT +#include "nvToolsExt.h" +std::unordered_map _map; +#endif + + +#ifdef TEXTURE_TRANSFER_PBOS +#define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024) +#define TEXTURE_TRANSFER_PBO_COUNT 128 +#endif + +using namespace gpu; +using namespace gpu::gl; + +GLTextureTransferHelper::GLTextureTransferHelper() { +#ifdef THREADED_TEXTURE_TRANSFER + setObjectName("TextureTransferThread"); + _context.create(); + initialize(true, QThread::LowPriority); + // Clean shutdown on UNIX, otherwise _canvas is freed early + connect(qApp, &QCoreApplication::aboutToQuit, [&] { terminate(); }); +#else + initialize(false, QThread::LowPriority); +#endif +} + +GLTextureTransferHelper::~GLTextureTransferHelper() { +#ifdef THREADED_TEXTURE_TRANSFER + if (isStillRunning()) { + terminate(); + } +#else + terminate(); +#endif +} + +void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { + GLTexture* object = Backend::getGPUObject(*texturePointer); + + //CLIMAX_MERGE_START + //Backend::incrementTextureGPUTransferCount(); + object->setSyncState(GLSyncState::Pending); + Lock lock(_mutex); + _pendingTextures.push_back(texturePointer); +} + +void GLTextureTransferHelper::setup() { +#ifdef THREADED_TEXTURE_TRANSFER + _context.makeCurrent(); + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // FIXME don't use opengl 4.5 DSA functionality without verifying it's present + glCreateRenderbuffers(1, &_drawRenderbuffer); + glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128); + glCreateFramebuffers(1, &_drawFramebuffer); + glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer); + glCreateFramebuffers(1, &_readFramebuffer); +#endif + +#ifdef TEXTURE_TRANSFER_PBOS + std::array pbos; + glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]); + for (uint32_t i = 0; i < TEXTURE_TRANSFER_PBO_COUNT; ++i) { + TextureTransferBlock newBlock; + newBlock._pbo = pbos[i]; + glNamedBufferStorage(newBlock._pbo, TEXTURE_TRANSFER_BLOCK_SIZE, 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); + newBlock._mapped = glMapNamedBufferRange(newBlock._pbo, 0, TEXTURE_TRANSFER_BLOCK_SIZE, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); + _readyQueue.push(newBlock); + } +#endif +#endif +} + +void GLTextureTransferHelper::shutdown() { +#ifdef THREADED_TEXTURE_TRANSFER + _context.makeCurrent(); +#endif + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); + glDeleteFramebuffers(1, &_drawFramebuffer); + _drawFramebuffer = 0; + glDeleteFramebuffers(1, &_readFramebuffer); + _readFramebuffer = 0; + + glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0); + glDeleteRenderbuffers(1, &_drawRenderbuffer); + _drawRenderbuffer = 0; +#endif +} + +void GLTextureTransferHelper::queueExecution(VoidLambda lambda) { + Lock lock(_mutex); + _pendingCommands.push_back(lambda); +} + +#define MAX_TRANSFERS_PER_PASS 2 + +bool GLTextureTransferHelper::process() { + // Take any new textures or commands off the queue + VoidLambdaList pendingCommands; + TextureList newTransferTextures; + { + Lock lock(_mutex); + newTransferTextures.swap(_pendingTextures); + pendingCommands.swap(_pendingCommands); + } + + if (!pendingCommands.empty()) { + for (auto command : pendingCommands) { + command(); + } + glFlush(); + } + + if (!newTransferTextures.empty()) { + for (auto& texturePointer : newTransferTextures) { +#ifdef HAVE_NSIGHT + _map[texturePointer] = nvtxRangeStart("TextureTansfer"); +#endif + GLTexture* object = Backend::getGPUObject(*texturePointer); + object->startTransfer(); + _transferringTextures.push_back(texturePointer); + _textureIterator = _transferringTextures.begin(); + } + _transferringTextures.sort([](const gpu::TexturePointer& a, const gpu::TexturePointer& b)->bool { + return a->getSize() < b->getSize(); + }); + } + + // No transfers in progress, sleep + if (_transferringTextures.empty()) { +#ifdef THREADED_TEXTURE_TRANSFER + QThread::usleep(1); +#endif + return true; + } + + static auto lastReport = usecTimestampNow(); + auto now = usecTimestampNow(); + auto lastReportInterval = now - lastReport; + if (lastReportInterval > USECS_PER_SECOND * 4) { + lastReport = now; + qDebug() << "Texture list " << _transferringTextures.size(); + } + + size_t transferCount = 0; + for (_textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) { + if (++transferCount > MAX_TRANSFERS_PER_PASS) { + break; + } + auto texture = *_textureIterator; + GLTexture* gltexture = Backend::getGPUObject(*texture); + if (gltexture->continueTransfer()) { + ++_textureIterator; + continue; + } + + gltexture->finishTransfer(); + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use +#endif + +#ifdef THREADED_TEXTURE_TRANSFER + clientWait(); +#endif + gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp(); + gltexture->updateSize(); + gltexture->setSyncState(gpu::gl::GLSyncState::Transferred); + //CLIMAX_MERGE_START + //Backend::decrementTextureGPUTransferCount(); +#ifdef HAVE_NSIGHT + // Mark the texture as transferred + nvtxRangeEnd(_map[texture]); + _map.erase(texture); +#endif + _textureIterator = _transferringTextures.erase(_textureIterator); + } + +#ifdef THREADED_TEXTURE_TRANSFER + if (!_transferringTextures.empty()) { + // Don't saturate the GPU + clientWait(); + } else { + // Don't saturate the CPU + QThread::msleep(1); + } +#endif + + return true; +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h new file mode 100644 index 0000000000..a23c282fd4 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h @@ -0,0 +1,78 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gl_GLTextureTransfer_h +#define hifi_gpu_gl_GLTextureTransfer_h + +#include +#include + +#include + +#include + +#include "GLShared.h" + +#ifdef Q_OS_WIN +#define THREADED_TEXTURE_TRANSFER +#endif + +#ifdef THREADED_TEXTURE_TRANSFER +// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread +// also, the current draw code is implicitly using OpenGL 4.5 functionality +//#define TEXTURE_TRANSFER_FORCE_DRAW +// FIXME PBO's increase the complexity and don't seem to work reliably +//#define TEXTURE_TRANSFER_PBOS +#endif + +namespace gpu { namespace gl { + +using TextureList = std::list; +using TextureListIterator = TextureList::iterator; + +class GLTextureTransferHelper : public GenericThread { +public: + using VoidLambda = std::function; + using VoidLambdaList = std::list; + using Pointer = std::shared_ptr; + GLTextureTransferHelper(); + ~GLTextureTransferHelper(); + void transferTexture(const gpu::TexturePointer& texturePointer); + void queueExecution(VoidLambda lambda); + + void setup() override; + void shutdown() override; + bool process() override; + +private: +#ifdef THREADED_TEXTURE_TRANSFER + ::gl::OffscreenContext _context; +#endif + +#ifdef TEXTURE_TRANSFER_FORCE_DRAW + // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread + GLuint _drawRenderbuffer { 0 }; + GLuint _drawFramebuffer { 0 }; + GLuint _readFramebuffer { 0 }; +#endif + + // A mutex for protecting items access on the render and transfer threads + Mutex _mutex; + // Commands that have been submitted for execution on the texture transfer thread + VoidLambdaList _pendingCommands; + // Textures that have been submitted for transfer + TextureList _pendingTextures; + // Textures currently in the transfer process + // Only used on the transfer thread + TextureList _transferringTextures; + TextureListIterator _textureIterator; + +}; + +} } + +#endif \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp new file mode 100644 index 0000000000..8c843c1ce3 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp @@ -0,0 +1,197 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 9/27/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(gpugleslogging, "hifi.gpu.gles") + +using namespace gpu; +using namespace gpu::gles; + +const std::string GLESBackend::GLES_VERSION { "GLES" }; + +void GLESBackend::do_draw(const Batch& batch, size_t paramOffset) { + Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; + uint32 numVertices = batch._params[paramOffset + 1]._uint; + uint32 startVertex = batch._params[paramOffset + 0]._uint; + + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else + + setupStereoSide(0); + glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(1); + glDrawArrays(mode, startVertex, numVertices); + +#endif + _stats._DSNumTriangles += 2 * numVertices / 3; + _stats._DSNumDrawcalls += 2; + + } else { + glDrawArrays(mode, startVertex, numVertices); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void) CHECK_GL_ERROR(); +} + +void GLESBackend::do_drawIndexed(const Batch& batch, size_t paramOffset) { + Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; + uint32 numIndices = batch._params[paramOffset + 1]._uint; + uint32 startIndex = batch._params[paramOffset + 0]._uint; + + GLenum glType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType]; + + auto typeByteSize = TYPE_SIZE[_input._indexBufferType]; + GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); + + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawElementsInstanced(mode, numIndices, glType, indexBufferByteOffset, 2); +#else + setupStereoSide(0); + glDrawElements(mode, numIndices, glType, indexBufferByteOffset); + setupStereoSide(1); + glDrawElements(mode, numIndices, glType, indexBufferByteOffset); +#endif + _stats._DSNumTriangles += 2 * numIndices / 3; + _stats._DSNumDrawcalls += 2; + } else { + //qDebug() << "GLESBackend::do_drawIndexed glDrawElements " << numIndices; + glDrawElements(mode, numIndices, glType, indexBufferByteOffset); + _stats._DSNumTriangles += numIndices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void) CHECK_GL_ERROR(); +} + +void GLESBackend::do_drawInstanced(const Batch& batch, size_t paramOffset) { + GLint numInstances = batch._params[paramOffset + 4]._uint; + Primitive primitiveType = (Primitive)batch._params[paramOffset + 3]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; + uint32 numVertices = batch._params[paramOffset + 2]._uint; + uint32 startVertex = batch._params[paramOffset + 1]._uint; + + + if (isStereo()) { + GLint trueNumInstances = 2 * numInstances; + +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, trueNumInstances); +#else + setupStereoSide(0); + glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); + setupStereoSide(1); + glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); +#endif + _stats._DSNumTriangles += (trueNumInstances * numVertices) / 3; + _stats._DSNumDrawcalls += trueNumInstances; + } else { + //qDebug() << "GLESBackend::do_drawInstanced glDrawArraysInstancedEXT " << numVertices << "," << numInstances; + glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); + _stats._DSNumTriangles += (numInstances * numVertices) / 3; + _stats._DSNumDrawcalls += numInstances; + } + _stats._DSNumAPIDrawcalls++; + + (void) CHECK_GL_ERROR(); +} + +void glbackend_glDrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount, GLint basevertex, GLuint baseinstance) { +//#if (GPU_INPUT_PROFILE == GPU_CORE_43) + //glDrawElementsInstancedBaseVertexBaseInstance(mode, count, type, indices, primcount, basevertex, baseinstance); +//#else + glDrawElementsInstanced(mode, count, type, indices, primcount); +//#endif +} + +void GLESBackend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) { + GLint numInstances = batch._params[paramOffset + 4]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 3]._uint]; + uint32 numIndices = batch._params[paramOffset + 2]._uint; + uint32 startIndex = batch._params[paramOffset + 1]._uint; + // FIXME glDrawElementsInstancedBaseVertexBaseInstance is only available in GL 4.3 + // and higher, so currently we ignore this field + uint32 startInstance = batch._params[paramOffset + 0]._uint; + GLenum glType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType]; + + auto typeByteSize = TYPE_SIZE[_input._indexBufferType]; + GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); + + if (isStereo()) { + GLint trueNumInstances = 2 * numInstances; + +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, trueNumInstances, 0, startInstance); +#else + setupStereoSide(0); + glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); + setupStereoSide(1); + glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); +#endif + + _stats._DSNumTriangles += (trueNumInstances * numIndices) / 3; + _stats._DSNumDrawcalls += trueNumInstances; + } else { + //qDebug() << "GLESBackend::do_drawIndexedInstanced glbackend_glDrawElementsInstancedBaseVertexBaseInstance " << numInstances; + glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); + _stats._DSNumTriangles += (numInstances * numIndices) / 3; + _stats._DSNumDrawcalls += numInstances; + } + + _stats._DSNumAPIDrawcalls++; + + (void)CHECK_GL_ERROR(); +} + + +void GLESBackend::do_multiDrawIndirect(const Batch& batch, size_t paramOffset) { +#if (GPU_INPUT_PROFILE == GPU_CORE_43) + uint commandCount = batch._params[paramOffset + 0]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 1]._uint]; + + //glMultiDrawArraysIndirect(mode, reinterpret_cast(_input._indirectBufferOffset), commandCount, (GLsizei)_input._indirectBufferStride); + qDebug() << "TODO: GLESBackend.cpp:do_multiDrawIndirect do_multiDrawIndirect"; + _stats._DSNumDrawcalls += commandCount; + _stats._DSNumAPIDrawcalls++; + +#else + // FIXME implement the slow path +#endif + (void)CHECK_GL_ERROR(); + +} + +void GLESBackend::do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) { +//#if (GPU_INPUT_PROFILE == GPU_CORE_43) + uint commandCount = batch._params[paramOffset + 0]._uint; + GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 1]._uint]; + GLenum indexType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType]; + + //glMultiDrawElementsIndirect(mode, indexType, reinterpret_cast(_input._indirectBufferOffset), commandCount, (GLsizei)_input._indirectBufferStride); + qDebug() << "TODO: GLESBackend.cpp:do_multiDrawIndexedIndirect glMultiDrawElementsIndirect"; + _stats._DSNumDrawcalls += commandCount; + _stats._DSNumAPIDrawcalls++; +//#else + // FIXME implement the slow path +//#endif + (void)CHECK_GL_ERROR(); +} diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h new file mode 100644 index 0000000000..69a417d952 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h @@ -0,0 +1,99 @@ +// +// GLESBackend.h +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Gabriel Calero & Cristian Duarte on 9/27/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_gles_GLESBackend_h +#define hifi_gpu_gles_GLESBackend_h + +#include + +#include "../gl/GLBackend.h" +#include "../gl/GLTexture.h" + + +namespace gpu { namespace gles { + +using namespace gpu::gl; + +class GLESBackend : public GLBackend { + using Parent = GLBackend; + // Context Backend static interface required + friend class Context; + +public: + explicit GLESBackend(bool syncCache) : Parent(syncCache) {} + GLESBackend() : Parent() {} + virtual ~GLESBackend() { + // call resetStages here rather than in ~GLBackend dtor because it will call releaseResourceBuffer + // which is pure virtual from GLBackend's dtor. + resetStages(); + } + + static const std::string GLES_VERSION; + const std::string& getVersion() const override { return GLES_VERSION; } + + + class GLESTexture : public GLTexture { + using Parent = GLTexture; + GLuint allocate(); + public: + GLESTexture(const std::weak_ptr& backend, const Texture& buffer, GLuint externalId); + GLESTexture(const std::weak_ptr& backend, const Texture& buffer, bool transferrable); + + 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; + void generateMips() const override; + }; + + +protected: + GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; + GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; + + GLuint getBufferID(const Buffer& buffer) override; + GLBuffer* syncGPUObject(const Buffer& buffer) override; + + GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override; + GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override; + + GLuint getQueryID(const QueryPointer& query) override; + GLQuery* syncGPUObject(const Query& query) override; + + // Draw Stage + void do_draw(const Batch& batch, size_t paramOffset) override; + void do_drawIndexed(const Batch& batch, size_t paramOffset) override; + void do_drawInstanced(const Batch& batch, size_t paramOffset) override; + void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override; + void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) override; + void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) override; + + // Input Stage + void updateInput() override; + void resetInputStage() 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(); + + // Output stage + void do_blit(const Batch& batch, size_t paramOffset) override; +}; + +} } + +Q_DECLARE_LOGGING_CATEGORY(gpugleslogging) + + +#endif \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp new file mode 100644 index 0000000000..f6bdea45af --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp @@ -0,0 +1,69 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 +// 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 "GLESBackend.h" +#include "../gl/GLBuffer.h" + +namespace gpu { + namespace gles { + + class GLESBuffer : public gpu::gl::GLBuffer { + using Parent = gpu::gl::GLBuffer; + static GLuint allocate() { + GLuint result; + glGenBuffers(1, &result); + return result; + } + + public: + GLESBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLESBuffer* original) : Parent(backend, buffer, allocate()) { + glBindBuffer(GL_ARRAY_BUFFER, _buffer); + glBufferData(GL_ARRAY_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + if (original && original->_size) { + glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer); + glBindBuffer(GL_COPY_READ_BUFFER, original->_buffer); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, original->_size); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + glBindBuffer(GL_COPY_READ_BUFFER, 0); + (void)CHECK_GL_ERROR(); + } + Backend::setGPUObject(buffer, this); + } + + void transfer() override { + glBindBuffer(GL_ARRAY_BUFFER, _buffer); + (void)CHECK_GL_ERROR(); + Size offset; + Size size; + Size currentPage { 0 }; + auto data = _gpuObject._renderSysmem.readData(); + while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) { + glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset); + (void)CHECK_GL_ERROR(); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + (void)CHECK_GL_ERROR(); + _gpuObject._renderPages._flags &= ~PageManager::DIRTY; + } + }; + } +} + +using namespace gpu; +using namespace gpu::gl; +using namespace gpu::gles; + + +GLuint GLESBackend::getBufferID(const Buffer& buffer) { + return GLESBuffer::getId(*this, buffer); +} + +GLBuffer* GLESBackend::syncGPUObject(const Buffer& buffer) { + return GLESBuffer::sync(*this, buffer); +} diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendInput.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendInput.cpp new file mode 100644 index 0000000000..d37a01eb90 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendInput.cpp @@ -0,0 +1,29 @@ +// +// GLESBackendInput.cpp +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Cristian Duarte & Gabriel Calero on 10/7/2016. +// 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 +// +#include "GLESBackend.h" + +using namespace gpu; +using namespace gpu::gles; + +void GLESBackend::updateInput() { + Parent::updateInput(); +} + + +void GLESBackend::resetInputStage() { + Parent::resetInputStage(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + for (uint32_t i = 0; i < _input._attributeActivation.size(); i++) { + glDisableVertexAttribArray(i); + glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, 0); + } +} \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp new file mode 100644 index 0000000000..8bf9267fde --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp @@ -0,0 +1,169 @@ +// +// GLESBackendOutput.cpp +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Gabriel Calero & Cristian Duarte on 9/27/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +#include + +#include "../gl/GLFramebuffer.h" +#include "../gl/GLTexture.h" + +namespace gpu { namespace gles { + +class GLESFramebuffer : public gl::GLFramebuffer { + using Parent = gl::GLFramebuffer; + static GLuint allocate() { + GLuint result; + glGenFramebuffers(1, &result); + return result; + } +public: + void update() override { + GLint currentFBO = -1; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO); + glBindFramebuffer(GL_FRAMEBUFFER, _fbo); + gl::GLTexture* gltexture = nullptr; + TexturePointer surface; + if (_gpuObject.getColorStamps() != _colorStamps) { + if (_gpuObject.hasColor()) { + _colorBuffers.clear(); + static const GLenum colorAttachments[] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3, + GL_COLOR_ATTACHMENT4, + GL_COLOR_ATTACHMENT5, + GL_COLOR_ATTACHMENT6, + GL_COLOR_ATTACHMENT7, + GL_COLOR_ATTACHMENT8, + GL_COLOR_ATTACHMENT9, + GL_COLOR_ATTACHMENT10, + GL_COLOR_ATTACHMENT11, + GL_COLOR_ATTACHMENT12, + GL_COLOR_ATTACHMENT13, + GL_COLOR_ATTACHMENT14, + GL_COLOR_ATTACHMENT15 }; + + int unit = 0; + for (auto& b : _gpuObject.getRenderBuffers()) { + surface = b._texture; + if (surface) { + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + } else { + gltexture = nullptr; + } + + if (gltexture) { + glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[unit], GL_TEXTURE_2D, gltexture->_texture, 0); + _colorBuffers.push_back(colorAttachments[unit]); + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[unit], GL_TEXTURE_2D, 0, 0); + } + unit++; + } + } + _colorStamps = _gpuObject.getColorStamps(); + } + + GLenum attachement = GL_DEPTH_STENCIL_ATTACHMENT; + if (!_gpuObject.hasStencil()) { + attachement = GL_DEPTH_ATTACHMENT; + } else if (!_gpuObject.hasDepth()) { + attachement = GL_STENCIL_ATTACHMENT; + } + + if (_gpuObject.getDepthStamp() != _depthStamp) { + auto surface = _gpuObject.getDepthStencilBuffer(); + if (_gpuObject.hasDepthStencil() && surface) { + gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + } + + if (gltexture) { + glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, gltexture->_texture, 0); + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, 0, 0); + } + _depthStamp = _gpuObject.getDepthStamp(); + } + + + // Last but not least, define where we draw + if (!_colorBuffers.empty()) { + glDrawBuffers((GLsizei)_colorBuffers.size(), _colorBuffers.data()); + } else { + glDrawBuffers(1, {GL_NONE}); + } + + // Now check for completness + _status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + // restore the current framebuffer + if (currentFBO != -1) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO); + } + + checkStatus(GL_DRAW_FRAMEBUFFER); + } + + +public: + GLESFramebuffer(const std::weak_ptr& backend, const gpu::Framebuffer& framebuffer) + : Parent(backend, framebuffer, allocate()) { } +}; + +gl::GLFramebuffer* gpu::gles::GLESBackend::syncGPUObject(const Framebuffer& framebuffer) { + return GLESFramebuffer::sync(*this, framebuffer); +} + +GLuint GLESBackend::getFramebufferID(const FramebufferPointer& framebuffer) { + return framebuffer ? GLESFramebuffer::getId(*this, *framebuffer) : 0; +} + +void GLESBackend::do_blit(const Batch& batch, size_t paramOffset) { + auto srcframebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); + Vec4i srcvp; + for (auto i = 0; i < 4; ++i) { + srcvp[i] = batch._params[paramOffset + 1 + i]._int; + } + + auto dstframebuffer = batch._framebuffers.get(batch._params[paramOffset + 5]._uint); + Vec4i dstvp; + for (auto i = 0; i < 4; ++i) { + dstvp[i] = batch._params[paramOffset + 6 + i]._int; + } + + // Assign dest framebuffer if not bound already + auto newDrawFBO = getFramebufferID(dstframebuffer); + if (_output._drawFBO != newDrawFBO) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, newDrawFBO); + } + + // always bind the read fbo + glBindFramebuffer(GL_READ_FRAMEBUFFER, getFramebufferID(srcframebuffer)); + + // Blit! + glBlitFramebuffer(srcvp.x, srcvp.y, srcvp.z, srcvp.w, + dstvp.x, dstvp.y, dstvp.z, dstvp.w, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + + // Always clean the read fbo to 0 + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + // Restore draw fbo if changed + if (_output._drawFBO != newDrawFBO) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _output._drawFBO); + } + + (void) CHECK_GL_ERROR(); +} + + +} } diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp new file mode 100644 index 0000000000..db541b07bc --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp @@ -0,0 +1,38 @@ +// +// GLESBackendQuery.cpp +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Gabriel Calero & Cristian Duarte on 9/27/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +#include "../gl/GLQuery.h" + +using namespace gpu; +using namespace gpu::gl; +using namespace gpu::gles; + +class GLESQuery : public GLQuery { + using Parent = GLQuery; +public: + static GLuint allocateQuery() { + GLuint result; + glGenQueries(1, &result); + return result; + } + + GLESQuery(const std::weak_ptr& backend, const Query& query) + : Parent(backend, query, allocateQuery(), allocateQuery()) { } +}; + +GLQuery* GLESBackend::syncGPUObject(const Query& query) { + return GLESQuery::sync(*this, query); +} + +GLuint GLESBackend::getQueryID(const QueryPointer& query) { + return GLESQuery::getId(*this, query); +} diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp new file mode 100644 index 0000000000..31a98edd12 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp @@ -0,0 +1,176 @@ +// +// GLESBackendTexture.cpp +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Gabriel Calero & Cristian Duarte on 9/27/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +#include +#include +#include + +// #include "../gl/GLTexelFormat.h" + +using namespace gpu; +using namespace gpu::gl; +using namespace gpu::gles; + +//using GL41TexelFormat = GLTexelFormat; +using GLESTexture = GLESBackend::GLESTexture; + +GLuint GLESTexture::allocate() { + //CLIMAX_MERGE_START + //Backend::incrementTextureGPUCount(); + //CLIMAX_MERGE_END + GLuint result; + glGenTextures(1, &result); + return result; +} + +GLuint GLESBackend::getTextureID(const TexturePointer& texture, bool transfer) { + return GLESTexture::getId(*this, texture, transfer); +} + +GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texture, bool transfer) { + return GLESTexture::sync(*this, texture, transfer); +} + +GLESTexture::GLESTexture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) + : GLTexture(backend, texture, externalId) { +} + +GLESTexture::GLESTexture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) + : GLTexture(backend, texture, allocate(), transferrable) { +} + +void GLESTexture::generateMips() const { + withPreservedTexture([&] { + glGenerateMipmap(_target); + }); + (void)CHECK_GL_ERROR(); +} + +void GLESTexture::allocateStorage() const { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); + (void)CHECK_GL_ERROR(); + glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); + (void)CHECK_GL_ERROR(); +/* if (GLEW_VERSION_4_2 && !_gpuObject.getTexelFormat().isCompressed()) { + // Get the dimensions, accounting for the downgrade level + Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip); + glTexStorage2D(_target, usedMipLevels(), texelFormat.internalFormat, dimensions.x, dimensions.y); + (void)CHECK_GL_ERROR(); + } else {*/ + for (uint16_t l = _minMip; l <= _maxMip; l++) { + // Get the mip level dimensions, accounting for the downgrade level + Vec3u dimensions = _gpuObject.evalMipDimensions(l); + for (GLenum target : getFaceTargets(_target)) { + glTexImage2D(target, l - _minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); + (void)CHECK_GL_ERROR(); + } + } + //} +} + +void GLESTexture::updateSize() const { + setSize(_virtualSize); + if (!_id) { + return; + } + + if (_gpuObject.getTexelFormat().isCompressed()) { + GLenum proxyType = GL_TEXTURE_2D; + GLuint numFaces = 1; + if (_gpuObject.getType() == gpu::Texture::TEX_CUBE) { + proxyType = CUBE_FACE_LAYOUT[0]; + numFaces = (GLuint)CUBE_NUM_FACES; + } + GLint gpuSize{ 0 }; + glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize); + (void)CHECK_GL_ERROR(); + + if (gpuSize) { + for (GLuint level = _minMip; level < _maxMip; level++) { + GLint levelSize{ 0 }; + //glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize); + //qDebug() << "TODO: GLBackendTexture.cpp:updateSize GL_TEXTURE_COMPRESSED_IMAGE_SIZE"; + levelSize *= numFaces; + + if (levelSize <= 0) { + break; + } + gpuSize += levelSize; + } + (void)CHECK_GL_ERROR(); + setSize(gpuSize); + return; + } + } +} + +// Move content bits from the CPU to the GPU for a given mip / face +void GLESTexture::transferMip(uint16_t mipLevel, uint8_t face) const { + auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); + //GLenum target = getFaceTargets()[face]; + GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face]; + auto size = _gpuObject.evalMipDimensions(mipLevel); + glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + (void)CHECK_GL_ERROR(); +} + +void GLESTexture::startTransfer() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Parent::startTransfer(); + + glBindTexture(_target, _id); + (void)CHECK_GL_ERROR(); + + // transfer pixels from each faces + uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1; + for (uint8_t f = 0; f < numFaces; f++) { + for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + if (_gpuObject.isStoredMipFaceAvailable(i, f)) { + transferMip(i, f); + } + } + } +} + +void GLESBackend::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_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); + } else { + glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + + glTexParameteri(_target, GL_TEXTURE_WRAP_S, WRAP_MODES[sampler.getWrapModeU()]); + glTexParameteri(_target, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); + glTexParameteri(_target, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); + + glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR_EXT, (const float*)&sampler.getBorderColor()); + + + 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"; + +} + diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp new file mode 100644 index 0000000000..5050db6edd --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp @@ -0,0 +1,67 @@ +// +// GLESBackendTransform.cpp +// libraries/gpu-gl-android/src/gpu/gles +// +// Created by Gabriel Calero & Cristian Duarte on 9/28/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" + +using namespace gpu; +using namespace gpu::gles; + +void GLESBackend::initTransform() { + glGenBuffers(1, &_transform._objectBuffer); + glGenBuffers(1, &_transform._cameraBuffer); + glGenBuffers(1, &_transform._drawCallInfoBuffer); + glGenTextures(1, &_transform._objectBufferTexture); + size_t cameraSize = sizeof(TransformStageState::CameraBufferElement); + while (_transform._cameraUboSize < cameraSize) { + _transform._cameraUboSize += _uboAlignment; + } +} + +void GLESBackend::transferTransformState(const Batch& batch) const { + // FIXME not thread safe + static std::vector bufferData; + if (!_transform._cameras.empty()) { + bufferData.resize(_transform._cameraUboSize * _transform._cameras.size()); + for (size_t i = 0; i < _transform._cameras.size(); ++i) { + memcpy(bufferData.data() + (_transform._cameraUboSize * i), &_transform._cameras[i], sizeof(TransformStageState::CameraBufferElement)); + } + glBindBuffer(GL_UNIFORM_BUFFER, _transform._cameraBuffer); + glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + + 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); + } + + if (!batch._namedData.empty()) { + bufferData.clear(); + for (auto& data : batch._namedData) { + auto currentSize = bufferData.size(); + auto bytesToCopy = data.second.drawCallInfos.size() * sizeof(Batch::DrawCallInfo); + bufferData.resize(currentSize + bytesToCopy); + memcpy(bufferData.data() + currentSize, data.second.drawCallInfos.data(), bytesToCopy); + _transform._drawCallInfoOffsets[data.first] = (GLvoid*)currentSize; + } + + glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer); + glBufferData(GL_ARRAY_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._objectBuffer); + + CHECK_GL_ERROR(); + + // Make sure the current Camera offset is unknown before render Draw + _transform._currentCameraOffset = INVALID_OFFSET; +} diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index c5df94235c..01cc652fd1 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -159,6 +159,7 @@ protected: friend class gl::GLBuffer; friend class gl41::GL41Buffer; friend class gl45::GL45Buffer; + friend class gles::GLESBuffer; }; using BufferUpdates = std::vector; diff --git a/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv b/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv index c9cc9e9dfa..1f788051bc 100755 --- a/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv +++ b/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv @@ -35,5 +35,5 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - varTexCoord0 = ((pos.xy + 1) * 0.5) * texcoordRect.zw + texcoordRect.xy; + varTexCoord0 = ((pos.xy + 1.0) * 0.5) * texcoordRect.zw + texcoordRect.xy; } diff --git a/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv b/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv index f55abf1089..845cf0326d 100755 --- a/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv +++ b/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv @@ -32,5 +32,5 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - varTexCoord0 = (pos.xy + 1) * 0.5; + varTexCoord0 = (pos.xy + 1.0) * 0.5; } diff --git a/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv b/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv index 1426ae48fd..289d8f96b1 100644 --- a/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv +++ b/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv @@ -23,7 +23,7 @@ void main(void) { ); vec4 pos = UNIT_QUAD[gl_VertexID]; - varTexCoord0 = (pos.xy + 1) * 0.5; + varTexCoord0 = (pos.xy + 1.0) * 0.5; gl_Position = pos; } diff --git a/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv b/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv index 1f9bc13700..554728417b 100755 --- a/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv +++ b/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv @@ -28,7 +28,7 @@ void main(void) { vec4 pos = UNIT_QUAD[gl_VertexID]; // standard transform but applied to the Texcoord - vec4 tc = vec4((pos.xy + 1) * 0.5, pos.zw); + vec4 tc = vec4((pos.xy + 1.0) * 0.5, pos.zw); TransformObject obj = getTransformObject(); <$transformModelToWorldPos(obj, tc, tc)$> diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index 88800652a5..8100efafaf 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -137,6 +137,11 @@ namespace gpu { class GL45Backend; class GL45Buffer; } + + namespace gles { + class GLESBackend; + class GLESBuffer; + } } #endif diff --git a/libraries/gpu/src/gpu/Frame.cpp b/libraries/gpu/src/gpu/Frame.cpp index 4854559d61..58ab7257f7 100644 --- a/libraries/gpu/src/gpu/Frame.cpp +++ b/libraries/gpu/src/gpu/Frame.cpp @@ -11,6 +11,10 @@ using namespace gpu; +Frame::Frame() { + batches.reserve(16); +} + Frame::~Frame() { if (framebuffer && framebufferRecycler) { framebufferRecycler(framebuffer); diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index bfebe85753..69872906e3 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -20,8 +20,8 @@ namespace gpu { friend class Context; public: + Frame(); virtual ~Frame(); - using Batches = std::vector; using FramebufferRecycler = std::function; using OverlayRecycler = std::function; diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index b49c681889..f1257e7c83 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -13,6 +13,7 @@ #include #include +#include #include using namespace gpu; diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index a65aaf765b..b3a500d68f 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -132,7 +132,11 @@ public: float getAspectRatio() const { return getWidth() / (float) getHeight() ; } +#ifndef ANDROID static const uint32 MAX_NUM_RENDER_BUFFERS = 8; +#else + static const uint32 MAX_NUM_RENDER_BUFFERS = 4; +#endif static uint32 getMaxNumRenderBuffers() { return MAX_NUM_RENDER_BUFFERS; } const GPUObjectPointer gpuObject {}; diff --git a/libraries/gpu/src/gpu/Query.cpp b/libraries/gpu/src/gpu/Query.cpp index 38a9d6db8c..03312e0db9 100644 --- a/libraries/gpu/src/gpu/Query.cpp +++ b/libraries/gpu/src/gpu/Query.cpp @@ -45,8 +45,8 @@ void Query::triggerReturnHandler(uint64_t queryResult, uint64_t batchElapsedTime RangeTimer::RangeTimer(const std::string& name) : _name(name) { for (int i = 0; i < QUERY_QUEUE_SIZE; i++) { - _timerQueries.push_back(std::make_shared([&, i] (const Query& query) { - _tailIndex ++; + _timerQueries.push_back(std::make_shared([this] (const Query& query) { + _tailIndex++; _movingAverageGPU.addSample(query.getGPUElapsedTime()); _movingAverageBatch.addSample(query.getBatchElapsedTime()); diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index b786222198..9feca4a3c9 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -43,16 +43,21 @@ layout(location=14) in int _inStereoSide; flat out int _stereoSide; // In stereo drawcall mode Instances are drawn twice (left then right) hence the true InstanceID is the gl_InstanceID / 2 -int gpu_InstanceID = gl_InstanceID >> 1; +int gpu_InstanceID() { + return gl_InstanceID >> 1; +} #else -int gpu_InstanceID = gl_InstanceID; - +int gpu_InstanceID() { + return gl_InstanceID; +} #endif #else -int gpu_InstanceID = gl_InstanceID; +int gpu_InstanceID() { + return gl_InstanceID; +} #endif @@ -166,7 +171,7 @@ TransformObject getTransformObject() { #ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN vec4 eyeClipEdge[2]= vec4[2](vec4(-1,0,0,1), vec4(1,0,0,1)); vec2 eyeOffsetScale = vec2(-0.5, +0.5); - uint eyeIndex = _stereoSide; + uint eyeIndex = uint(_stereoSide); gl_ClipDistance[0] = dot(<$clipPos$>, eyeClipEdge[eyeIndex]); float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w; <$clipPos$>.x = newClipPosX; diff --git a/libraries/image/CMakeLists.txt b/libraries/image/CMakeLists.txt index 85d3d8f1ae..442fa714b3 100644 --- a/libraries/image/CMakeLists.txt +++ b/libraries/image/CMakeLists.txt @@ -2,10 +2,10 @@ set(TARGET_NAME image) setup_hifi_library() link_hifi_libraries(shared gpu) -target_glm() - -add_dependency_external_projects(nvtt) -find_package(NVTT REQUIRED) -target_include_directories(${TARGET_NAME} PRIVATE ${NVTT_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${NVTT_LIBRARIES}) -add_paths_to_fixup_libs(${NVTT_DLL_PATH}) +if (NOT ANDROID) + add_dependency_external_projects(nvtt) + find_package(NVTT REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${NVTT_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${NVTT_LIBRARIES}) + add_paths_to_fixup_libs(${NVTT_DLL_PATH}) +endif() \ No newline at end of file diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index f274dc54f8..e9fb447e99 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -11,7 +11,8 @@ #include "Image.h" -#include +#include + #include #include @@ -28,7 +29,13 @@ using namespace gpu; +#if defined(Q_OS_ANDROID) +#define CPU_MIPMAPS 0 +#else #define CPU_MIPMAPS 1 +#include +#endif + static std::mutex settingsMutex; static Setting::Handle compressColorTextures("hifi.graphics.compressColorTextures", false); @@ -292,6 +299,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { return srcImage; } +#if defined(NVTT_API) struct MyOutputHandler : public nvtt::OutputHandler { MyOutputHandler(gpu::Texture* texture, int face) : _texture(texture), _face(face) {} @@ -330,6 +338,7 @@ struct MyErrorHandler : public nvtt::ErrorHandler { qCWarning(imagelogging) << "Texture compression error:" << nvtt::errorString(e); } }; +#endif void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { #if CPU_MIPMAPS @@ -450,7 +459,7 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { nvtt::Compressor compressor; compressor.process(inputOptions, compressionOptions, outputOptions); #else - texture->autoGenerateMips(-1); + texture->setAutoGenerateMips(true); #endif } diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h index 02dcbe4664..c0cca200d7 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h +++ b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h @@ -30,7 +30,7 @@ public: TOUCH_AXIS_Y_NEG, }; - enum TouchGestureAxisChannel { + enum TouchGestureAxisChannel { TOUCH_GESTURE_PINCH_POS = TOUCH_AXIS_Y_NEG + 1, TOUCH_GESTURE_PINCH_NEG, }; diff --git a/libraries/ktx/CMakeLists.txt b/libraries/ktx/CMakeLists.txt index 967ea71eb6..efd8b76567 100644 --- a/libraries/ktx/CMakeLists.txt +++ b/libraries/ktx/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME ktx) setup_hifi_library() -include_hifi_library_headers(shared) \ No newline at end of file +link_hifi_libraries(shared) diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index 0ffa27986d..013ec056e3 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -66,7 +66,7 @@ Q_INVOKABLE void thruModeEnable(bool enable); public: Midi(); - virtual ~Midi(); + virtual ~Midi(); }; #endif // hifi_Midi_h diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 704455c981..2f29f47864 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -173,9 +173,9 @@ void GeometryReader::run() { QString urlname = _url.path().toLower(); if (!urlname.isEmpty() && !_url.path().isEmpty() && - (_url.path().toLower().endsWith(".fbx") || - _url.path().toLower().endsWith(".obj") || - _url.path().toLower().endsWith(".obj.gz"))) { + (_url.path().toLower().endsWith(".fbx") || + _url.path().toLower().endsWith(".obj") || + _url.path().toLower().endsWith(".obj.gz"))) { FBXGeometry::Pointer fbxGeometry; if (_url.path().toLower().endsWith(".fbx")) { @@ -185,15 +185,15 @@ void GeometryReader::run() { } } else if (_url.path().toLower().endsWith(".obj")) { fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _combineParts, _url)); - } else if (_url.path().toLower().endsWith(".obj.gz")) { - QByteArray uncompressedData; - if (gunzip(_data, uncompressedData)){ - fbxGeometry.reset(OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url)); - } else { - throw QString("failed to decompress .obj.gz" ); - } + } else if (_url.path().toLower().endsWith(".obj.gz")) { + QByteArray uncompressedData; + if (gunzip(_data, uncompressedData)){ + fbxGeometry.reset(OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url)); + } else { + throw QString("failed to decompress .obj.gz" ); + } - } else { + } else { throw QString("unsupported format"); } diff --git a/libraries/model/src/model/Light.slh b/libraries/model/src/model/Light.slh index 67946abea0..093a87adc8 100644 --- a/libraries/model/src/model/Light.slh +++ b/libraries/model/src/model/Light.slh @@ -41,7 +41,7 @@ SphericalHarmonics getLightAmbientSphere(LightAmbient l) { return l._ambientSphe float getLightAmbientIntensity(LightAmbient l) { return l._ambient.x; } -bool getLightHasAmbientMap(LightAmbient l) { return l._ambient.y > 0; } +bool getLightHasAmbientMap(LightAmbient l) { return l._ambient.y > 0.0; } float getLightAmbientMapNumMips(LightAmbient l) { return l._ambient.y; } <@func declareLightBuffer(N)@> diff --git a/libraries/model/src/model/LightIrradiance.shared.slh b/libraries/model/src/model/LightIrradiance.shared.slh index 4a2ee40c9d..13d6cf4b93 100644 --- a/libraries/model/src/model/LightIrradiance.shared.slh +++ b/libraries/model/src/model/LightIrradiance.shared.slh @@ -37,7 +37,7 @@ float lightIrradiance_evalLightAttenuation(LightIrradiance li, float d) { // "Fade" the edges of light sources to make things look a bit more attractive. // Note: this tends to look a bit odd at lower exponents. - attenuation *= min(1, max(0, -(d - cutoff))); + attenuation *= min(1.0, max(0.0, -(d - cutoff))); return attenuation; } diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index 288e98d5a5..c3592c5da2 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -2,44 +2,18 @@ set(TARGET_NAME networking) setup_hifi_library(Network) link_hifi_libraries(shared) -target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes") +target_openssl() +target_tbb() if (WIN32) - # we need ws2_32.lib on windows, but it's static so we don't bubble it up - target_link_libraries(${TARGET_NAME} ws2_32.lib) -endif () - -add_dependency_external_projects(tbb) - -# find required dependencies -find_package(OpenSSL REQUIRED) -find_package(TBB REQUIRED) - -if (APPLE) + # we need ws2_32.lib on windows, but it's static so we don't bubble it up + target_link_libraries(${TARGET_NAME} ws2_32.lib) +elseif(APPLE) + # IOKit is needed for getting machine fingerprint find_library(FRAMEWORK_IOKIT IOKit) find_library(CORE_FOUNDATION CoreFoundation) -endif () - -if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") - # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto - message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." - "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") -endif () - -include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") - -# append OpenSSL to our list of libraries to link -target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES} ${TBB_LIBRARIES}) - -# IOKit is needed for getting machine fingerprint -if (APPLE) target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT} ${CORE_FOUNDATION}) -endif (APPLE) - -# libcrypto uses dlopen in libdl -if (UNIX) - target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) -endif (UNIX) - -# append tbb includes to our list of includes to bubble -target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) +elseif(UNIX) + # libcrypto uses dlopen in libdl + target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) +endif () diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 99e1962387..2376a3b2b6 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -29,11 +29,15 @@ #include "UserActivityLogger.h" #include "udt/PacketHeaders.h" +#ifdef Q_OS_ANDROID +const QString DEFAULT_HIFI_ADDRESS = "hifi://android/0.0,0.0,-200"; +#else #if USE_STABLE_GLOBAL_SERVICES const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome/hello"; #else const QString DEFAULT_HIFI_ADDRESS = "hifi://dev-welcome/hello"; #endif +#endif const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index cb0b620a54..ea66d018e5 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -59,7 +59,11 @@ void AssetClient::init() { auto& networkAccessManager = NetworkAccessManager::getInstance(); if (!networkAccessManager.cache()) { if (_cacheDir.isEmpty()) { +#ifdef Q_OS_ANDROID + QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); +#else QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); +#endif _cacheDir = !cachePath.isEmpty() ? cachePath : "interfaceCache"; } QNetworkDiskCache* cache = new QNetworkDiskCache(); diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index a41283cc0d..eb3a75d14f 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -187,7 +187,7 @@ void AssetResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytes emit progress(bytesReceived, bytesTotal); auto now = p_high_resolution_clock::now(); - + // Recording ATP bytes downloaded in stats DependencyManager::get()->updateStat(STAT_ATP_RESOURCE_TOTAL_BYTES, bytesReceived); diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index c6d0370a70..edba520bd5 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -201,11 +201,11 @@ void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesT _sendTimer->start(); emit progress(bytesReceived, bytesTotal); - + // Recording HTTP bytes downloaded in stats DependencyManager::get()->updateStat(STAT_HTTP_RESOURCE_TOTAL_BYTES, bytesReceived); - - + + } void HTTPResourceRequest::onTimeout() { diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 954b9685af..d0cb0109c7 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -668,7 +668,11 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t } // insert the new node and release our read lock +#ifdef Q_OS_ANDROID + _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer)); +#else _nodeHash.emplace(newNode->getUUID(), newNodePointer); +#endif readLocker.unlock(); qCDebug(networking) << "Added" << *newNode; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 48e5c8a62c..fcf369f786 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -809,7 +809,7 @@ void NodeList::sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationN void NodeList::ignoreNodeBySessionID(const QUuid& nodeID, bool ignoreEnabled) { // enumerate the nodes to send a reliable ignore packet to each that can leverage it if (!nodeID.isNull() && _sessionUUID != nodeID) { - eachMatchingNode([&nodeID](const SharedNodePointer& node)->bool { + eachMatchingNode([](const SharedNodePointer& node)->bool { if (node->getType() == NodeType::AudioMixer || node->getType() == NodeType::AvatarMixer) { return true; } else { diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index e94c43b6fb..67359ee862 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -9,9 +9,28 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "NodePermissions.h" + +#include #include #include -#include "NodePermissions.h" + + + +size_t std::hash::operator()(const NodePermissionsKey& key) const { + size_t result = qHash(key.first); + result <<= sizeof(size_t) / 2; + +#if (QT_POINTER_SIZE == 8) + const uint MASK = 0x00FF; +#else + const uint MASK = 0xFFFF; +#endif + + result |= (qHash(key.second) & MASK); + return result; +} + NodePermissionsKey NodePermissions::standardNameLocalhost = NodePermissionsKey("localhost", 0); NodePermissionsKey NodePermissions::standardNameLoggedIn = NodePermissionsKey("logged-in", 0); diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 129d7e5c08..8f7bdaebfe 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -29,13 +29,8 @@ using NodePermissionsKeyList = QList>; namespace std { template<> - struct hash { - size_t operator()(const NodePermissionsKey& key) const { - size_t result = qHash(key.first); - result <<= 32; - result |= qHash(key.second); - return result; - } + struct hash { + size_t operator()(const NodePermissionsKey& key) const; }; } diff --git a/libraries/networking/src/udt/CongestionControl.h b/libraries/networking/src/udt/CongestionControl.h index 3a93134a5e..e6a462651e 100644 --- a/libraries/networking/src/udt/CongestionControl.h +++ b/libraries/networking/src/udt/CongestionControl.h @@ -123,9 +123,9 @@ private: p_high_resolution_clock::time_point _lastRCTime = p_high_resolution_clock::now(); // last rate increase time - bool _slowStart { true }; // if in slow start phase + bool _slowStart { true }; // if in slow start phase SequenceNumber _lastACK; // last ACKed sequence number from previous - bool _loss { false }; // if loss happened since last rate increase + bool _loss { false }; // if loss happened since last rate increase SequenceNumber _lastDecreaseMaxSeq; // max pkt seq num sent out when last decrease happened double _lastDecreasePeriod { 1 }; // value of _packetSendPeriod when last decrease happened int _nakCount { 0 }; // number of NAKs in congestion epoch diff --git a/libraries/networking/src/udt/ControlPacket.cpp b/libraries/networking/src/udt/ControlPacket.cpp index 0d5d3e8c25..6fdefd36f6 100644 --- a/libraries/networking/src/udt/ControlPacket.cpp +++ b/libraries/networking/src/udt/ControlPacket.cpp @@ -68,7 +68,7 @@ ControlPacket::ControlPacket(std::unique_ptr data, qint64 size, const Hi } ControlPacket::ControlPacket(ControlPacket&& other) : - BasePacket(std::move(other)) + BasePacket(std::move(other)) { _type = other._type; } diff --git a/libraries/physics/src/CharacterRayResult.h b/libraries/physics/src/CharacterRayResult.h index e8b0bb7f99..239ab9570a 100644 --- a/libraries/physics/src/CharacterRayResult.h +++ b/libraries/physics/src/CharacterRayResult.h @@ -26,19 +26,19 @@ public: protected: const CharacterGhostObject* _character; - // Note: Public data members inherited from ClosestRayResultCallback - // - // btVector3 m_rayFromWorld;//used to calculate hitPointWorld from hitFraction - // btVector3 m_rayToWorld; - // btVector3 m_hitNormalWorld; - // btVector3 m_hitPointWorld; - // - // Note: Public data members inherited from RayResultCallback - // - // btScalar m_closestHitFraction; - // const btCollisionObject* m_collisionObject; - // short int m_collisionFilterGroup; - // short int m_collisionFilterMask; + // Note: Public data members inherited from ClosestRayResultCallback + // + // btVector3 m_rayFromWorld;//used to calculate hitPointWorld from hitFraction + // btVector3 m_rayToWorld; + // btVector3 m_hitNormalWorld; + // btVector3 m_hitPointWorld; + // + // Note: Public data members inherited from RayResultCallback + // + // btScalar m_closestHitFraction; + // const btCollisionObject* m_collisionObject; + // short int m_collisionFilterGroup; + // short int m_collisionFilterMask; }; #endif // hifi_CharacterRayResult_h diff --git a/libraries/physics/src/CharacterSweepResult.cpp b/libraries/physics/src/CharacterSweepResult.cpp index a5c4092b1d..36e36185d3 100755 --- a/libraries/physics/src/CharacterSweepResult.cpp +++ b/libraries/physics/src/CharacterSweepResult.cpp @@ -25,7 +25,7 @@ CharacterSweepResult::CharacterSweepResult(const CharacterGhostObject* character } btScalar CharacterSweepResult::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool useWorldFrame) { - // skip objects that we shouldn't collide with + // skip objects that we shouldn't collide with if (!convexResult.m_hitCollisionObject->hasContactResponse()) { return btScalar(1.0); } @@ -37,6 +37,6 @@ btScalar CharacterSweepResult::addSingleResult(btCollisionWorld::LocalConvexResu } void CharacterSweepResult::resetHitHistory() { - m_hitCollisionObject = nullptr; - m_closestHitFraction = btScalar(1.0f); + m_hitCollisionObject = nullptr; + m_closestHitFraction = btScalar(1.0f); } diff --git a/libraries/physics/src/CharacterSweepResult.h b/libraries/physics/src/CharacterSweepResult.h index 1e2898a3cf..3c7bb66282 100644 --- a/libraries/physics/src/CharacterSweepResult.h +++ b/libraries/physics/src/CharacterSweepResult.h @@ -22,13 +22,13 @@ class CharacterSweepResult : public btCollisionWorld::ClosestConvexResultCallbac public: CharacterSweepResult(const CharacterGhostObject* character); virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool useWorldFrame) override; - void resetHitHistory(); + void resetHitHistory(); protected: const CharacterGhostObject* _character; // NOTE: Public data members inherited from ClosestConvexResultCallback: // - // btVector3 m_convexFromWorld; // unused except by btClosestNotMeConvexResultCallback + // btVector3 m_convexFromWorld; // unused except by btClosestNotMeConvexResultCallback // btVector3 m_convexToWorld; // unused except by btClosestNotMeConvexResultCallback // btVector3 m_hitNormalWorld; // btVector3 m_hitPointWorld; diff --git a/libraries/physics/src/CollisionRenderMeshCache.h b/libraries/physics/src/CollisionRenderMeshCache.h index 6a6857a5ae..10b2440db2 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -21,7 +21,7 @@ class CollisionRenderMeshCache { public: - using Key = const void*; // must actually be a const btCollisionShape* + using Key = const void*; // must actually be a const btCollisionShape* CollisionRenderMeshCache(); ~CollisionRenderMeshCache(); diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt index 3ebd0f3d14..daf6fefccc 100644 --- a/libraries/procedural/CMakeLists.txt +++ b/libraries/procedural/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME procedural) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared gpu gpu-gl networking model model-networking ktx image) +link_hifi_libraries(shared gpu networking model model-networking ktx image) diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 0079ffae66..94232c81b2 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Widgets OpenGL Network Qml Quick Script) -link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities image procedural) +link_hifi_libraries(shared ktx gpu model model-networking render animation fbx image procedural) include_hifi_library_headers(networking) include_hifi_library_headers(octree) include_hifi_library_headers(audio) diff --git a/libraries/render-utils/src/DeferredBufferRead.slh b/libraries/render-utils/src/DeferredBufferRead.slh index 315de30fea..fbca241bb9 100644 --- a/libraries/render-utils/src/DeferredBufferRead.slh +++ b/libraries/render-utils/src/DeferredBufferRead.slh @@ -90,7 +90,7 @@ DeferredFragment unpackDeferredFragmentNoPositionNoAmbient(vec2 texcoord) { vec4 diffuseVal; DeferredFragment frag; - frag.depthVal = -1; + frag.depthVal = -1.0; normalVal = texture(normalMap, texcoord); diffuseVal = texture(albedoMap, texcoord); diff --git a/libraries/render-utils/src/FadeEffect.h b/libraries/render-utils/src/FadeEffect.h index 4b4e401332..e4a7debd1e 100644 --- a/libraries/render-utils/src/FadeEffect.h +++ b/libraries/render-utils/src/FadeEffect.h @@ -47,8 +47,8 @@ private: glm::vec3 _lastBaseOffset { 0.f, 0.f, 0.f }; glm::vec3 _lastBaseInvSize { 1.f, 1.f, 1.f }; - explicit FadeEffect(); - virtual ~FadeEffect() { } + explicit FadeEffect(); + virtual ~FadeEffect() { } }; #endif // hifi_render_utils_FadeEffect_h diff --git a/libraries/render-utils/src/ForwardBuffer.slh b/libraries/render-utils/src/ForwardBuffer.slh new file mode 100644 index 0000000000..4d1dc89aa4 --- /dev/null +++ b/libraries/render-utils/src/ForwardBuffer.slh @@ -0,0 +1,68 @@ + +<@if not FORWARD_BUFFER_SLH@> +<@def FORWARD_BUFFER_SLH@> + +<@include gpu/PackedNormal.slh@> + +// Unpack the metallic-mode value +const float FRAG_PACK_SHADED_NON_METALLIC = 0.0; +const float FRAG_PACK_SHADED_METALLIC = 0.1; +const float FRAG_PACK_SHADED_RANGE_INV = 1.0 / (FRAG_PACK_SHADED_METALLIC - FRAG_PACK_SHADED_NON_METALLIC); + +const float FRAG_PACK_LIGHTMAPPED_NON_METALLIC = 0.2; +const float FRAG_PACK_LIGHTMAPPED_METALLIC = 0.3; +const float FRAG_PACK_LIGHTMAPPED_RANGE_INV = 1.0 / (FRAG_PACK_LIGHTMAPPED_METALLIC - FRAG_PACK_LIGHTMAPPED_NON_METALLIC); + +const float FRAG_PACK_SCATTERING_NON_METALLIC = 0.4; +const float FRAG_PACK_SCATTERING_METALLIC = 0.5; +const float FRAG_PACK_SCATTERING_RANGE_INV = 1.0 / (FRAG_PACK_SCATTERING_METALLIC - FRAG_PACK_SCATTERING_NON_METALLIC); + +const float FRAG_PACK_UNLIT = 0.6; + +const int FRAG_MODE_UNLIT = 0; +const int FRAG_MODE_SHADED = 1; +const int FRAG_MODE_LIGHTMAPPED = 2; +const int FRAG_MODE_SCATTERING = 3; + +void unpackModeMetallic(float rawValue, out int mode, out float metallic) { + if (rawValue <= FRAG_PACK_SHADED_METALLIC) { + mode = FRAG_MODE_SHADED; + metallic = clamp((rawValue - FRAG_PACK_SHADED_NON_METALLIC) * FRAG_PACK_SHADED_RANGE_INV, 0.0, 1.0); + } else if (rawValue <= FRAG_PACK_LIGHTMAPPED_METALLIC) { + mode = FRAG_MODE_LIGHTMAPPED; + metallic = clamp((rawValue - FRAG_PACK_LIGHTMAPPED_NON_METALLIC) * FRAG_PACK_LIGHTMAPPED_RANGE_INV, 0.0, 1.0); + } else if (rawValue <= FRAG_PACK_SCATTERING_METALLIC) { + mode = FRAG_MODE_SCATTERING; + metallic = clamp((rawValue - FRAG_PACK_SCATTERING_NON_METALLIC) * FRAG_PACK_SCATTERING_RANGE_INV, 0.0, 1.0); + } else if (rawValue >= FRAG_PACK_UNLIT) { + mode = FRAG_MODE_UNLIT; + metallic = 0.0; + } +} + +float packShadedMetallic(float metallic) { + return mix(FRAG_PACK_SHADED_NON_METALLIC, FRAG_PACK_SHADED_METALLIC, metallic); +} + +float packLightmappedMetallic(float metallic) { + return mix(FRAG_PACK_LIGHTMAPPED_NON_METALLIC, FRAG_PACK_LIGHTMAPPED_METALLIC, metallic); +} + +float packScatteringMetallic(float metallic) { + return mix(FRAG_PACK_SCATTERING_NON_METALLIC, FRAG_PACK_SCATTERING_METALLIC, metallic); +} + +float packUnlit() { + return FRAG_PACK_UNLIT; +} + +<@endif@> diff --git a/libraries/render-utils/src/ForwardBufferWrite.slh b/libraries/render-utils/src/ForwardBufferWrite.slh new file mode 100644 index 0000000000..873514d51f --- /dev/null +++ b/libraries/render-utils/src/ForwardBufferWrite.slh @@ -0,0 +1,63 @@ + +<@if not FORWARD_BUFFER_WRITE_SLH@> +<@def FORWARD_BUFFER_WRITE_SLH@> + +<@include ForwardBuffer.slh@> + + +layout(location = 0) out vec4 _fragColor0; + +// the alpha threshold +const float alphaThreshold = 0.5; +float evalOpaqueFinalAlpha(float alpha, float mapAlpha) { + return mix(alpha, 1.0 - alpha, step(mapAlpha, alphaThreshold)); +} + +const float DEFAULT_ROUGHNESS = 0.9; +const float DEFAULT_SHININESS = 10.0; +const float DEFAULT_METALLIC = 0.0; +const vec3 DEFAULT_SPECULAR = vec3(0.1); +const vec3 DEFAULT_EMISSIVE = vec3(0.0); +const float DEFAULT_OCCLUSION = 1.0; +const float DEFAULT_SCATTERING = 0.0; +const vec3 DEFAULT_FRESNEL = DEFAULT_EMISSIVE; + +void packForwardFragment(vec3 normal, float alpha, vec3 albedo, float roughness, float metallic, vec3 emissive, float occlusion, float scattering) { + if (alpha != 1.0) { + discard; + } + _fragColor0 = vec4(albedo, ((scattering > 0.0) ? packScatteringMetallic(metallic) : packShadedMetallic(metallic))); +} + +void packForwardFragmentLightmap(vec3 normal, float alpha, vec3 albedo, float roughness, float metallic, vec3 fresnel, vec3 lightmap) { + if (alpha != 1.0) { + discard; + } + + _fragColor0 = vec4(albedo, packLightmappedMetallic(metallic)); +} + +void packForwardFragmentUnlit(vec3 normal, float alpha, vec3 color) { + if (alpha != 1.0) { + discard; + } + _fragColor0 = vec4(color, packUnlit()); +} + +void packForwardFragmentTranslucent(vec3 normal, float alpha, vec3 albedo, vec3 fresnel, float roughness) { + if (alpha <= 0.0) { + discard; + } + _fragColor0 = vec4(albedo.rgb, alpha); +} + +<@endif@> diff --git a/libraries/render-utils/src/ForwardGlobalLight.slh b/libraries/render-utils/src/ForwardGlobalLight.slh new file mode 100644 index 0000000000..aba0498ef5 --- /dev/null +++ b/libraries/render-utils/src/ForwardGlobalLight.slh @@ -0,0 +1,198 @@ + +<@if not DEFERRED_GLOBAL_LIGHT_SLH@> +<@def DEFERRED_GLOBAL_LIGHT_SLH@> + +<@include model/Light.slh@> + +<@include LightingModel.slh@> +<$declareLightBuffer()$> +<$declareLightAmbientBuffer()$> + +<@include LightAmbient.slh@> +<@include LightDirectional.slh@> + + +<@func prepareGlobalLight(isScattering)@> + // prepareGlobalLight + // Transform directions to worldspace + vec3 fragNormal = vec3((normal)); + vec3 fragEyeVector = vec3(invViewMat * vec4(-1.0*position, 0.0)); + vec3 fragEyeDir = normalize(fragEyeVector); + + // Get light + Light light = getLight(); + LightAmbient lightAmbient = getLightAmbient(); + + vec3 lightDirection = getLightDirection(light); + vec3 lightIrradiance = getLightIrradiance(light); + + vec3 color = vec3(0.0); + +<@endfunc@> + + +<@func declareEvalAmbientGlobalColor()@> +vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, float roughness) { + <$prepareGlobalLight()$> + color += albedo * getLightColor(light) * obscurance * getLightAmbientIntensity(lightAmbient); + return color; +} +<@endfunc@> + +<@func declareEvalAmbientSphereGlobalColor(supportScattering)@> + +<$declareLightingAmbient(1, _SCRIBE_NULL, _SCRIBE_NULL, $supportScattering$)$> +<$declareLightingDirectional($supportScattering$)$> + +<@if supportScattering@> +<$declareDeferredCurvature()$> +<@endif@> + +vec3 evalAmbientSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, +vec3 albedo, vec3 fresnel, float metallic, float roughness +<@if supportScattering@> + , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature +<@endif@> ) { + + <$prepareGlobalLight($supportScattering$)$> + + // Ambient + vec3 ambientDiffuse; + vec3 ambientSpecular; + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance +<@if supportScattering@> + ,scattering, midNormalCurvature, lowNormalCurvature +<@endif@> ); + color += ambientDiffuse; + color += ambientSpecular; + + + // Directional + vec3 directionalDiffuse; + vec3 directionalSpecular; + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation +<@if supportScattering@> + ,scattering, midNormalCurvature, lowNormalCurvature +<@endif@> ); + color += directionalDiffuse; + color += directionalSpecular; + + return color; +} + +<@endfunc@> + + +<@func declareEvalSkyboxGlobalColor(supportScattering)@> + +<$declareLightingAmbient(_SCRIBE_NULL, 1, _SCRIBE_NULL, $supportScattering$)$> +<$declareLightingDirectional($supportScattering$)$> + +<@if supportScattering@> +<$declareDeferredCurvature()$> +<@endif@> + +vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, + vec3 albedo, vec3 fresnel, float metallic, float roughness +<@if supportScattering@> + , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature +<@endif@> + ) { + <$prepareGlobalLight($supportScattering$)$> + + // Ambient + vec3 ambientDiffuse; + vec3 ambientSpecular; + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance +<@if supportScattering@> + ,scattering, midNormalCurvature, lowNormalCurvature +<@endif@> + ); + color += ambientDiffuse; + color += ambientSpecular; + + + // Directional + vec3 directionalDiffuse; + vec3 directionalSpecular; + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation +<@if supportScattering@> + ,scattering, midNormalCurvature, lowNormalCurvature +<@endif@> + ); + color += directionalDiffuse; + color += directionalSpecular; + + return color; +} + +<@endfunc@> + +<@func declareEvalLightmappedColor()@> +vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 normal, vec3 albedo, vec3 lightmap) { + Light light = getLight(); + LightAmbient ambient = getLightAmbient(); + + // Catch normals perpendicular to the projection plane, hence the magic number for the threshold + // It should be just 0, but we have inaccuracy so we overshoot + const float PERPENDICULAR_THRESHOLD = -0.005; + vec3 fragNormal = vec3(invViewMat * vec4(normal, 0.0)); // transform to worldspace + float diffuseDot = dot(fragNormal, -getLightDirection(light)); + float facingLight = step(PERPENDICULAR_THRESHOLD, diffuseDot); + + // Reevaluate the shadow attenuation for light facing fragments + float lightAttenuation = (1.0 - facingLight) + facingLight * shadowAttenuation; + + // Diffuse light is the lightmap dimmed by shadow + vec3 diffuseLight = lightAttenuation * lightmap; + + // Ambient light is the lightmap when in shadow + vec3 ambientLight = (1.0 - lightAttenuation) * lightmap * getLightAmbientIntensity(ambient); + + return isLightmapEnabled() * obscurance * albedo * (diffuseLight + ambientLight); +} +<@endfunc@> + + + + +<@func declareEvalGlobalLightingAlphaBlended()@> + +<$declareLightingAmbient(1, 1, 1)$> +<$declareLightingDirectional()$> + +vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) { + <$prepareGlobalLight()$> + + color += emissive * isEmissiveEnabled(); + + // Ambient + vec3 ambientDiffuse; + vec3 ambientSpecular; + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + color += ambientDiffuse; + color += ambientSpecular / opacity; + + // Directional + vec3 directionalDiffuse; + vec3 directionalSpecular; + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + color += directionalDiffuse; + color += directionalSpecular / opacity; + + return color; +} + +<@endfunc@> + + +<@endif@> diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 3bf83d08c9..02e4ca9748 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -18,9 +18,11 @@ #include #include +#include +#include + #include #include -#include #include "TextureCache.h" #include "RenderUtilsLogging.h" diff --git a/libraries/render-utils/src/LightClusterGrid_shared.slh b/libraries/render-utils/src/LightClusterGrid_shared.slh index 50499cf31a..6d43e71920 100644 --- a/libraries/render-utils/src/LightClusterGrid_shared.slh +++ b/libraries/render-utils/src/LightClusterGrid_shared.slh @@ -36,7 +36,7 @@ vec3 frustumGrid_volumeToGrid(vec3 vpos, ivec3 dims) { vec4 frustumGrid_volumeToClip(vec3 vpos, float rangeNear, float rangeFar) { vec3 ndcPos = vec3(-1.0f + 2.0f * vpos.x, -1.0f + 2.0f * vpos.y, vpos.z); - float depth = rangeNear * (1 - ndcPos.z) + rangeFar * (ndcPos.z); + float depth = rangeNear * (1.0f - ndcPos.z) + rangeFar * (ndcPos.z); vec4 clipPos = vec4(ndcPos.x * depth, ndcPos.y * depth, 1.0f, depth); return clipPos; } @@ -114,14 +114,14 @@ int frustumGrid_eyeDepthToClusterLayer(float eyeZ) { float volumeZ = frustumGrid_eyeToVolumeDepth(eyeZ, frustumGrid.rangeNear, frustumGrid.rangeFar); - float gridZ = frustumGrid_volumeToGridDepth(volumeZ, frustumGrid.dims); + int gridZ = int(frustumGrid_volumeToGridDepth(volumeZ, frustumGrid.dims)); if (gridZ >= frustumGrid.dims.z) { gridZ = frustumGrid.dims.z; } - return int(gridZ); + return gridZ; } ivec3 frustumGrid_eyeToClusterPos(vec3 eyePos) { @@ -134,8 +134,8 @@ ivec3 frustumGrid_eyeToClusterPos(vec3 eyePos) { vec3 gridPos = frustumGrid_volumeToGrid(volumePos, frustumGrid.dims); - if (gridPos.z >= frustumGrid.dims.z) { - gridPos.z = frustumGrid.dims.z; + if (gridPos.z >= float(frustumGrid.dims.z)) { + gridPos.z = float(frustumGrid.dims.z); } ivec3 igridPos = ivec3(floor(gridPos)); @@ -154,7 +154,7 @@ ivec3 frustumGrid_eyeToClusterPos(vec3 eyePos) { int frustumGrid_eyeToClusterDirH(vec3 eyeDir) { if (eyeDir.z >= 0.0f) { - return (eyeDir.x > 0 ? frustumGrid.dims.x : -1); + return (eyeDir.x > 0.0f ? frustumGrid.dims.x : -1); } float eyeDepth = -eyeDir.z; @@ -168,7 +168,7 @@ int frustumGrid_eyeToClusterDirH(vec3 eyeDir) { int frustumGrid_eyeToClusterDirV(vec3 eyeDir) { if (eyeDir.z >= 0.0f) { - return (eyeDir.y > 0 ? frustumGrid.dims.y : -1); + return (eyeDir.y > 0.0f ? frustumGrid.dims.y : -1); } float eyeDepth = -eyeDir.z; diff --git a/libraries/render-utils/src/LightPoint.slh b/libraries/render-utils/src/LightPoint.slh index ac1e415d9d..7e389e11f6 100644 --- a/libraries/render-utils/src/LightPoint.slh +++ b/libraries/render-utils/src/LightPoint.slh @@ -59,12 +59,12 @@ bool evalLightPointEdge(out vec3 color, Light light, vec4 fragLightDirLen, vec3 // Show edges float edge = abs(2.0 * ((lightVolume_getRadius(light.volume) - fragLightDistance) / (0.1)) - 1.0); - if (edge < 1) { + if (edge < 1.0) { float edgeCoord = exp2(-8.0*edge*edge); color = vec3(edgeCoord * edgeCoord * getLightColor(light)); } - return (edge < 1); + return (edge < 1.0); } <@endfunc@> diff --git a/libraries/render-utils/src/LightSpot.slh b/libraries/render-utils/src/LightSpot.slh index a38851b039..8627dae0eb 100644 --- a/libraries/render-utils/src/LightSpot.slh +++ b/libraries/render-utils/src/LightSpot.slh @@ -66,12 +66,12 @@ bool evalLightSpotEdge(out vec3 color, Light light, vec4 fragLightDirLen, float float edgeDistS = dot(fragLightDistance * vec2(cosSpotAngle, sqrt(1.0 - cosSpotAngle * cosSpotAngle)), -lightVolume_getSpotOutsideNormal2(light.volume)); float edgeDist = min(edgeDistR, edgeDistS); float edge = abs(2.0 * (edgeDist / (0.1)) - 1.0); - if (edge < 1) { + if (edge < 1.0) { float edgeCoord = exp2(-8.0*edge*edge); color = vec3(edgeCoord * edgeCoord * getLightColor(light)); } - return (edge < 1); + return (edge < 1.0); } <@endfunc@> diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 209a1f38d6..521c4894dc 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -157,7 +157,7 @@ vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float m float power = specularDistribution(roughness, fragNormal, halfDir); vec3 specular = fresnelColor * power * diffuse; - return vec4(specular, (1.0 - metallic) * diffuse * (1 - fresnelColor.x)); + return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x)); } // Frag Shading returns the diffuse amount as W and the specular rgb as xyz @@ -171,7 +171,7 @@ vec4 evalPBRShadingDielectric(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDi float power = specularDistribution(roughness, fragNormal, halfDir); vec3 specular = vec3(fresnelScalar) * power * diffuse; - return vec4(specular, diffuse * (1 - fresnelScalar)); + return vec4(specular, diffuse * (1.0 - fresnelScalar)); } vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, vec3 fresnel) { diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index e694935361..6c77fc4a91 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -66,7 +66,7 @@ vec3 fetchNormalMap(vec2 uv) { // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out vec2 t = 2.0 * (texture(normalMap, uv).rg - vec2(0.5, 0.5)); vec2 t2 = t*t; - return vec3(t.x, sqrt(1 - t2.x - t2.y), t.y); + return vec3(t.x, sqrt(1.0 - t2.x - t2.y), t.y); } <@endif@> @@ -163,7 +163,7 @@ vec3 fetchLightmapMap(vec2 uv) { vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); // attenuate the normal map divergence from the mesh normal based on distance // THe attenuation range [20,100] meters from the eye is arbitrary for now - vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(20, 100, (-<$fragPos$>).z)); + vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(20.0, 100.0, (-<$fragPos$>).z)); <$normal$> = vec3(normalizedTangent * localNormal.x + normalizedNormal * localNormal.y + normalizedBitangent * localNormal.z); } <@endfunc@> diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 517fe97dba..e640f54fd4 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -14,7 +14,6 @@ #include #include "DeferredLightingEffect.h" -#include "EntityItem.h" using namespace render; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 63aeacf80c..3b55cac0b8 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -382,8 +382,8 @@ protected: QVector> _collisionRenderItems; QMap _collisionRenderItemsMap; - QVector> _modelMeshRenderItems; - QMap _modelMeshRenderItemsMap; + QVector> _modelMeshRenderItems; + QMap _modelMeshRenderItemsMap; render::ItemIDs _modelMeshRenderItemIDs; diff --git a/libraries/render-utils/src/PickItemsJob.cpp b/libraries/render-utils/src/PickItemsJob.cpp index 48ba605d7b..860262a969 100644 --- a/libraries/render-utils/src/PickItemsJob.cpp +++ b/libraries/render-utils/src/PickItemsJob.cpp @@ -31,26 +31,26 @@ void PickItemsJob::run(const render::RenderContextPointer& renderContext, const } render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const { - const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition(); - const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection(); - BoxFace face; - glm::vec3 normal; - float isectDistance; - render::ItemBound nearestItem( render::Item::INVALID_ITEM_ID ); - const float minDistance = 0.2f; - const float maxDistance = 50.f; + const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition(); + const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection(); + BoxFace face; + glm::vec3 normal; + float isectDistance; + render::ItemBound nearestItem( render::Item::INVALID_ITEM_ID ); + const float minDistance = 0.2f; + const float maxDistance = 50.f; render::ItemKey itemKey; - for (const auto& itemBound : inputs) { - if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) { - auto& item = renderContext->_scene->getItem(itemBound.id); + for (const auto& itemBound : inputs) { + if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) { + auto& item = renderContext->_scene->getItem(itemBound.id); itemKey = item.getKey(); - if (itemKey.isWorldSpace() && isectDistance>minDistance && isectDistance < minIsectDistance && isectDistanceminDistance && isectDistance < minIsectDistance && isectDistance; + using Config = PickItemsConfig; + using Input = render::ItemBounds; + using Output = render::ItemBounds; + using JobModel = render::Job::ModelIO; PickItemsJob(render::ItemKey::Flags validKeys = render::ItemKey::Builder().withTypeMeta().withTypeShape().build()._flags, render::ItemKey::Flags excludeKeys = 0); - void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, const PickItemsJob::Input& input, PickItemsJob::Output& output); + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const PickItemsJob::Input& input, PickItemsJob::Output& output); private: @@ -47,7 +47,7 @@ private: render::ItemKey::Flags _excludeKeys; bool _isEnabled{ false }; - render::ItemBound findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const; + render::ItemBound findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const; }; #endif // hifi_render_utils_PickItemsJob_h diff --git a/libraries/render-utils/src/SubsurfaceScattering.slh b/libraries/render-utils/src/SubsurfaceScattering.slh index 42ffafd9ff..201ec2291a 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.slh +++ b/libraries/render-utils/src/SubsurfaceScattering.slh @@ -105,7 +105,7 @@ vec3 integrate(float cosTheta, float skinRadius) { vec3 totalLight = vec3(0.0); const float _PI = 3.14159265358979523846; - const float step = 2.0 * _PI / <$NumIntegrationSteps$>; + const float step = 2.0 * _PI / float(<$NumIntegrationSteps$>); float a = -(_PI); diff --git a/libraries/render-utils/src/forward_model.slf b/libraries/render-utils/src/forward_model.slf index daeead65ec..7b708a1d24 100644 --- a/libraries/render-utils/src/forward_model.slf +++ b/libraries/render-utils/src/forward_model.slf @@ -1,7 +1,7 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> -// model.frag +// forward_model.frag // fragment shader // // Created by Andrzej Kapolka on 10/14/13. @@ -11,12 +11,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> + !> + +<@include ForwardGlobalLight.slh@> +<$declareEvalSkyboxGlobalColor()$> <@include model/Material.slh@> +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + <@include MaterialTextures.slh@> -<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, _SCRIBE_NULL)$> in vec4 _position; in vec3 _normal; @@ -24,12 +30,13 @@ in vec3 _color; in vec2 _texCoord0; in vec2 _texCoord1; +out vec4 _fragColor; void main(void) { Material mat = getMaterial(); int matKey = getMaterialKey(mat); <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$> - <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + !> float opacity = 1.0; <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; @@ -39,21 +46,43 @@ void main(void) { <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; albedo *= _color; + float metallic = getMaterialMetallic(mat); + vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value + if (metallic <= 0.5) { + metallic = 0.0; + } else { + fresnel = albedo; + metallic = 1.0; + } + float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; - float scattering = getMaterialScattering(mat); - packDeferredFragment( - normalize(_normal.xyz), - opacity, + vec3 fragPosition = _position.xyz; + + TransformCamera cam = getTransformCamera(); + vec3 fragNormal; + <$transformEyeToWorldDir(cam, _normal, fragNormal)$> + + /* vec4 color = vec4(evalSkyboxGlobalColor( + cam._viewInverse, + 1.0, + 1.0, + fragPosition, + fragNormal, albedo, - roughness, - getMaterialMetallic(mat), - emissive, - occlusionTex, - scattering); + fresnel, + metallic, + roughness), + opacity); + color.rgb += emissive * isEmissiveEnabled(); + + */ + + _fragColor = vec4(albedo, opacity); + } diff --git a/libraries/render-utils/src/forward_model_normal_map.slf b/libraries/render-utils/src/forward_model_normal_map.slf index 5cc1a1859f..a199483b9f 100644 --- a/libraries/render-utils/src/forward_model_normal_map.slf +++ b/libraries/render-utils/src/forward_model_normal_map.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// model_normal_map.frag +// forward_model_normal_map.frag // fragment shader // // Created by Andrzej Kapolka on 10/29/13. @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> +<@include ForwardBufferWrite.slh@> <@include model/Material.slh@> @@ -47,12 +47,12 @@ void main(void) { <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + <$tangentToViewSpace(normalTex, _normal, _tangent, viewNormal)$> float scattering = getMaterialScattering(mat); <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; - packDeferredFragment( + packForwardFragment( viewNormal, opacity, albedo, diff --git a/libraries/render-utils/src/forward_model_normal_specular_map.slf b/libraries/render-utils/src/forward_model_normal_specular_map.slf index 9e079b33a0..ca6bbec3da 100644 --- a/libraries/render-utils/src/forward_model_normal_specular_map.slf +++ b/libraries/render-utils/src/forward_model_normal_specular_map.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// model_normal_specular_map.frag +// forward_model_normal_specular_map.frag // fragment shader // // Created by Andrzej Kapolka on 5/6/14. @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> +<@include ForwardBufferWrite.slh@> <@include model/Material.slh@> @@ -47,14 +47,14 @@ void main(void) { <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + <$tangentToViewSpace(normalTex, _normal, _tangent, viewNormal)$> float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; float scattering = getMaterialScattering(mat); - packDeferredFragment( + packForwardFragment( normalize(viewNormal.xyz), opacity, albedo, diff --git a/libraries/render-utils/src/forward_model_specular_map.slf b/libraries/render-utils/src/forward_model_specular_map.slf index 47b5e3389d..d2fdd18794 100644 --- a/libraries/render-utils/src/forward_model_specular_map.slf +++ b/libraries/render-utils/src/forward_model_specular_map.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// model_specular_map.frag +// forward_model_specular_map.frag // fragment shader // // Created by Andrzej Kapolka on 5/6/14. @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> +<@include ForwardBufferWrite.slh@> <@include model/Material.slh@> @@ -51,7 +51,7 @@ void main(void) { float scattering = getMaterialScattering(mat); - packDeferredFragment( + packForwardFragment( normalize(_normal), opacity, albedo, diff --git a/libraries/render-utils/src/forward_model_translucent.slf b/libraries/render-utils/src/forward_model_translucent.slf new file mode 100644 index 0000000000..52e8ce50e7 --- /dev/null +++ b/libraries/render-utils/src/forward_model_translucent.slf @@ -0,0 +1,81 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// forward_model_translucent.frag +// fragment shader +// +// Created by Sam Gateau on 2/15/2016. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include model/Material.slh@> + +<@include ForwardGlobalLight.slh@> + +<$declareEvalGlobalLightingAlphaBlended()$> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> + +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec4 _position; +in vec3 _normal; +in vec3 _color; +in float _alpha; + +out vec4 _fragColor; + +void main(void) { + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = getMaterialOpacity(mat) * _alpha; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value + if (metallic <= 0.5) { + metallic = 0.0; + } else { + fresnel = albedo; + metallic = 1.0; + } + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 fragPosition = _position.xyz; + vec3 fragNormal = normalize(_normal); + + TransformCamera cam = getTransformCamera(); + + _fragColor = vec4(evalGlobalLightingAlphaBlended( + cam._viewInverse, + 1.0, + occlusionTex, + fragPosition, + fragNormal, + albedo, + fresnel, + metallic, + emissive, + roughness, opacity), + opacity); +} diff --git a/libraries/render-utils/src/forward_model_unlit.slf b/libraries/render-utils/src/forward_model_unlit.slf index 750b51fe8c..fb760467c9 100644 --- a/libraries/render-utils/src/forward_model_unlit.slf +++ b/libraries/render-utils/src/forward_model_unlit.slf @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// material_opaque_unlit.frag +// forward_model_unlit.frag // fragment shader // // Created by Sam Gateau on 5/5/2016. @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> +<@include ForwardBufferWrite.slh@> <@include LightingModel.slh@> <@include model/Material.slh@> @@ -38,7 +38,7 @@ void main(void) { <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; albedo *= _color; - packDeferredFragmentUnlit( + packForwardFragmentUnlit( normalize(_normal), opacity, albedo * isUnlitEnabled()); diff --git a/libraries/render-utils/src/glowLine.slf b/libraries/render-utils/src/glowLine.slf index c0af97930a..6f0281c5ec 100644 --- a/libraries/render-utils/src/glowLine.slf +++ b/libraries/render-utils/src/glowLine.slf @@ -20,7 +20,7 @@ void main(void) { float alpha = 1.0 - abs(inColor.a); // Convert from a linear alpha curve to a sharp peaked one - alpha = pow(alpha, 10); + alpha = pow(alpha, 10.0); // Drop everything where the curve falls off to nearly nothing if (alpha <= 0.05) { diff --git a/libraries/render-utils/src/lightClusters_drawClusterContent.slv b/libraries/render-utils/src/lightClusters_drawClusterContent.slv index c570957dfb..b88e2e9ee2 100644 --- a/libraries/render-utils/src/lightClusters_drawClusterContent.slv +++ b/libraries/render-utils/src/lightClusters_drawClusterContent.slv @@ -53,12 +53,12 @@ void main(void) { - ivec3 cluster = clusterGrid_getCluster(gpu_InstanceID); + ivec3 cluster = clusterGrid_getCluster(gpu_InstanceID()); int numLights = cluster.x + cluster.y; float numLightsScale = clamp(numLights * 0.1, 0.0, 1.0); - ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID); + ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID()); float boxScale = 0.99; vec3 eyePos = frustumGrid_clusterPosToEye(clusterPos, vec3((1.0 - boxScale) * 0.5 + (1.0 - numLightsScale) * boxScale * 0.5) + numLightsScale * boxScale * pos.xyz); @@ -69,5 +69,5 @@ void main(void) { TransformCamera cam = getTransformCamera(); <$transformWorldToClipPos(cam, worldPos, gl_Position)$> - varColor = vec4(colorWheel(fract(float(gpu_InstanceID) / float(frustumGrid_numClusters()))), (numLights >0 ? 0.9 : 0.1)); + varColor = vec4(colorWheel(fract(float(gpu_InstanceID()) / float(frustumGrid_numClusters()))), (numLights >0 ? 0.9 : 0.1)); } \ No newline at end of file diff --git a/libraries/render-utils/src/lightClusters_drawClusterFromDepth.slv b/libraries/render-utils/src/lightClusters_drawClusterFromDepth.slv index 5840f11d0a..912c39f93c 100644 --- a/libraries/render-utils/src/lightClusters_drawClusterFromDepth.slv +++ b/libraries/render-utils/src/lightClusters_drawClusterFromDepth.slv @@ -53,7 +53,7 @@ void main(void) { ); vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; - ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID); + ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID()); vec3 eyePos = frustumGrid_clusterPosToEye(clusterPos, vec3(0.05) + 0.9 * pos.xyz); vec4 worldPos = frustumGrid_eyeToWorld(vec4(eyePos.xyz, 1.0)); @@ -62,5 +62,5 @@ void main(void) { TransformCamera cam = getTransformCamera(); <$transformWorldToClipPos(cam, worldPos, gl_Position)$> - varColor = vec4(colorWheel(fract(float(gpu_InstanceID) / float(frustumGrid_numClusters()))), 0.9); + varColor = vec4(colorWheel(fract(float(gpu_InstanceID()) / float(frustumGrid_numClusters()))), 0.9); } \ No newline at end of file diff --git a/libraries/render-utils/src/lightClusters_drawGrid.slv b/libraries/render-utils/src/lightClusters_drawGrid.slv index 1475666ec6..aac7fe59a5 100644 --- a/libraries/render-utils/src/lightClusters_drawGrid.slv +++ b/libraries/render-utils/src/lightClusters_drawGrid.slv @@ -54,10 +54,10 @@ void main(void) { vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; - ivec3 cluster = clusterGrid_getCluster(gpu_InstanceID); + ivec3 cluster = clusterGrid_getCluster(gpu_InstanceID()); int numLights = cluster.x + cluster.y; - ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID); + ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID()); float boxScale = 1.0; @@ -69,5 +69,5 @@ void main(void) { TransformCamera cam = getTransformCamera(); <$transformWorldToClipPos(cam, worldPos, gl_Position)$> - varColor = vec4(colorWheel(fract(float(gpu_InstanceID) / float(frustumGrid_numClusters()))), (numLights > 0 ? 0.9 : 0.0)); + varColor = vec4(colorWheel(fract(float(gpu_InstanceID()) / float(frustumGrid_numClusters()))), (numLights > 0 ? 0.9 : 0.0)); } \ No newline at end of file diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 228560f394..1e3b6908cd 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -72,7 +72,7 @@ void main(void) { normal, 1.0, diffuse, - max(0, 1.0 - shininess / 128.0), + max(0.0, 1.0 - shininess / 128.0), DEFAULT_METALLIC, specular, specular); @@ -81,7 +81,7 @@ void main(void) { normal, 1.0, diffuse, - max(0, 1.0 - shininess / 128.0), + max(0.0, 1.0 - shininess / 128.0), length(specular), DEFAULT_EMISSIVE, DEFAULT_OCCLUSION, diff --git a/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf b/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf index e96ac60b45..ecbc60b648 100644 --- a/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf +++ b/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf @@ -123,7 +123,7 @@ void main(void) { // Calculate dF/du and dF/dv vec2 viewportScale = perspectiveScale * getInvWidthHeight(); - vec2 du = vec2( viewportScale.x * (stereoSide.w > 0.0 ? 0.5 : 1.0), 0.0f ); + vec2 du = vec2( viewportScale.x * (float(stereoSide.w) > 0.0 ? 0.5 : 1.0), 0.0f ); vec2 dv = vec2( 0.0f, viewportScale.y ); vec4 dFdu = vec4(getWorldNormalDiff(frameTexcoordPos, du), getEyeDepthDiff(frameTexcoordPos, du)); diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 561dff4290..8fd05bd320 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -2,8 +2,7 @@ set(TARGET_NAME render) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared ktx gpu model) # render needs octree only for getAccuracyAngle(float, int) -include_hifi_library_headers(octree) +link_hifi_libraries(shared ktx gpu model octree) target_nsight() diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 240693b422..1650d09c5d 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -16,7 +16,7 @@ #include "Scene.h" #include "../task/Task.h" -#include "gpu/Batch.h" +#include namespace render { diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 21638fbb70..96faf9719e 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -25,7 +25,7 @@ #include "Args.h" -#include "model/Material.h" +#include #include "ShapePipeline.h" namespace render { diff --git a/libraries/render/src/render/TransitionStage.h b/libraries/render/src/render/TransitionStage.h index 8dfef1b78e..226d531d8b 100644 --- a/libraries/render/src/render/TransitionStage.h +++ b/libraries/render/src/render/TransitionStage.h @@ -18,19 +18,19 @@ namespace render { - // Transition stage to set up Transition-related effects - class TransitionStage : public render::Stage { - public: + // Transition stage to set up Transition-related effects + class TransitionStage : public render::Stage { + public: static const std::string& getName() { return _name; } - using Index = indexed_container::Index; - static const Index INVALID_INDEX{ indexed_container::INVALID_INDEX }; + using Index = indexed_container::Index; + static const Index INVALID_INDEX{ indexed_container::INVALID_INDEX }; using TransitionIdList = indexed_container::Indices; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } - bool checkTransitionId(Index index) const { return _transitions.checkIndex(index); } + bool checkTransitionId(Index index) const { return _transitions.checkIndex(index); } const Transition& getTransition(Index TransitionId) const { return _transitions.get(TransitionId); } @@ -48,10 +48,10 @@ namespace render { static std::string _name; - Transitions _transitions; + Transitions _transitions; TransitionIdList _activeTransitionIds; - }; - using TransitionStagePointer = std::shared_ptr; + }; + using TransitionStagePointer = std::shared_ptr; class TransitionStageSetup { public: diff --git a/libraries/render/src/render/drawItemBounds.slf b/libraries/render/src/render/drawItemBounds.slf index 4fb23df8f6..e01d1607bd 100644 --- a/libraries/render/src/render/drawItemBounds.slf +++ b/libraries/render/src/render/drawItemBounds.slf @@ -19,7 +19,7 @@ out vec4 outFragColor; void main(void) { float var = step(fract(varTexcoord.x * varTexcoord.y * 1.0), 0.5); - if (varColor.a == 0) { + if (varColor.a == 0.0) { outFragColor = vec4(mix(vec3(0.0), varColor.xyz, var), mix(0.0, 1.0, var)); } else { diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 6dc3188b3f..fef11c12e9 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -33,7 +33,7 @@ bool UsersScriptingInterface::getIgnoreStatus(const QUuid& nodeID) { void UsersScriptingInterface::personalMute(const QUuid& nodeID, bool muteEnabled) { // ask the NodeList to mute the user with the given session ID - // "Personal Mute" only applies one way and is not global + // "Personal Mute" only applies one way and is not global DependencyManager::get()->personalMuteNodeBySessionID(nodeID, muteEnabled); } diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index a156f56d96..c21f96cd47 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -90,6 +90,6 @@ glm::vec3 Vec3::fromPolar(float elevation, float azimuth) { } float Vec3::getAngle(const glm::vec3& v1, const glm::vec3& v2) { - return glm::acos(glm::dot(glm::normalize(v1), glm::normalize(v2))); + return glm::acos(glm::dot(glm::normalize(v1), glm::normalize(v2))); } diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 19148b26e9..76faaab415 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -25,7 +25,7 @@ WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) : WebSocketClass::WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket) : _webSocket(qWebSocket), - _engine(engine) + _engine(engine) { initialize(); } diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 1fefda06b3..f9b835df5c 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -3,8 +3,6 @@ set(TARGET_NAME shared) # TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp) setup_hifi_library(Gui Network Script Widgets) -target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes") - if (WIN32) target_link_libraries(${TARGET_NAME} Wbemuuid.lib) endif() diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index 7a453e63c3..e6fc7ce96b 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -12,6 +12,7 @@ #ifndef hifi_DependencyManager_h #define hifi_DependencyManager_h +#include #include #include #include @@ -64,6 +65,15 @@ public: template static void registerInheritance(); + template + static size_t typeHash() { +#ifdef Q_OS_ANDROID + size_t hashCode = std::hash{}( typeid(T).name() ); +#else + size_t hashCode = typeid(T).hash_code(); +#endif + return hashCode; + } private: static DependencyManager& manager(); @@ -134,14 +144,14 @@ void DependencyManager::destroy() { template void DependencyManager::registerInheritance() { - size_t baseHashCode = typeid(Base).hash_code(); - size_t derivedHashCode = typeid(Derived).hash_code(); + size_t baseHashCode = typeHash(); + size_t derivedHashCode = typeHash(); manager()._inheritanceHash.insert(baseHashCode, derivedHashCode); } template size_t DependencyManager::getHashCode() { - size_t hashCode = typeid(T).hash_code(); + size_t hashCode = typeHash(); auto derivedHashCode = _inheritanceHash.find(hashCode); while (derivedHashCode != _inheritanceHash.end()) { diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 0636411f51..cd7a6530f9 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "PathUtils.h" + #include #include #include @@ -16,7 +18,6 @@ #include #include #include -#include "PathUtils.h" #include #include // std::once #include "shared/GlobalAppProperties.h" @@ -92,18 +93,17 @@ QUrl PathUtils::defaultScriptsLocation(const QString& newDefaultPath) { if (!overriddenDefaultScriptsLocation.isEmpty()) { path = overriddenDefaultScriptsLocation; } else { -#ifdef Q_OS_WIN - path = QCoreApplication::applicationDirPath() + "/scripts"; -#elif defined(Q_OS_OSX) +#if defined(Q_OS_OSX) path = QCoreApplication::applicationDirPath() + "/../Resources/scripts"; +#elif defined(Q_OS_ANDROID) + path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/scripts"; #else path = QCoreApplication::applicationDirPath() + "/scripts"; #endif } // turn the string into a legit QUrl - QFileInfo fileInfo(path); - return QUrl::fromLocalFile(fileInfo.canonicalFilePath()); + return QUrl::fromLocalFile(QFileInfo(path).canonicalFilePath()); } QString PathUtils::stripFilename(const QUrl& url) { diff --git a/libraries/shared/src/Preferences.h b/libraries/shared/src/Preferences.h index 6fa2cb9b1f..a243a6d58d 100644 --- a/libraries/shared/src/Preferences.h +++ b/libraries/shared/src/Preferences.h @@ -257,12 +257,12 @@ public: }; class SpinnerSliderPreference : public FloatPreference { - Q_OBJECT + Q_OBJECT public: - SpinnerSliderPreference(const QString& category, const QString& name, Getter getter, Setter setter) - : FloatPreference(category, name, getter, setter) { } + SpinnerSliderPreference(const QString& category, const QString& name, Getter getter, Setter setter) + : FloatPreference(category, name, getter, setter) { } - Type getType() override { return SpinnerSlider; } + Type getType() override { return SpinnerSlider; } }; class IntSpinnerPreference : public IntPreference { diff --git a/libraries/shared/src/SettingHandle.h b/libraries/shared/src/SettingHandle.h index 341a4cb101..e77ee00b05 100644 --- a/libraries/shared/src/SettingHandle.h +++ b/libraries/shared/src/SettingHandle.h @@ -39,7 +39,7 @@ public: QStringList childKeys() const; QStringList allKeys() const; bool contains(const QString& key) const; - int beginReadArray(const QString & prefix); + int beginReadArray(const QString & prefix); void beginWriteArray(const QString& prefix, int size = -1); void endArray(); void setArrayIndex(int i); diff --git a/libraries/shared/src/shared/PlatformHacks.h b/libraries/shared/src/shared/PlatformHacks.h new file mode 100644 index 0000000000..909f0bac9a --- /dev/null +++ b/libraries/shared/src/shared/PlatformHacks.h @@ -0,0 +1,18 @@ +// +// Created by Bradley Austin Davis on 2017/09/15 +// 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 +// +#pragma once +#ifndef hifi_shared_PlatformHacks_h +#define hifi_shared_PlatformHacks_h + +#include + +#if defined(Q_OS_ANDROID) +#include "platform/AndroidHacks.h" +#endif + +#endif diff --git a/libraries/shared/src/shared/platform/AndroidHacks.h b/libraries/shared/src/shared/platform/AndroidHacks.h new file mode 100644 index 0000000000..29ab013f98 --- /dev/null +++ b/libraries/shared/src/shared/platform/AndroidHacks.h @@ -0,0 +1,50 @@ +// +// androidhacks.h +// interface/src +// +// Created by Cristian Duarte & Gabriel Calero on 1/4/17. +// Copyright 2017 High Fidelity, Inc. +// +// hacks to get android to compile +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#pragma once +#ifndef hifi_shared_platform_androidhacks_h +#define hifi_shared_platform_androidhacks_h + +#include +#include + +#include + +// Only for gnu stl, so checking if using llvm +// (If there is a better check than this http://stackoverflow.com/questions/31657499/how-to-detect-stdlib-libc-in-the-preprocessor, improve this one) +#if (_LIBCPP_VERSION) + // NOTHING, these functions are well defined on libc++ +#else + +using namespace std; +namespace std +{ + // to_string impl + // error: no member named 'to_string' in namespace 'std' + // http://stackoverflow.com/questions/26095886/error-to-string-is-not-a-member-of-std + template + inline std::string to_string(T value) { + std::ostringstream os; + os << value; + return os.str(); + } + + inline float stof(std::string str) { + return atof(str.c_str()); + } +} + +#endif // _LIBCPP_VERSION + +#endif // hifi_androidhacks_h diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index 68a6fd25b9..f28157ff97 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -2,7 +2,5 @@ set(TARGET_NAME ui) setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebEngine WebSockets XmlPatterns) link_hifi_libraries(shared networking gl audio) -if (NOT ANDROID) - # Required for some low level GL interaction in the OffscreenQMLSurface - target_glew() -endif () +# Required for some low level GL interaction in the OffscreenQMLSurface +target_opengl() diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index 70daff944a..12cb7e2e4b 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -170,7 +170,7 @@ void VrMenu::addMenu(QMenu* menu) { QObject* parent = menu->parent(); QObject* qmlParent = nullptr; QMenu* parentMenu = dynamic_cast(parent); - if (parentMenu) { + if (parentMenu && menu->menuAction()) { MenuUserData* userData = MenuUserData::forObject(parentMenu->menuAction()); if (!userData) { return; diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index d03842d45a..6125aea15c 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -827,7 +827,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even switch (event->type()) { case QEvent::Resize: { QResizeEvent* resizeEvent = static_cast(event); - QWidget* widget = dynamic_cast(originalDestination); + QWidget* widget = static_cast(originalDestination); if (widget) { this->resize(resizeEvent->size()); } @@ -932,7 +932,7 @@ void OffscreenQmlSurface::focusDestroyed(QObject *obj) { } void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) { - QQuickItem* item = dynamic_cast(object); + QQuickItem* item = static_cast(object); if (!item) { setFocusText(false); _currentFocusItem = nullptr; @@ -1019,6 +1019,10 @@ void OffscreenQmlSurface::synthesizeKeyPress(QString key) { } void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool numeric) { +#if Q_OS_ANDROID + return; +#endif + if (!object) { return; } diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 18844a1995..3d06a4b223 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -88,7 +88,7 @@ ovrSession acquireOculusSession() { } if (!session) { - ovrInitParams initParams { + ovrInitParams initParams { ovrInit_RequestVersion | ovrInit_MixedRendering, OVR_MINOR_VERSION, nullptr, 0, 0 }; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 80c8698bb6..804ff7d62e 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -181,7 +181,7 @@ bool OculusLegacyDisplayPlugin::internalActivate() { } void OculusLegacyDisplayPlugin::internalDeactivate() { - Parent::internalDeactivate(); + Parent::internalDeactivate(); ovrHmd_Destroy(_hmd); _hmd = nullptr; ovr_Shutdown(); diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 20345467df..36bdd1c792 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -17,7 +17,7 @@ const float TARGET_RATE_OculusLegacy = 75.0f; class GLWindow; class OculusLegacyDisplayPlugin : public HmdDisplayPlugin { - using Parent = HmdDisplayPlugin; + using Parent = HmdDisplayPlugin; public: OculusLegacyDisplayPlugin(); bool isSupported() const override; diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index 9847e9f7b9..7c6886ad93 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -19,6 +19,7 @@ #include #include +#include #include @@ -114,13 +115,37 @@ public: } }; + + +const std::string VERTEX_SHADER_DEFINES{ R"GLSL( +#version 410 core +#define GPU_VERTEX_SHADER +#define GPU_TRANSFORM_IS_STEREO +#define GPU_TRANSFORM_STEREO_CAMERA +#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED +#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN +)GLSL" }; + +const std::string PIXEL_SHADER_DEFINES{ R"GLSL( +#version 410 core +#define GPU_PIXEL_SHADER +#define GPU_TRANSFORM_IS_STEREO +#define GPU_TRANSFORM_STEREO_CAMERA +#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED +#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN +)GLSL" }; + void testShaderBuild(const char* vs_src, const char * fs_src) { - auto vs = gpu::Shader::createVertex(std::string(vs_src)); - auto fs = gpu::Shader::createPixel(std::string(fs_src)); - auto pr = gpu::Shader::createProgram(vs, fs); - if (!gpu::Shader::makeProgram(*pr)) { + std::string error; + GLuint vs, fs; + if (!gl::compileShader(GL_VERTEX_SHADER, vs_src, VERTEX_SHADER_DEFINES, vs, error) || + !gl::compileShader(GL_FRAGMENT_SHADER, fs_src, PIXEL_SHADER_DEFINES, fs, error)) { throw std::runtime_error("Failed to compile shader"); } + auto pr = gl::compileProgram({ vs, fs }, error); + if (!pr) { + throw std::runtime_error("Failed to link shader"); + } } void QTestWindow::draw() { diff --git a/tools/oven/src/ui/BakeWidget.cpp b/tools/oven/src/ui/BakeWidget.cpp index 23a4822d82..9fb8f2f880 100644 --- a/tools/oven/src/ui/BakeWidget.cpp +++ b/tools/oven/src/ui/BakeWidget.cpp @@ -17,7 +17,7 @@ #include "BakeWidget.h" BakeWidget::BakeWidget(QWidget* parent, Qt::WindowFlags flags) : - QWidget(parent, flags) + QWidget(parent, flags) { }