diff --git a/CMakeLists.txt b/CMakeLists.txt index ff9fbe9244..0d95ac446f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,8 +14,12 @@ include("cmake/init.cmake") include("cmake/compiler.cmake") if (BUILD_SCRIBE_ONLY) - add_subdirectory(tools/scribe) - return() + add_subdirectory(tools/scribe) + return() +endif() + +if (NOT DEFINED CLIENT_ONLY) + set(CLIENT_ONLY 0) endif() if (NOT DEFINED SERVER_ONLY) @@ -23,59 +27,67 @@ if (NOT DEFINED SERVER_ONLY) endif() if (ANDROID OR UWP) - set(MOBILE 1) + set(MOBILE 1) else() - set(MOBILE 0) + set(MOBILE 0) endif() +set(BUILD_CLIENT_OPTION ON) +set(BUILD_SERVER_OPTION ON) +set(BUILD_TESTS_OPTION ON) +set(BUILD_TOOLS_OPTION ON) +set(BUILD_INSTALLER_OPTION ON) +set(GLES_OPTION OFF) +set(DISABLE_QML_OPTION OFF) + if (ANDROID OR UWP) - option(BUILD_SERVER "Build server components" OFF) - option(BUILD_TOOLS "Build tools" OFF) - option(BUILD_INSTALLER "Build installer" OFF) -else() - option(BUILD_SERVER "Build server components" ON) - option(BUILD_TOOLS "Build tools" ON) - option(BUILD_INSTALLER "Build installer" ON) + set(BUILD_SERVER_OPTION OFF) + set(BUILD_TOOLS_OPTION OFF) + set(BUILD_INSTALLER OFF) +endif() + +if (CLIENT_ONLY) + set(BUILD_SERVER_OPTION OFF) endif() if (SERVER_ONLY) - option(BUILD_CLIENT "Build client components" OFF) - option(BUILD_TESTS "Build tests" OFF) -else() - option(BUILD_CLIENT "Build client components" ON) - option(BUILD_TESTS "Build tests" ON) + set(BUILD_CLIENT_OPTION OFF) + set(BUILD_TESTS_OPTION OFF) endif() if (ANDROID) - option(USE_GLES "Use OpenGL ES" ON) - set(PLATFORM_QT_COMPONENTS AndroidExtras WebView) + set(GLES_OPTION ON) + set(PLATFORM_QT_COMPONENTS AndroidExtras WebView) else () - option(USE_GLES "Use OpenGL ES" OFF) - set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) + set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) endif () if (USE_GLES AND (NOT ANDROID)) - option(DISABLE_QML "Disable QML" ON) -else() - option(DISABLE_QML "Disable QML" OFF) + set(DISABLE_QML_OPTION ON) endif() + +option(BUILD_CLIENT "Build client components" ${BUILD_CLIENT_OPTION}) +option(BUILD_SERVER "Build server components" ${BUILD_SERVER_OPTION}) +option(BUILD_TESTS "Build tests" ${BUILD_TESTS_OPTION}) +option(BUILD_TOOLS "Build tools" ${BUILD_TOOLS_OPTION}) +option(BUILD_INSTALLER "Build installer" ${BUILD_INSTALLER_OPTION}) +option(USE_GLES "Use OpenGL ES" ${GLES_OPTION}) +option(DISABLE_QML "Disable QML" ${DISABLE_QML_OPTION}) option(DISABLE_KTX_CACHE "Disable KTX Cache" OFF) set(PLATFORM_QT_GL OpenGL) if (USE_GLES) add_definitions(-DUSE_GLES) - set(PLATFORM_GL_BACKEND gpu-gles) + set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gles) else() - set(PLATFORM_GL_BACKEND gpu-gl) + set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gl) endif() - foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS}) list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}") endforeach() - MESSAGE(STATUS "Build server: " ${BUILD_SERVER}) MESSAGE(STATUS "Build client: " ${BUILD_CLIENT}) MESSAGE(STATUS "Build tests: " ${BUILD_TESTS}) @@ -84,17 +96,17 @@ MESSAGE(STATUS "Build installer: " ${BUILD_INSTALLER}) MESSAGE(STATUS "GL ES: " ${USE_GLES}) if (DISABLE_QML) -MESSAGE(STATUS "QML disabled!") -add_definitions(-DDISABLE_QML) + MESSAGE(STATUS "QML disabled!") + add_definitions(-DDISABLE_QML) endif() if (DISABLE_KTX_CACHE) -MESSAGE(STATUS "KTX cache disabled!") -add_definitions(-DDISABLE_KTX_CACHE) + MESSAGE(STATUS "KTX cache disabled!") + add_definitions(-DDISABLE_KTX_CACHE) endif() if (UNIX AND DEFINED ENV{HIFI_MEMORY_DEBUGGING}) - MESSAGE(STATUS "Memory debugging is enabled") + MESSAGE(STATUS "Memory debugging is enabled") endif() # @@ -160,16 +172,16 @@ endif() add_subdirectory(tools) if (BUILD_TESTS) - add_subdirectory(tests) + add_subdirectory(tests) endif() if (BUILD_INSTALLER) - if (UNIX) - install( - DIRECTORY "${CMAKE_SOURCE_DIR}/scripts" - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/interface - COMPONENT ${CLIENT_COMPONENT} - ) - endif() - generate_installers() + if (UNIX) + install( + DIRECTORY "${CMAKE_SOURCE_DIR}/scripts" + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/interface + COMPONENT ${CLIENT_COMPONENT} + ) + endif() + generate_installers() endif() diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 87827a27d9..1eb43a45a5 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -46,15 +46,68 @@ static const uint8_t CPU_AFFINITY_COUNT_LOW = 1; static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000; #endif -const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; - static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" }; static QStringList BAKEABLE_TEXTURE_EXTENSIONS; -static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = {}; +static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = { }; + static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; static const QString BAKED_SCRIPT_SIMPLE_NAME = "asset.js"; +static const ModelBakeVersion CURRENT_MODEL_BAKE_VERSION = (ModelBakeVersion)((BakeVersion)ModelBakeVersion::COUNT - 1); +static const TextureBakeVersion CURRENT_TEXTURE_BAKE_VERSION = (TextureBakeVersion)((BakeVersion)TextureBakeVersion::COUNT - 1); +static const ScriptBakeVersion CURRENT_SCRIPT_BAKE_VERSION = (ScriptBakeVersion)((BakeVersion)ScriptBakeVersion::COUNT - 1); + +BakedAssetType assetTypeForExtension(const QString& extension) { + auto extensionLower = extension.toLower(); + if (BAKEABLE_MODEL_EXTENSIONS.contains(extensionLower)) { + return Model; + } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extensionLower.toLocal8Bit())) { + return Texture; + } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extensionLower)) { + return Script; + } + return Undefined; +} + +BakedAssetType assetTypeForFilename(const QString& filename) { + auto dotIndex = filename.lastIndexOf("."); + if (dotIndex == -1) { + return BakedAssetType::Undefined; + } + + auto extension = filename.mid(dotIndex + 1); + return assetTypeForExtension(extension); +} + +QString bakedFilenameForAssetType(BakedAssetType type) { + switch (type) { + case Model: + return BAKED_MODEL_SIMPLE_NAME; + case Texture: + return BAKED_TEXTURE_SIMPLE_NAME; + case Script: + return BAKED_SCRIPT_SIMPLE_NAME; + default: + return ""; + } +} + +BakeVersion currentBakeVersionForAssetType(BakedAssetType type) { + switch (type) { + case Model: + return (BakeVersion)CURRENT_MODEL_BAKE_VERSION; + case Texture: + return (BakeVersion)CURRENT_TEXTURE_BAKE_VERSION; + case Script: + return (BakeVersion)CURRENT_SCRIPT_BAKE_VERSION; + default: + return 0; + } +} + +const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; + void AssetServer::bakeAsset(const AssetUtils::AssetHash& assetHash, const AssetUtils::AssetPath& assetPath, const QString& filePath) { qDebug() << "Starting bake for: " << assetPath << assetHash; auto it = _pendingBakes.find(assetHash); @@ -167,36 +220,38 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU return false; } - auto dotIndex = path.lastIndexOf("."); - if (dotIndex == -1) { + BakedAssetType type = assetTypeForFilename(path); + + if (type == Undefined) { return false; } - auto extension = path.mid(dotIndex + 1); + QString bakedFilename = bakedFilenameForAssetType(type); + auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + assetHash + "/" + bakedFilename; + auto mappingIt = _fileMappings.find(bakedPath); + bool bakedMappingExists = mappingIt != _fileMappings.end(); - QString bakedFilename; + // If the path is mapped to the original file's hash, baking has been disabled for this + // asset + if (bakedMappingExists && mappingIt->second == assetHash) { + return false; + } bool loaded; AssetMeta meta; std::tie(loaded, meta) = readMetaFile(assetHash); - // TODO: Allow failed bakes that happened on old versions to be re-baked - if (loaded && meta.failedLastBake) { + if (type == Texture && !loaded) { return false; } - if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { - bakedFilename = BAKED_MODEL_SIMPLE_NAME; - } else if (loaded && BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit())) { - bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; - } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extension)) { - bakedFilename = BAKED_SCRIPT_SIMPLE_NAME; - } else { + auto currentVersion = currentBakeVersionForAssetType(type); + + if (loaded && (meta.failedLastBake && meta.bakeVersion >= currentVersion)) { return false; } - auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + assetHash + "/" + bakedFilename; - return _fileMappings.find(bakedPath) == _fileMappings.end(); + return !bakedMappingExists || (meta.bakeVersion < currentVersion); } bool interfaceRunning() { @@ -598,15 +653,9 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketLi // first, figure out from the mapping extension what type of file this is auto assetPathExtension = assetPath.mid(assetPath.lastIndexOf('.') + 1).toLower(); - QString bakedRootFile; - if (BAKEABLE_MODEL_EXTENSIONS.contains(assetPathExtension)) { - bakedRootFile = BAKED_MODEL_SIMPLE_NAME; - } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(assetPathExtension.toLocal8Bit())) { - bakedRootFile = BAKED_TEXTURE_SIMPLE_NAME; - } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(assetPathExtension)) { - bakedRootFile = BAKED_SCRIPT_SIMPLE_NAME; - } + auto type = assetTypeForFilename(assetPath); + QString bakedRootFile = bakedFilenameForAssetType(type); auto originalAssetHash = it->second; QString redirectedAssetHash; @@ -653,9 +702,19 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketLi auto query = QUrlQuery(url.query()); bool isSkybox = query.hasQueryItem("skybox"); if (isSkybox) { - writeMetaFile(originalAssetHash); - if (!bakingDisabled) { - maybeBake(assetPath, originalAssetHash); + bool loaded; + AssetMeta meta; + std::tie(loaded, meta) = readMetaFile(originalAssetHash); + + if (!loaded) { + AssetMeta needsBakingMeta; + needsBakingMeta.bakeVersion = NEEDS_BAKING_BAKE_VERSION; + + writeMetaFile(originalAssetHash, needsBakingMeta); + if (!bakingDisabled) { + maybeBake(assetPath, originalAssetHash); + } + } } } @@ -1275,15 +1334,19 @@ QString getBakeMapping(const AssetUtils::AssetHash& hash, const QString& relativ } void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, QString errors) { - qDebug() << "Failed: " << originalAssetHash << assetPath << errors; + qDebug() << "Failed to bake: " << originalAssetHash << assetPath << "(" << errors << ")"; bool loaded; AssetMeta meta; std::tie(loaded, meta) = readMetaFile(originalAssetHash); + auto type = assetTypeForFilename(assetPath); + auto currentTypeVersion = currentBakeVersionForAssetType(type); + meta.failedLastBake = true; meta.lastBakeErrors = errors; + meta.bakeVersion = currentTypeVersion; writeMetaFile(originalAssetHash, meta); @@ -1373,17 +1436,20 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina qWarning() << "Failed to remove temporary directory:" << bakedTempOutputDir; } - if (!errorCompletingBake) { - // create the meta file to store which version of the baking process we just completed - writeMetaFile(originalAssetHash); - } else { + auto type = assetTypeForFilename(originalAssetPath); + auto currentTypeVersion = currentBakeVersionForAssetType(type); + + AssetMeta meta; + meta.bakeVersion = currentTypeVersion; + meta.failedLastBake = errorCompletingBake; + + if (errorCompletingBake) { qWarning() << "Could not complete bake for" << originalAssetHash; - AssetMeta meta; - meta.failedLastBake = true; meta.lastBakeErrors = errorReason; - writeMetaFile(originalAssetHash, meta); } + writeMetaFile(originalAssetHash, meta); + _pendingBakes.remove(originalAssetHash); } @@ -1447,7 +1513,7 @@ bool AssetServer::writeMetaFile(AssetUtils::AssetHash originalAssetHash, const A // construct the JSON that will be in the meta file QJsonObject metaFileObject; - metaFileObject[BAKE_VERSION_KEY] = meta.bakeVersion; + metaFileObject[BAKE_VERSION_KEY] = (int)meta.bakeVersion; metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake; metaFileObject[LAST_BAKE_ERRORS_KEY] = meta.lastBakeErrors; @@ -1479,27 +1545,13 @@ bool AssetServer::setBakingEnabled(const AssetUtils::AssetPathList& paths, bool for (const auto& path : paths) { auto it = _fileMappings.find(path); if (it != _fileMappings.end()) { + auto type = assetTypeForFilename(path); + if (type == Undefined) { + continue; + } + QString bakedFilename = bakedFilenameForAssetType(type); + auto hash = it->second; - - auto dotIndex = path.lastIndexOf("."); - if (dotIndex == -1) { - continue; - } - - auto extension = path.mid(dotIndex + 1); - - QString bakedFilename; - - if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { - bakedFilename = BAKED_MODEL_SIMPLE_NAME; - } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) { - bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; - } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extension)) { - bakedFilename = BAKED_SCRIPT_SIMPLE_NAME; - } else { - continue; - } - auto bakedMapping = getBakeMapping(hash, bakedFilename); auto it = _fileMappings.find(bakedMapping); diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 00ab27c74d..a55a15e6fc 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -23,8 +23,47 @@ #include "RegisteredMetaTypes.h" +using BakeVersion = int; +static const BakeVersion INITIAL_BAKE_VERSION = 0; +static const BakeVersion NEEDS_BAKING_BAKE_VERSION = -1; + +enum BakedAssetType : int { + Model = 0, + Texture, + Script, + + NUM_ASSET_TYPES, + Undefined +}; + +// ATTENTION! If you change the current version for an asset type, you will also +// need to update the function currentBakeVersionForAssetType() inside of AssetServer.cpp. +enum class ModelBakeVersion : BakeVersion { + Initial = INITIAL_BAKE_VERSION, + + COUNT +}; + +// ATTENTION! See above. +enum class TextureBakeVersion : BakeVersion { + Initial = INITIAL_BAKE_VERSION, + + COUNT +}; + +// ATTENTION! See above. +enum class ScriptBakeVersion : BakeVersion { + Initial = INITIAL_BAKE_VERSION, + FixEmptyScripts, + + COUNT +}; + struct AssetMeta { - int bakeVersion { 0 }; + AssetMeta() { + } + + BakeVersion bakeVersion; bool failedLastBake { false }; QString lastBakeErrors; }; diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 60cb1e349b..1255c18e71 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -476,6 +476,7 @@ void EntityScriptServer::clear() { // do this here (instead of in deleter) to avoid marshalling unload signals back to this thread _entitiesScriptEngine->unloadAllEntityScripts(); _entitiesScriptEngine->stop(); + _entitiesScriptEngine->waitTillDoneRunning(); } _entityViewer.clear(); @@ -565,8 +566,15 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer mess void EntityScriptServer::aboutToFinish() { shutdownScriptEngine(); + auto entityScriptingInterface = DependencyManager::get(); // our entity tree is going to go away so tell that to the EntityScriptingInterface - DependencyManager::get()->setEntityTree(nullptr); + entityScriptingInterface->setEntityTree(nullptr); + + // Should always be true as they are singletons. + if (entityScriptingInterface->getPacketSender() == &_entityEditSender) { + // The packet sender is about to go away. + entityScriptingInterface->setPacketSender(nullptr); + } DependencyManager::get()->cleanup(); diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 032f83e8be..742c5b5b94 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -23,7 +23,7 @@ macro(GENERATE_INSTALLERS) set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) if (PR_BUILD) - set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") + set(CPACK_NSIS_COMPRESSOR "bzip2") endif () set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) @@ -58,6 +58,23 @@ macro(GENERATE_INSTALLERS) set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR}) set(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT ${CLIENT_COMPONENT}) include(InstallRequiredSystemLibraries) + + if (CLIENT_ONLY OR SERVER_ONLY) + set(CPACK_MONOLITHIC_INSTALL 1) + endif () + + # setup conditional checks for server component selection depending on + # the inclusion of the server component at all + if (CLIENT_ONLY) + set(SERVER_COMPONENT_CONDITIONAL "0 == 1") + set(CLIENT_COMPONENT_CONDITIONAL "1 == 1") + elseif (SERVER_ONLY) + set(SERVER_COMPONENT_CONDITIONAL "1 == 1") + set(CLIENT_COMPONENT_CONDITIONAL "0 == 1") + else () + set(SERVER_COMPONENT_CONDITIONAL "\\\${SectionIsSelected} \\\${${SERVER_COMPONENT}}") + set(CLIENT_COMPONENT_CONDITIONAL "\\\${SectionIsSelected} \\\${${CLIENT_COMPONENT}}") + endif () elseif (APPLE) # produce a drag and drop DMG on OS X set(CPACK_GENERATOR "DragNDrop") @@ -88,8 +105,13 @@ macro(GENERATE_INSTALLERS) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") - cpack_add_component(${CLIENT_COMPONENT} DISPLAY_NAME "High Fidelity Interface") - cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "High Fidelity Sandbox") + if (BUILD_CLIENT) + cpack_add_component(${CLIENT_COMPONENT} DISPLAY_NAME "High Fidelity Interface") + endif () + + if (BUILD_SERVER) + cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "High Fidelity Sandbox") + endif () include(CPack) endmacro() diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index a962504e72..601fbdaa20 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -27,6 +27,11 @@ macro(SET_PACKAGING_PARAMETERS) message(STATUS "The BRANCH environment variable is: $ENV{BRANCH}") message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}") + # setup component categories for installer + set(DDE_COMPONENT dde) + set(CLIENT_COMPONENT client) + set(SERVER_COMPONENT server) + if (RELEASE_TYPE STREQUAL "PRODUCTION") set(DEPLOY_PACKAGE TRUE) set(PRODUCTION_BUILD 1) @@ -153,11 +158,6 @@ macro(SET_PACKAGING_PARAMETERS) set(GA_TRACKING_ID $ENV{GA_TRACKING_ID}) endif () - # setup component categories for installer - set(DDE_COMPONENT dde) - set(CLIENT_COMPONENT client) - set(SERVER_COMPONENT server) - # print out some results for testing this new build feature message(STATUS "The BUILD_GLOBAL_SERVICES variable is: ${BUILD_GLOBAL_SERVICES}") message(STATUS "The USE_STABLE_GLOBAL_SERVICES variable is: ${USE_STABLE_GLOBAL_SERVICES}") diff --git a/cmake/templates/CPackProperties.cmake.in b/cmake/templates/CPackProperties.cmake.in index 1a0fa2fac7..9c303f7532 100644 --- a/cmake/templates/CPackProperties.cmake.in +++ b/cmake/templates/CPackProperties.cmake.in @@ -46,3 +46,5 @@ set(CLIENT_ID_REG_KEY "@CLIENT_ID_REG_KEY@") set(INSTALLER_HEADER_IMAGE "@INSTALLER_HEADER_IMAGE@") set(UNINSTALLER_HEADER_IMAGE "@UNINSTALLER_HEADER_IMAGE@") set(ADD_REMOVE_ICON_PATH "@ADD_REMOVE_ICON_PATH@") +set(SERVER_COMPONENT_CONDITIONAL "@SERVER_COMPONENT_CONDITIONAL@") +set(CLIENT_COMPONENT_CONDITIONAL "@CLIENT_COMPONENT_CONDITIONAL@") diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index f174727f95..7faa67d1b0 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -437,6 +437,12 @@ SectionEnd !define MUI_PAGE_CUSTOMFUNCTION_PRE PageComponentsPre @CPACK_NSIS_PAGE_COMPONENTS@ + ; the MUI_PAGE_CUSTOMFUNCTION_PRE shouldn't be defined here + ; which can happen for a component-less (like client only) install + !ifdef MUI_PAGE_CUSTOMFUNCTION_PRE + !undef MUI_PAGE_CUSTOMFUNCTION_PRE + !endif + Page custom PostInstallOptionsPage ReadPostInstallOptions !define MUI_PAGE_CUSTOMFUNCTION_PRE PageInstallFilesPre @@ -612,10 +618,11 @@ Function InstallTypesPage ${If} $CustomInstallTemporaryState == ${BST_UNCHECKED} ${NSD_Check} $ExpressInstallRadioButton + Call ChangeExpressLabel + ${Else} + Call ChangeCustomLabel ${EndIf} - Call ChangeExpressLabel - nsDialogs::Show FunctionEnd @@ -649,7 +656,7 @@ Function PostInstallOptionsPage StrCpy $CurrentOffset 0 StrCpy $OffsetUnits u - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @INTERFACE_HF_SHORTCUT_NAME@" Pop $DesktopClientCheckbox IntOp $CurrentOffset $CurrentOffset + 15 @@ -658,7 +665,7 @@ Function PostInstallOptionsPage !insertmacro SetInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED} ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @CONSOLE_HF_SHORTCUT_NAME@" Pop $DesktopServerCheckbox IntOp $CurrentOffset $CurrentOffset + 15 @@ -667,7 +674,7 @@ Function PostInstallOptionsPage !insertmacro SetInstallOption $DesktopServerCheckbox @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ ${BST_UNCHECKED} ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ after install" Pop $LaunchServerNowCheckbox @@ -681,7 +688,7 @@ Function PostInstallOptionsPage IntOp $CurrentOffset $CurrentOffset + 15 ${EndIf} - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @INTERFACE_HF_SHORTCUT_NAME@ after install" Pop $LaunchClientNowCheckbox IntOp $CurrentOffset $CurrentOffset + 30 @@ -694,7 +701,7 @@ Function PostInstallOptionsPage ${EndIf} ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ on startup" Pop $ServerStartupCheckbox IntOp $CurrentOffset $CurrentOffset + 15 @@ -703,7 +710,7 @@ Function PostInstallOptionsPage !insertmacro SetInstallOption $ServerStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED} ${EndIf} - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Perform a clean install (Delete older settings and content)" Pop $CleanInstallCheckbox IntOp $CurrentOffset $CurrentOffset + 15 @@ -711,11 +718,11 @@ Function PostInstallOptionsPage ${If} @PR_BUILD@ == 1 ; a PR build defaults all install options expect LaunchServerNowCheckbox, LaunchClientNowCheckbox and the settings copy to unchecked - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ${NSD_SetState} $DesktopClientCheckbox ${BST_UNCHECKED} ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ${NSD_SetState} $DesktopServerCheckbox ${BST_UNCHECKED} ${NSD_SetState} $ServerStartupCheckbox ${BST_UNCHECKED} ${EndIf} @@ -774,12 +781,12 @@ Function ReadInstallTypes FunctionEnd Function ReadPostInstallOptions - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if the user asked for a desktop shortcut to High Fidelity ${NSD_GetState} $DesktopClientCheckbox $DesktopClientState ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ; check if the user asked for a desktop shortcut to Sandbox ${NSD_GetState} $DesktopServerCheckbox $DesktopServerState @@ -792,24 +799,24 @@ Function ReadPostInstallOptions ${NSD_GetState} $CopyFromProductionCheckbox $CopyFromProductionState ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ; check if we need to launch the server post-install ${NSD_GetState} $LaunchServerNowCheckbox $LaunchServerNowState ${EndIf} - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if we need to launch the client post-install ${NSD_GetState} $LaunchClientNowCheckbox $LaunchClientNowState ${EndIf} - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if the user asked for a clean install ${NSD_GetState} $CleanInstallCheckbox $CleanInstallState ${EndIf} FunctionEnd Function HandlePostInstallOptions - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if the user asked for a desktop shortcut to High Fidelity ${If} $DesktopClientState == ${BST_CHECKED} CreateShortCut "$DESKTOP\@INTERFACE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" @@ -820,7 +827,7 @@ Function HandlePostInstallOptions ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ; check if the user asked for a desktop shortcut to Sandbox ${If} $DesktopServerState == ${BST_CHECKED} CreateShortCut "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" @@ -849,7 +856,7 @@ Function HandlePostInstallOptions ${EndIf} ${EndIf} - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if the user asked for a clean install ${If} $CleanInstallState == ${BST_CHECKED} SetShellVarContext current @@ -886,7 +893,8 @@ Function HandlePostInstallOptions ${EndIf} ${EndIf} - ${If} $LaunchServerNowState == ${BST_CHECKED} + ${If} @SERVER_COMPONENT_CONDITIONAL@ + ${AndIf} $LaunchServerNowState == ${BST_CHECKED} !insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ YES ; both launches use the explorer trick in case the user has elevated permissions for the installer @@ -900,7 +908,7 @@ Function HandlePostInstallOptions Exec '"$WINDIR\explorer.exe" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"' ${EndIf} - ${Else} + ${ElseIf} @CLIENT_COMPONENT_CONDITIONAL@ !insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ NO ; launch uses the explorer trick in case the user has elevated permissions for the installer @@ -1026,7 +1034,7 @@ Section "-Core installation" @CPACK_NSIS_CREATE_ICONS_EXTRA@ ; Conditional handling for Interface specific options - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@INTERFACE_SHORTCUT_NAME@.lnk" \ "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" @@ -1041,7 +1049,7 @@ Section "-Core installation" ${EndIf} ; Conditional handling for server console shortcut - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" \ "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" ${EndIf} @@ -1185,8 +1193,8 @@ Function .onSelChange !insertmacro SectionList MaybeSelectionChanged ; if neither component is selected, disable the install button - ${IfNot} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} - ${AndIfNot} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${IfNot} @CLIENT_COMPONENT_CONDITIONAL@ + ${AndIfNot} @SERVER_COMPONENT_CONDITIONAL@ GetDlgItem $0 $HWNDPARENT 1 EnableWindow $0 0 ${Else} diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 3fec5a9000..853e87ae23 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -132,6 +132,41 @@ $(document).ready(function(){ var ACTIVE_BACKUP_ROW_CLASS = 'active-backup'; var CORRUPTED_ROW_CLASS = 'danger'; + $('body').on('click', '.' + BACKUP_DOWNLOAD_LINK_CLASS, function(ev) { + ev.preventDefault(); + var backupID = $(this).data('backup-id'); + + showSpinnerAlert("Preparing backup..."); + function checkBackupStatus() { + $.ajax({ + url: "/api/backups/" + backupID, + dataType: 'json', + success: function(data) { + if (data.complete) { + if (data.error == '') { + location.href = "/api/backups/download/" + backupID; + swal.close(); + } else { + showErrorMessage( + "Error", + "There was an error preparing your backup. Please refresh the page and try again." + ); + } + } else { + setTimeout(checkBackupStatus, 500); + } + }, + error: function() { + showErrorMessage( + "Error", + "There was an error preparing your backup." + ); + }, + }); + } + checkBackupStatus(); + }); + function reloadBackupInformation() { // make a GET request to get backup information to populate the table $.ajax({ @@ -164,7 +199,7 @@ $(document).ready(function(){ + ""; } diff --git a/domain-server/resources/web/js/shared.js b/domain-server/resources/web/js/shared.js index 84bba4de56..1647da045f 100644 --- a/domain-server/resources/web/js/shared.js +++ b/domain-server/resources/web/js/shared.js @@ -2,7 +2,7 @@ if (typeof Settings === "undefined") { Settings = {}; } -Object.assign(Settings, { +$.extend(Settings, { DEPRECATED_CLASS: 'deprecated-setting', TRIGGER_CHANGE_CLASS: 'trigger-change', DATA_ROW_CLASS: 'value-row', diff --git a/domain-server/src/DomainContentBackupManager.cpp b/domain-server/src/DomainContentBackupManager.cpp index 85040d8c35..518ed73f9e 100644 --- a/domain-server/src/DomainContentBackupManager.cpp +++ b/domain-server/src/DomainContentBackupManager.cpp @@ -55,15 +55,20 @@ DomainContentBackupManager::DomainContentBackupManager(const QString& backupDire const QVariantList& backupRules, std::chrono::milliseconds persistInterval, bool debugTimestampNow) : + _consolidatedBackupDirectory(PathUtils::generateTemporaryDir()), _backupDirectory(backupDirectory), _persistInterval(persistInterval), _lastCheck(p_high_resolution_clock::now()) { - setObjectName("DomainContentBackupManager"); // Make sure the backup directory exists. QDir(_backupDirectory).mkpath("."); parseBackupRules(backupRules); + + constexpr int CONSOLIDATED_BACKUP_CLEANER_INTERVAL_MSECS = 30 * 1000; + _consolidatedBackupCleanupTimer.setInterval(CONSOLIDATED_BACKUP_CLEANER_INTERVAL_MSECS); + connect(&_consolidatedBackupCleanupTimer, &QTimer::timeout, this, &DomainContentBackupManager::removeOldConsolidatedBackups); + _consolidatedBackupCleanupTimer.start(); } void DomainContentBackupManager::parseBackupRules(const QVariantList& backupRules) { @@ -498,23 +503,87 @@ void DomainContentBackupManager::backup() { } } -void DomainContentBackupManager::consolidateBackup(MiniPromise::Promise promise, QString fileName) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "consolidateBackup", Q_ARG(MiniPromise::Promise, promise), - Q_ARG(QString, fileName)); - return; +void DomainContentBackupManager::removeOldConsolidatedBackups() { + constexpr std::chrono::minutes MAX_TIME_TO_KEEP_CONSOLIDATED_BACKUP { 30 }; + auto now = std::chrono::system_clock::now(); + auto it = _consolidatedBackups.begin(); + while (it != _consolidatedBackups.end()) { + auto& backup = it->second; + auto diff = now - backup.createdAt; + if (diff > MAX_TIME_TO_KEEP_CONSOLIDATED_BACKUP) { + QFile oldBackup(backup.absoluteFilePath); + if (!oldBackup.exists() || oldBackup.remove()) { + qDebug() << "Removed old consolidated backup: " << backup.absoluteFilePath; + it = _consolidatedBackups.erase(it); + } else { + qDebug() << "Failed to remove old consolidated backup: " << backup.absoluteFilePath; + it++; + } + } else { + it++; + } + } +} + +ConsolidatedBackupInfo DomainContentBackupManager::consolidateBackup(QString fileName) { + { + std::lock_guard lock { _consolidatedBackupsMutex }; + auto it = _consolidatedBackups.find(fileName); + + if (it != _consolidatedBackups.end()) { + return it->second; + } + } + QMetaObject::invokeMethod(this, "consolidateBackupInternal", Q_ARG(QString, fileName)); + return { + ConsolidatedBackupInfo::CONSOLIDATING, + "", + "", + std::chrono::system_clock::now() + }; +} + +void DomainContentBackupManager::consolidateBackupInternal(QString fileName) { + auto markFailure = [this, &fileName](QString error) { + qWarning() << "Failed to consolidate backup:" << fileName << error; + { + std::lock_guard lock { _consolidatedBackupsMutex }; + auto& consolidatedBackup = _consolidatedBackups[fileName]; + consolidatedBackup.state = ConsolidatedBackupInfo::COMPLETE_WITH_ERROR; + consolidatedBackup.error = error; + } + }; + + { + std::lock_guard lock { _consolidatedBackupsMutex }; + + auto it = _consolidatedBackups.find(fileName); + if (it != _consolidatedBackups.end()) { + return; + } + + _consolidatedBackups[fileName] = { + ConsolidatedBackupInfo::CONSOLIDATING, + "", + "", + std::chrono::system_clock::now() + }; } QDir backupDir { _backupDirectory }; if (!backupDir.exists()) { - qCritical() << "Backup directory does not exist, bailing consolidation of backup"; - promise->resolve({ { "success", false } }); + markFailure("Backup directory does not exist, bailing consolidation of backup"); return; } auto filePath = backupDir.absoluteFilePath(fileName); + + if (!QFile::exists(filePath)) { + markFailure("Backup does not exist"); + return; + } - auto copyFilePath = QDir::tempPath() + "/" + fileName; + auto copyFilePath = _consolidatedBackupDirectory + "/" + fileName; { QFile copyFile(copyFilePath); @@ -523,8 +592,7 @@ void DomainContentBackupManager::consolidateBackup(MiniPromise::Promise promise, } auto copySuccess = QFile::copy(filePath, copyFilePath); if (!copySuccess) { - qCritical() << "Failed to create copy of backup."; - promise->resolve({ { "success", false } }); + markFailure("Failed to create copy of backup."); return; } @@ -532,7 +600,7 @@ void DomainContentBackupManager::consolidateBackup(MiniPromise::Promise promise, if (!zip.open(QuaZip::mdAdd)) { qCritical() << "Could not open backup archive:" << filePath; qCritical() << " ERROR:" << zip.getZipError(); - promise->resolve({ { "success", false } }); + markFailure("Could not open backup archive"); return; } @@ -544,14 +612,17 @@ void DomainContentBackupManager::consolidateBackup(MiniPromise::Promise promise, if (zip.getZipError() != UNZ_OK) { qCritical() << "Failed to consolidate backup: " << zip.getZipError(); - promise->resolve({ { "success", false } }); + markFailure("Failed to consolidate backup"); return; } - promise->resolve({ - { "success", true }, - { "backupFilePath", copyFilePath } - }); + { + std::lock_guard lock { _consolidatedBackupsMutex }; + auto& consolidatedBackup = _consolidatedBackups[fileName]; + consolidatedBackup.state = ConsolidatedBackupInfo::COMPLETE_WITH_SUCCESS; + consolidatedBackup.absoluteFilePath = copyFilePath; + } + } void DomainContentBackupManager::createManualBackup(MiniPromise::Promise promise, const QString& name) { diff --git a/domain-server/src/DomainContentBackupManager.h b/domain-server/src/DomainContentBackupManager.h index fbc8084ac6..2b07afe0b3 100644 --- a/domain-server/src/DomainContentBackupManager.h +++ b/domain-server/src/DomainContentBackupManager.h @@ -15,9 +15,15 @@ #ifndef hifi_DomainContentBackupManager_h #define hifi_DomainContentBackupManager_h +#include + #include #include #include +#include + +#include +#include #include @@ -38,6 +44,18 @@ struct BackupItemInfo { bool isManualBackup; }; +struct ConsolidatedBackupInfo { + enum State { + CONSOLIDATING, + COMPLETE_WITH_ERROR, + COMPLETE_WITH_SUCCESS + }; + State state; + QString error; + QString absoluteFilePath; + std::chrono::system_clock::time_point createdAt; +}; + class DomainContentBackupManager : public GenericThread { Q_OBJECT public: @@ -61,6 +79,7 @@ public: void addBackupHandler(BackupHandlerPointer handler); void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist void replaceData(QByteArray data); + ConsolidatedBackupInfo consolidateBackup(QString fileName); public slots: void getAllBackupsAndStatus(MiniPromise::Promise promise); @@ -68,7 +87,6 @@ public slots: void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName); void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup); void deleteBackup(MiniPromise::Promise promise, const QString& backupName); - void consolidateBackup(MiniPromise::Promise promise, QString fileName); signals: void loadCompleted(); @@ -91,11 +109,21 @@ protected: bool recoverFromBackupZip(const QString& backupName, QuaZip& backupZip); +private slots: + void removeOldConsolidatedBackups(); + void consolidateBackupInternal(QString fileName); + private: + QTimer _consolidatedBackupCleanupTimer; + + const QString _consolidatedBackupDirectory; const QString _backupDirectory; std::vector _backupHandlers; std::chrono::milliseconds _persistInterval { 0 }; + std::mutex _consolidatedBackupsMutex; + std::unordered_map _consolidatedBackups; + std::atomic _isRecovering { false }; QString _recoveryFilename { }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index a11fe74c58..197ac7eac2 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -164,6 +164,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : _iceServerAddr(ICE_SERVER_DEFAULT_HOSTNAME), _iceServerPort(ICE_SERVER_DEFAULT_PORT) { + PathUtils::removeTemporaryApplicationDirs(); + parseCommandLine(); DependencyManager::set(); @@ -1933,6 +1935,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString URI_API_DOMAINS_ID = "/api/domains/"; const QString URI_API_BACKUPS = "/api/backups"; const QString URI_API_BACKUPS_ID = "/api/backups/"; + const QString URI_API_BACKUPS_DOWNLOAD_ID = "/api/backups/download/"; const QString URI_API_BACKUPS_RECOVER = "/api/backups/recover/"; const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; @@ -2133,37 +2136,40 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url }); _contentManager->getAllBackupsAndStatus(deferred); return true; + } else if (url.path().startsWith(URI_API_BACKUPS_DOWNLOAD_ID)) { + auto id = url.path().mid(QString(URI_API_BACKUPS_DOWNLOAD_ID).length()); + auto info = _contentManager->consolidateBackup(id); + + if (info.state == ConsolidatedBackupInfo::COMPLETE_WITH_SUCCESS) { + auto file { std::unique_ptr(new QFile(info.absoluteFilePath)) }; + if (file->open(QIODevice::ReadOnly)) { + constexpr const char* CONTENT_TYPE_ZIP = "application/zip"; + auto downloadedFilename = id; + downloadedFilename.replace(QRegularExpression(".zip$"), ".content.zip"); + auto contentDisposition = "attachment; filename=\"" + downloadedFilename + "\""; + connectionPtr->respond(HTTPConnection::StatusCode200, std::move(file), CONTENT_TYPE_ZIP, { + { "Content-Disposition", contentDisposition.toUtf8() } + }); + } else { + qCritical(domain_server) << "Unable to load consolidated backup at:" << info.absoluteFilePath; + connectionPtr->respond(HTTPConnection::StatusCode500, "Error opening backup"); + } + } else if (info.state == ConsolidatedBackupInfo::COMPLETE_WITH_ERROR) { + connectionPtr->respond(HTTPConnection::StatusCode500, ("Error creating backup: " + info.error).toUtf8()); + } else { + connectionPtr->respond(HTTPConnection::StatusCode400, "Backup unavailable"); + } + return true; } else if (url.path().startsWith(URI_API_BACKUPS_ID)) { auto id = url.path().mid(QString(URI_API_BACKUPS_ID).length()); - auto deferred = makePromise("consolidateBackup"); - deferred->then([connectionPtr, JSON_MIME_TYPE, id](QString error, QVariantMap result) { - if (!connectionPtr) { - return; - } + auto info = _contentManager->consolidateBackup(id); - QJsonObject rootJSON; - auto success = result["success"].toBool(); - if (success) { - auto path = result["backupFilePath"].toString(); - auto file { std::unique_ptr(new QFile(path)) }; - if (file->open(QIODevice::ReadOnly)) { - constexpr const char* CONTENT_TYPE_ZIP = "application/zip"; - - auto downloadedFilename = id; - downloadedFilename.replace(QRegularExpression(".zip$"), ".content.zip"); - auto contentDisposition = "attachment; filename=\"" + downloadedFilename + "\""; - connectionPtr->respond(HTTPConnection::StatusCode200, std::move(file), CONTENT_TYPE_ZIP, { - { "Content-Disposition", contentDisposition.toUtf8() } - }); - } else { - qCritical(domain_server) << "Unable to load consolidated backup at:" << path << result; - connectionPtr->respond(HTTPConnection::StatusCode500, "Error opening backup"); - } - } else { - connectionPtr->respond(HTTPConnection::StatusCode400); - } - }); - _contentManager->consolidateBackup(deferred, id); + QJsonObject rootJSON { + { "complete", info.state == ConsolidatedBackupInfo::COMPLETE_WITH_SUCCESS }, + { "error", info.error } + }; + QJsonDocument docJSON { rootJSON }; + connectionPtr->respond(HTTPConnection::StatusCode200, docJSON.toJson(), JSON_MIME_TYPE.toUtf8()); return true; } else if (url.path() == URI_RESTART) { @@ -2216,7 +2222,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString ASSIGNMENT_INSTANCES_HEADER = "ASSIGNMENT-INSTANCES"; const QString ASSIGNMENT_POOL_HEADER = "ASSIGNMENT-POOL"; - QByteArray assignmentInstancesValue = connection->requestHeaders().value(ASSIGNMENT_INSTANCES_HEADER.toLocal8Bit()); + QByteArray assignmentInstancesValue = connection->requestHeader(ASSIGNMENT_INSTANCES_HEADER.toLocal8Bit()); int numInstances = 1; @@ -2228,7 +2234,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url } QString assignmentPool = emptyPool; - QByteArray assignmentPoolValue = connection->requestHeaders().value(ASSIGNMENT_POOL_HEADER.toLocal8Bit()); + QByteArray assignmentPoolValue = connection->requestHeader(ASSIGNMENT_POOL_HEADER.toLocal8Bit()); if (!assignmentPoolValue.isEmpty()) { // specific pool requested, set that on the created assignment @@ -2626,7 +2632,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl if (!_oauthProviderURL.isEmpty() && (adminUsersVariant.isValid() || adminRolesVariant.isValid())) { - QString cookieString = connection->requestHeaders().value(HTTP_COOKIE_HEADER_KEY); + QString cookieString = connection->requestHeader(HTTP_COOKIE_HEADER_KEY); const QString COOKIE_UUID_REGEX_STRING = HIFI_SESSION_COOKIE_KEY + "=([\\d\\w-]+)($|;)"; QRegExp cookieUUIDRegex(COOKIE_UUID_REGEX_STRING); @@ -2671,7 +2677,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl static const QByteArray REQUESTED_WITH_HEADER = "X-Requested-With"; static const QString XML_REQUESTED_WITH = "XMLHttpRequest"; - if (connection->requestHeaders().value(REQUESTED_WITH_HEADER) == XML_REQUESTED_WITH) { + if (connection->requestHeader(REQUESTED_WITH_HEADER) == XML_REQUESTED_WITH) { // unauthorized XHR requests get a 401 and not a 302, since there isn't an XHR // path to OAuth authorize connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY); @@ -2702,7 +2708,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl const QByteArray BASIC_AUTH_HEADER_KEY = "Authorization"; // check if a username and password have been provided with the request - QString basicAuthString = connection->requestHeaders().value(BASIC_AUTH_HEADER_KEY); + QString basicAuthString = connection->requestHeader(BASIC_AUTH_HEADER_KEY); if (!basicAuthString.isEmpty()) { QStringList splitAuthString = basicAuthString.split(' '); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 4ce35027f2..d98ac67dfd 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -26,17 +26,17 @@ generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources if (ANDROID) # on Android, don't compress the rcc binary add_custom_command( - OUTPUT ${RESOURCES_RCC} - DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} - COMMAND "${QT_DIR}/bin/rcc" - ARGS ${RESOURCES_QRC} -no-compress -binary -o ${RESOURCES_RCC} + OUTPUT ${RESOURCES_RCC} + DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} + COMMAND "${QT_DIR}/bin/rcc" + ARGS ${RESOURCES_QRC} -no-compress -binary -o ${RESOURCES_RCC} ) else () add_custom_command( - OUTPUT ${RESOURCES_RCC} - DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} - COMMAND "${QT_DIR}/bin/rcc" - ARGS ${RESOURCES_QRC} -binary -o ${RESOURCES_RCC} + OUTPUT ${RESOURCES_RCC} + DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} + COMMAND "${QT_DIR}/bin/rcc" + ARGS ${RESOURCES_QRC} -binary -o ${RESOURCES_RCC} ) endif() @@ -203,12 +203,6 @@ if (WIN32) add_dependency_external_projects(steamworks) endif() -# include OPENSSL -include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") - -# append OpenSSL to our list of libraries to link -target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) - # disable /OPT:REF and /OPT:ICF for the Debug builds # This will prevent the following linker warnings # LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification @@ -232,6 +226,9 @@ link_hifi_libraries( # include the binary directory of render-utils for shader includes target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/render-utils") +# include OpenSSL +target_openssl() + target_bullet() target_opengl() add_crashpad() diff --git a/interface/resources/icons/+android/stats.svg b/interface/resources/icons/+android/stats.svg new file mode 100644 index 0000000000..f642650c66 --- /dev/null +++ b/interface/resources/icons/+android/stats.svg @@ -0,0 +1,16 @@ + + + + + + diff --git a/interface/resources/icons/tablet-icons/market-a-msg.svg b/interface/resources/icons/tablet-icons/market-a-msg.svg new file mode 100644 index 0000000000..0ab93f3cc8 --- /dev/null +++ b/interface/resources/icons/tablet-icons/market-a-msg.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/market-a.svg b/interface/resources/icons/tablet-icons/market-a.svg index f8ba17301e..db2d948d7b 100644 --- a/interface/resources/icons/tablet-icons/market-a.svg +++ b/interface/resources/icons/tablet-icons/market-a.svg @@ -1,64 +1,15 @@ - - - -image/svg+xml \ No newline at end of file + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/market-i-msg.svg b/interface/resources/icons/tablet-icons/market-i-msg.svg new file mode 100644 index 0000000000..488c507c6e --- /dev/null +++ b/interface/resources/icons/tablet-icons/market-i-msg.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/market-i.svg b/interface/resources/icons/tablet-icons/market-i.svg index bf9aa9335f..7d11507cdb 100644 --- a/interface/resources/icons/tablet-icons/market-i.svg +++ b/interface/resources/icons/tablet-icons/market-i.svg @@ -1,23 +1,19 @@ - - + - - - - - - - - + + + + diff --git a/interface/resources/qml/+android/StatText.qml b/interface/resources/qml/+android/StatText.qml new file mode 100644 index 0000000000..5dc8377030 --- /dev/null +++ b/interface/resources/qml/+android/StatText.qml @@ -0,0 +1,9 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +Text { + color: "white"; + style: Text.Outline; + styleColor: "black"; + font.pixelSize: 15; +} diff --git a/interface/resources/qml/controls-uit/FilterBar.qml b/interface/resources/qml/controls-uit/FilterBar.qml new file mode 100644 index 0000000000..ecae790b22 --- /dev/null +++ b/interface/resources/qml/controls-uit/FilterBar.qml @@ -0,0 +1,321 @@ +// +// FilterBar.qml +// +// Created by Zach Fox on 17 Feb 2018-03-12 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 + +import "../styles-uit" +import "../controls-uit" as HifiControls + +Item { + id: root; + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + readonly property bool isFaintGrayColorScheme: colorScheme == hifi.colorSchemes.faintGray + property bool error: false; + property alias textFieldHeight: textField.height; + property string placeholderText; + property alias dropdownHeight: dropdownContainer.height; + property alias text: textField.text; + property alias primaryFilterChoices: filterBarModel; + property int primaryFilter_index: -1; + property string primaryFilter_filterName: ""; + property string primaryFilter_displayName: ""; + signal accepted; + + onPrimaryFilter_indexChanged: { + if (primaryFilter_index === -1) { + primaryFilter_filterName = ""; + primaryFilter_displayName = ""; + } else { + primaryFilter_filterName = filterBarModel.get(primaryFilter_index).filterName; + primaryFilter_displayName = filterBarModel.get(primaryFilter_index).displayName; + } + } + + TextField { + id: textField; + + anchors.top: parent.top; + anchors.right: parent.right; + anchors.left: parent.left; + + font.family: "Fira Sans" + font.pixelSize: hifi.fontSizes.textFieldInput; + + placeholderText: root.primaryFilter_index === -1 ? root.placeholderText : ""; + + TextMetrics { + id: primaryFilterTextMetrics; + font.family: "FiraSans Regular"; + font.pixelSize: hifi.fontSizes.textFieldInput; + font.capitalization: Font.AllUppercase; + text: root.primaryFilter_displayName; + } + + // workaround for https://bugreports.qt.io/browse/QTBUG-49297 + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Return: + case Qt.Key_Enter: + event.accepted = true; + + // emit accepted signal manually + if (acceptableInput) { + root.accepted(); + root.forceActiveFocus(); + } + break; + case Qt.Key_Backspace: + if (textField.text === "") { + primaryFilter_index = -1; + } + break; + } + } + + onAccepted: { + root.forceActiveFocus(); + } + + onActiveFocusChanged: { + if (!activeFocus) { + dropdownContainer.visible = false; + } + } + + color: { + if (isLightColorScheme) { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.lightGray + } + } else if (isFaintGrayColorScheme) { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.lightGray + } + } else { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.lightGrayText + } + } + } + + background: Rectangle { + id: mainFilterBarRectangle; + + color: { + if (isLightColorScheme) { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.textFieldLightBackground + } + } else if (isFaintGrayColorScheme) { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.faintGray50 + } + } else { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.baseGrayShadow + } + } + } + + border.color: textField.error ? hifi.colors.redHighlight : + (textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray)) + border.width: 1 + radius: 4 + + Item { + id: searchButtonContainer; + anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; + height: parent.height; + width: 42; + + // Search icon + HiFiGlyphs { + id: searchIcon; + text: hifi.glyphs.search + color: textField.color + size: 40; + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + width: paintedWidth; + } + + // Carat + HiFiGlyphs { + text: hifi.glyphs.caratDn; + color: textField.color; + size: 40; + anchors.left: parent.left; + anchors.leftMargin: 15; + width: paintedWidth; + } + + MouseArea { + anchors.fill: parent; + onClicked: { + textField.forceActiveFocus(); + dropdownContainer.visible = !dropdownContainer.visible; + } + } + } + + Rectangle { + z: 999; + id: primaryFilterContainer; + color: textField.activeFocus ? hifi.colors.faintGray : hifi.colors.white; + width: primaryFilterTextMetrics.tightBoundingRect.width + 14; + height: parent.height - 8; + anchors.verticalCenter: parent.verticalCenter; + anchors.left: searchButtonContainer.right; + anchors.leftMargin: 4; + visible: primaryFilterText.text !== ""; + radius: height/2; + + FiraSansRegular { + id: primaryFilterText; + text: root.primaryFilter_displayName; + anchors.fill: parent; + color: textField.activeFocus ? hifi.colors.black : hifi.colors.lightGray; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + size: hifi.fontSizes.textFieldInput; + font.capitalization: Font.AllUppercase; + } + + MouseArea { + anchors.fill: parent; + onClicked: { + textField.forceActiveFocus(); + } + } + } + + // "Clear" button + HiFiGlyphs { + text: hifi.glyphs.error + color: textField.color + size: 40 + anchors.right: parent.right + anchors.rightMargin: hifi.dimensions.textPadding - 2 + anchors.verticalCenter: parent.verticalCenter + visible: root.text !== "" || root.primaryFilter_index !== -1; + + MouseArea { + anchors.fill: parent; + onClicked: { + root.text = ""; + root.primaryFilter_index = -1; + dropdownContainer.visible = false; + textField.forceActiveFocus(); + } + } + } + } + + selectedTextColor: hifi.colors.black + selectionColor: hifi.colors.primaryHighlight + leftPadding: 44 + (root.primaryFilter_index === -1 ? 0 : primaryFilterTextMetrics.tightBoundingRect.width + 20); + rightPadding: 44; + } + + Rectangle { + id: dropdownContainer; + visible: false; + height: 50 * filterBarModel.count; + width: parent.width; + anchors.top: textField.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + color: hifi.colors.white; + + ListModel { + id: filterBarModel; + } + + ListView { + id: dropdownListView; + interactive: false; + anchors.fill: parent; + model: filterBarModel; + delegate: Rectangle { + id: dropDownButton; + color: hifi.colors.white; + width: parent.width; + height: 50; + + RalewaySemiBold { + id: dropDownButtonText; + text: model.displayName; + anchors.fill: parent; + anchors.leftMargin: 12; + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + size: 18; + } + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + propagateComposedEvents: false; + onEntered: { + dropDownButton.color = hifi.colors.blueHighlight; + } + onExited: { + dropDownButton.color = hifi.colors.white; + } + onClicked: { + textField.forceActiveFocus(); + root.primaryFilter_index = index; + dropdownContainer.visible = false; + } + } + } + } + } + + DropShadow { + anchors.fill: dropdownContainer; + horizontalOffset: 0; + verticalOffset: 4; + radius: 4.0; + samples: 9 + color: Qt.rgba(0, 0, 0, 0.25); + source: dropdownContainer; + visible: dropdownContainer.visible; + } + + function changeFilterByDisplayName(name) { + for (var i = 0; i < filterBarModel.count; i++) { + if (filterBarModel.get(i).displayName === name) { + root.primaryFilter_index = i; + return; + } + } + + console.log("Passed displayName not found in filterBarModel! primaryFilter unchanged."); + } +} diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index 782ab454b5..f94541897b 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -163,8 +163,10 @@ TextField { text: textField.label colorScheme: textField.colorScheme anchors.left: parent.left + anchors.right: parent.right anchors.bottom: parent.top anchors.bottomMargin: 3 + wrapMode: Text.WordWrap visible: label != "" } } diff --git a/interface/resources/qml/dialogs/TabletQueryDialog.qml b/interface/resources/qml/dialogs/TabletQueryDialog.qml index e21677c12c..60dbc106dc 100644 --- a/interface/resources/qml/dialogs/TabletQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletQueryDialog.qml @@ -65,34 +65,33 @@ TabletModalWindow { id: modalWindowItem width: parent.width - 12 height: 240 - anchors { - verticalCenter: parent.verticalCenter - horizontalCenter: parent.horizontalCenter - } + anchors.horizontalCenter: parent.horizontalCenter QtObject { id: d - readonly property int minWidth: 470 - readonly property int maxWidth: 470 + readonly property int minWidth: modalWindowItem.width + readonly property int maxWidth: modalWindowItem.width readonly property int minHeight: 120 readonly property int maxHeight: 720 function resize() { - var targetWidth = Math.max(titleWidth, 470) + var targetWidth = Math.max(titleWidth, modalWindowItem.width) var targetHeight = (items ? comboBox.controlHeight : textResult.controlHeight) + 5 * hifi.dimensions.contentSpacing.y + buttons.height modalWindowItem.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth); - modalWindowItem.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0) + modalWindowItem.frameMarginTop + modalWindowItem.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + modalWindowItem.frameMarginTop + modalWindowItem.y = (root.height - (modalWindowItem.height + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0))) / 2 } } Item { anchors { top: parent.top - bottom: keyboard.top; + bottom: buttons.top; left: parent.left; right: parent.right; margins: 0 bottomMargin: 2 * hifi.dimensions.contentSpacing.y + topMargin: modalWindowItem.frameMarginTop } // FIXME make a text field type that can be bound to a history for autocompletion @@ -106,6 +105,7 @@ TabletModalWindow { right: parent.right; bottom: parent.bottom leftMargin: 5 + rightMargin: 5 } } @@ -124,22 +124,6 @@ TabletModalWindow { } } - property alias keyboardOverride: root.keyboardOverride - property alias keyboardRaised: root.keyboardRaised - property alias punctuationMode: root.punctuationMode - - Keyboard { - id: keyboard - raised: keyboardEnabled && keyboardRaised - numeric: punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: buttons.top - bottomMargin: raised ? 2 * hifi.dimensions.contentSpacing.y : 0 - } - } - Flow { id: buttons focus: true @@ -150,6 +134,7 @@ TabletModalWindow { bottom: parent.bottom right: parent.right margins: 0 + rightMargin: hifi.dimensions.borderRadius bottomMargin: hifi.dimensions.contentSpacing.y } Button { action: cancelAction } @@ -177,7 +162,17 @@ TabletModalWindow { } } - Keys.onPressed: { + Keyboard { + id: keyboard + raised: keyboardEnabled && keyboardRaised + numeric: punctuationMode + anchors { + left: parent.left + right: parent.right + top: modalWindowItem.bottom + } + } + Keys.onPressed: { if (!visible) { return } diff --git a/interface/resources/qml/hifi/+android/StatsBar.qml b/interface/resources/qml/hifi/+android/StatsBar.qml new file mode 100644 index 0000000000..aee438b44f --- /dev/null +++ b/interface/resources/qml/hifi/+android/StatsBar.qml @@ -0,0 +1,71 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 +import Qt.labs.settings 1.0 +import "../../styles-uit" +import "../../controls-uit" as HifiControlsUit +import "../../controls" as HifiControls +import ".." + +Item { + id: bar + x:300 + y:0 + width: 300 + height: 300 + z: -1 + + signal sendToScript(var message); + signal windowClosed(); + + property bool shown: true + + onShownChanged: { + bar.visible = shown; + } + + Rectangle { + anchors.fill : parent + color: "transparent" + Flow { + id: flowMain + spacing: 10 + flow: Flow.TopToBottom + layoutDirection: Flow.TopToBottom + anchors.fill: parent + anchors.margins: 4 + } + } + + Component.onCompleted: { + // put on bottom + x = 300; + y = 0; + width = 300; + height = 300; + } + + function addButton(properties) { + var component = Qt.createComponent("button.qml"); + if (component.status == Component.Ready) { + var button = component.createObject(flowMain); + // copy all properites to button + var keys = Object.keys(properties).forEach(function (key) { + button[key] = properties[key]; + }); + return button; + } else if( component.status == Component.Error) { + console.log("Load button errors " + component.errorString()); + } + } + + function urlHelper(src) { + if (src.match(/\bhttp/)) { + return src; + } else { + return "../../../" + src; + } + } + +} diff --git a/interface/resources/qml/hifi/+android/bottombar.qml b/interface/resources/qml/hifi/+android/bottombar.qml index e31dc9453f..66117d0389 100644 --- a/interface/resources/qml/hifi/+android/bottombar.qml +++ b/interface/resources/qml/hifi/+android/bottombar.qml @@ -25,6 +25,7 @@ import "." Item { id: bar x:0 + height: 255 property bool shown: true @@ -45,10 +46,10 @@ Item { anchors.fill: parent } - Rectangle { + Rectangle { id: background anchors.fill : parent - color: "#FF000000" + color: "#FF000000" border.color: "#FFFFFF" anchors.bottomMargin: -1 anchors.leftMargin: -1 @@ -104,13 +105,25 @@ Item { } } } - } + } + + function relocateAndResize(newWindowWidth, newWindowHeight) { + width = newWindowWidth; + y = newWindowHeight - height; + } + + function onWindowGeometryChanged(rect) { + relocateAndResize(rect.width, rect.height); + } Component.onCompleted: { // put on bottom - width = Window.innerWidth; - height = 255; - y = Window.innerHeight - height; + relocateAndResize(Window.innerWidth, Window.innerHeight); + Window.geometryChanged.connect(onWindowGeometryChanged); // In devices with bars appearing at startup we should listen for this + } + + Component.onDestruction: { + Window.geometryChanged.disconnect(onWindowGeometryChanged); } function addButton(properties) { diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 96ffa390bf..9933953fe8 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -30,25 +30,31 @@ Rectangle { property string activeView: "initialize"; property bool ownershipStatusReceived: false; property bool balanceReceived: false; + property bool availableUpdatesReceived: false; + property string baseItemName: ""; property string itemName; property string itemId; property string itemHref; property string itemAuthor; + property int itemEdition: -1; + property string certificateId; property double balanceAfterPurchase; property bool alreadyOwned: false; property int itemPrice: -1; property bool isCertified; property string itemType; - property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar"]; - property var itemTypesText: ["entity", "wearable", "content set", "app", "avatar"]; - property var buttonTextNormal: ["REZ", "WEAR", "REPLACE CONTENT SET", "INSTALL", "WEAR"]; - property var buttonTextClicked: ["REZZED!", "WORN!", "CONTENT SET REPLACED!", "INSTALLED!", "AVATAR CHANGED!"] - property var buttonGlyph: [hifi.glyphs.wand, hifi.glyphs.hat, hifi.glyphs.globe, hifi.glyphs.install, hifi.glyphs.avatar]; + property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar", "unknown"]; + property var itemTypesText: ["entity", "wearable", "content set", "app", "avatar", "item"]; + property var buttonTextNormal: ["REZ", "WEAR", "REPLACE CONTENT SET", "INSTALL", "WEAR", "REZ"]; + property var buttonTextClicked: ["REZZED!", "WORN!", "CONTENT SET REPLACED!", "INSTALLED!", "AVATAR CHANGED!", "REZZED!"] + property var buttonGlyph: [hifi.glyphs.wand, hifi.glyphs.hat, hifi.glyphs.globe, hifi.glyphs.install, hifi.glyphs.avatar, hifi.glyphs.wand]; property bool shouldBuyWithControlledFailure: false; property bool debugCheckoutSuccess: false; property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); property string referrer; property bool isInstalled; + property bool isUpdating; + property string baseAppURL; // Style color: hifi.colors.white; Connections { @@ -103,8 +109,8 @@ Rectangle { if (result.status !== 'success') { console.log("Failed to get balance", result.data.message); } else { - root.balanceReceived = true; root.balanceAfterPurchase = result.data.balance - root.itemPrice; + root.balanceReceived = true; root.refreshBuyUI(); } } @@ -113,13 +119,13 @@ Rectangle { if (result.status !== 'success') { console.log("Failed to get Already Owned status", result.data.message); } else { - root.ownershipStatusReceived = true; if (result.data.marketplace_item_id === root.itemId) { root.alreadyOwned = result.data.already_owned; } else { console.log("WARNING - Received 'Already Owned' status about different Marketplace ID!"); root.alreadyOwned = false; } + root.ownershipStatusReceived = true; root.refreshBuyUI(); } } @@ -129,11 +135,53 @@ Rectangle { root.isInstalled = true; } } + + onAvailableUpdatesResult: { + if (result.status !== 'success') { + console.log("Failed to get Available Updates", result.data.message); + } else { + for (var i = 0; i < result.data.updates.length; i++) { + // If the ItemID of the item we're looking at matches EITHER the ID of a "base" item + // OR the ID of an "updated" item, we're updating. + if (root.itemId === result.data.updates[i].item_id || + root.itemId === result.data.updates[i].updated_item_id) { + if (root.itemEdition !== -1 && root.itemEdition !== parseInt(result.data.updates[i].edition_number)) { + continue; + } + root.isUpdating = true; + root.baseItemName = result.data.updates[i].base_item_title; + // This CertID is the one corresponding to the base item CertID that the user already owns + root.certificateId = result.data.updates[i].certificate_id; + if (root.itemType === "app") { + root.baseAppURL = result.data.updates[i].item_download_url; + } + break; + } + } + root.availableUpdatesReceived = true; + refreshBuyUI(); + } + } + + onUpdateItemResult: { + if (result.status !== 'success') { + failureErrorText.text = result.message; + root.activeView = "checkoutFailure"; + } else { + root.itemHref = result.data.download_url; + if (result.data.categories.indexOf("Wearables") > -1) { + root.itemType = "wearable"; + } + root.activeView = "checkoutSuccess"; + } + } } onItemIdChanged: { root.ownershipStatusReceived = false; Commerce.alreadyOwned(root.itemId); + root.availableUpdatesReceived = false; + Commerce.getAvailableUpdates(root.itemId); itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg"; } @@ -161,6 +209,7 @@ Rectangle { } onItemPriceChanged: { + root.balanceReceived = false; Commerce.balance(); } @@ -240,6 +289,7 @@ Rectangle { Component.onCompleted: { ownershipStatusReceived = false; balanceReceived = false; + availableUpdatesReceived = false; Commerce.getWalletStatus(); } } @@ -316,7 +366,7 @@ Rectangle { Rectangle { id: loading; z: 997; - visible: !root.ownershipStatusReceived || !root.balanceReceived; + visible: !root.ownershipStatusReceived || !root.balanceReceived || !root.availableUpdatesReceived; anchors.fill: parent; color: hifi.colors.white; @@ -412,6 +462,7 @@ Rectangle { // "HFC" balance label HiFiGlyphs { id: itemPriceTextLabel; + visible: !(root.isUpdating && root.itemEdition > 0); text: hifi.glyphs.hfc; // Size size: 30; @@ -427,9 +478,9 @@ Rectangle { } FiraSansSemiBold { id: itemPriceText; - text: (root.itemPrice === -1) ? "--" : root.itemPrice; + text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : root.itemPrice); // Text size - size: 26; + size: (root.isUpdating && root.itemEdition > 0) ? 20 : 26; // Anchors anchors.top: parent.top; anchors.right: parent.right; @@ -529,9 +580,13 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: "VIEW THIS ITEM IN MY PURCHASES"; + text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN MY PURCHASES"; onClicked: { - sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName}); + if (root.isUpdating) { + sendToScript({method: 'checkout_goToPurchases', filterText: root.baseItemName}); + } else { + sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName}); + } } } @@ -539,7 +594,7 @@ Rectangle { HifiControlsUit.Button { id: buyButton; visible: !((root.itemType === "avatar" || root.itemType === "app") && viewInMyPurchasesButton.visible) - enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived) || (!root.isCertified); + enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived && availableUpdatesReceived) || (!root.isCertified) || root.isUpdating; color: viewInMyPurchasesButton.visible ? hifi.buttons.white : hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; anchors.top: viewInMyPurchasesButton.visible ? viewInMyPurchasesButton.bottom : @@ -548,10 +603,19 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: ((root.isCertified) ? ((ownershipStatusReceived && balanceReceived) ? - (viewInMyPurchasesButton.visible ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item"); + text: (root.isUpdating && root.itemEdition > 0) ? "CONFIRM UPDATE" : (((root.isCertified) ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ? + ((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item")); onClicked: { - if (root.isCertified) { + if (root.isUpdating && root.itemEdition > 0) { + // If we're updating an app, the existing app needs to be uninstalled. + // This call will fail/return `false` if the app isn't installed, but that's OK. + if (root.itemType === "app") { + Commerce.uninstallApp(root.baseAppURL); + } + buyButton.enabled = false; + loading.visible = true; + Commerce.updateItem(root.certificateId); + } else if (root.isCertified) { if (!root.shouldBuyWithControlledFailure) { if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { lightboxPopup.titleText = "Purchase Content Set"; @@ -975,7 +1039,7 @@ Rectangle { buyButton.color = hifi.buttons.red; root.shouldBuyWithControlledFailure = true; } else { - buyButton.text = (root.isCertified ? ((ownershipStatusReceived && balanceReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); + buyButton.text = (root.isCertified ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); buyButton.color = hifi.buttons.blue; root.shouldBuyWithControlledFailure = false; } @@ -1001,12 +1065,13 @@ Rectangle { function fromScript(message) { switch (message.method) { case 'updateCheckoutQML': - itemId = message.params.itemId; - itemName = message.params.itemName; + root.itemId = message.params.itemId; + root.itemName = message.params.itemName.trim(); root.itemPrice = message.params.itemPrice; - itemHref = message.params.itemHref; - referrer = message.params.referrer; - itemAuthor = message.params.itemAuthor; + root.itemHref = message.params.itemHref; + root.referrer = message.params.referrer; + root.itemAuthor = message.params.itemAuthor; + root.itemEdition = message.params.itemEdition || -1; refreshBuyUI(); break; default: @@ -1015,35 +1080,70 @@ Rectangle { } signal sendToScript(var message); + function canBuyAgain() { + return (root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet" || root.itemType === "unknown"); + } + + function handleContentSets() { + if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { + buyText.text = "The domain owner must enable 'Replace Content' permissions for you in this " + + "domain's server settings before you can replace this domain's content with " + root.itemName + ""; + buyTextContainer.color = "#FFC3CD"; + buyTextContainer.border.color = "#F3808F"; + buyGlyph.text = hifi.glyphs.alert; + buyGlyph.size = 54; + } + } + + function handleBuyAgainLogic() { + // If you can buy this item again... + if (canBuyAgain()) { + // If you can't afford another copy of the item... + if (root.balanceAfterPurchase < 0) { + // If you already own the item... + if (root.alreadyOwned) { + buyText.text = "Your Wallet does not have sufficient funds to purchase this item again."; + // Else if you don't already own the item... + } else { + buyText.text = "Your Wallet does not have sufficient funds to purchase this item."; + } + buyTextContainer.color = "#FFC3CD"; + buyTextContainer.border.color = "#F3808F"; + buyGlyph.text = hifi.glyphs.alert; + buyGlyph.size = 54; + // If you CAN afford another copy of the item... + } else { + handleContentSets(); + } + } + } + function refreshBuyUI() { if (root.isCertified) { - if (root.ownershipStatusReceived && root.balanceReceived) { - if (root.balanceAfterPurchase < 0) { - if (root.alreadyOwned) { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item again."; - viewInMyPurchasesButton.visible = true; + if (root.ownershipStatusReceived && root.balanceReceived && root.availableUpdatesReceived) { + buyText.text = ""; + + // If the user IS on the checkout page for the updated version of an owned item... + if (root.isUpdating) { + // If the user HAS already selected a specific edition to update... + if (root.itemEdition > 0) { + buyText.text = "By pressing \"Confirm Update\", you agree to trade in your old item for the updated item that replaces it."; + buyTextContainer.color = "#FFFFFF"; + buyTextContainer.border.color = "#FFFFFF"; + // Else if the user HAS NOT selected a specific edition to update... } else { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item."; - } - buyTextContainer.color = "#FFC3CD"; - buyTextContainer.border.color = "#F3808F"; - buyGlyph.text = hifi.glyphs.alert; - buyGlyph.size = 54; + viewInMyPurchasesButton.visible = true; + + handleBuyAgainLogic(); + } + // If the user IS NOT on the checkout page for the updated verison of an owned item... + // (i.e. they are checking out an item "normally") } else { if (root.alreadyOwned) { viewInMyPurchasesButton.visible = true; - } else { - buyText.text = ""; - } - - if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { - buyText.text = "The domain owner must enable 'Replace Content' permissions for you in this " + - "domain's server settings before you can replace this domain's content with " + root.itemName + ""; - buyTextContainer.color = "#FFC3CD"; - buyTextContainer.border.color = "#F3808F"; - buyGlyph.text = hifi.glyphs.alert; - buyGlyph.size = 54; } + + handleBuyAgainLogic(); } } else { buyText.text = ""; @@ -1062,11 +1162,13 @@ Rectangle { root.activeView = "checkoutMain"; } else { root.activeView = "checkoutSuccess"; + root.ownershipStatusReceived = false; + Commerce.alreadyOwned(root.itemId); + root.availableUpdatesReceived = false; + Commerce.getAvailableUpdates(root.itemId); + root.balanceReceived = false; + Commerce.balance(); } - root.balanceReceived = false; - root.ownershipStatusReceived = false; - Commerce.alreadyOwned(root.itemId); - Commerce.balance(); } // diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml index 8a7e809b3d..8105688131 100644 --- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml +++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml @@ -28,6 +28,7 @@ Item { property string referrerURL: (Account.metaverseServerURL + "/marketplace?"); readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin; property alias usernameDropdownVisible: usernameDropdown.visible; + property bool messagesWaiting: false; height: mainContainer.height + additionalDropdownHeight; @@ -38,6 +39,7 @@ Item { if (walletStatus === 0) { sendToParent({method: "needsLogIn"}); } else if (walletStatus === 5) { + Commerce.getAvailableUpdates(); Commerce.getSecurityImage(); } else if (walletStatus > 5) { console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus); @@ -58,6 +60,14 @@ Item { securityImage.source = "image://security/securityImage"; } } + + onAvailableUpdatesResult: { + if (result.status !== 'success') { + console.log("Failed to get Available Updates", result.data.message); + } else { + root.messagesWaiting = result.data.updates.length > 0; + } + } } Component.onCompleted: { @@ -134,13 +144,25 @@ Item { anchors.fill: parent; hoverEnabled: enabled; onClicked: { - sendToParent({method: 'header_goToPurchases'}); + sendToParent({ method: 'header_goToPurchases', hasUpdates: root.messagesWaiting }); } onEntered: myPurchasesText.color = hifi.colors.blueHighlight; onExited: myPurchasesText.color = hifi.colors.blueAccent; } } + Rectangle { + id: messagesWaitingLight; + visible: root.messagesWaiting; + anchors.right: myPurchasesLink.left; + anchors.rightMargin: -2; + anchors.verticalCenter: parent.verticalCenter; + height: 10; + width: height; + radius: height/2; + color: "red"; + } + TextMetrics { id: textMetrics; font.family: "Raleway" diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index fb8e509cde..4cfa61c9ed 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -48,11 +48,14 @@ Item { property bool hasPermissionToRezThis; property bool permissionExplanationCardVisible; property bool isInstalled; + property string upgradeUrl; + property string upgradeTitle; + property bool isShowingMyItems; property string originalStatusText; property string originalStatusColor; - height: 110; + height: (root.upgradeUrl === "" || root.isShowingMyItems) ? 110 : 150; width: parent.width; Connections { @@ -137,6 +140,14 @@ Item { anchors.verticalCenter: parent.verticalCenter; height: root.height - 10; + // START "incorrect indentation to prevent insane diffs" + Item { + id: itemContainer; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: parent.top; + height: 100; + Image { id: itemPreviewImage; source: root.itemPreviewImageUrl; @@ -357,7 +368,7 @@ Item { Item { id: statusContainer; - visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.purchaseStatusChanged; + visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.purchaseStatusChanged || root.numberSold > -1; anchors.left: itemName.left; anchors.top: certificateContainer.bottom; anchors.topMargin: 8; @@ -376,7 +387,7 @@ Item { "PENDING..." } else if (root.purchaseStatus === "invalidated") { "INVALIDATED" - } else if (root.numberSold !== -1) { + } else if (root.numberSold > -1) { ("Sales: " + root.numberSold + "/" + (root.limitedRun === -1 ? "\u221e" : root.limitedRun)) } else { "" @@ -634,6 +645,48 @@ Item { } } } + } + // END "incorrect indentation to prevent insane diffs" + + Rectangle { + id: upgradeAvailableContainer; + visible: root.upgradeUrl !== "" && !root.isShowingMyItems; + anchors.top: itemContainer.bottom; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + color: "#B5EAFF"; + + RalewayRegular { + id: updateAvailableText; + text: "UPDATE AVAILABLE"; + size: 13; + anchors.left: parent.left; + anchors.leftMargin: 12; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + width: paintedWidth; + color: hifi.colors.black; + verticalAlignment: Text.AlignVCenter; + } + + RalewaySemiBold { + id: updateNowText; + text: "Update this item now"; + size: 13; + anchors.left: updateAvailableText.right; + anchors.leftMargin: 16; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + width: paintedWidth; + color: hifi.colors.black; + verticalAlignment: Text.AlignVCenter; + + onLinkActivated: { + sendToPurchases({method: 'updateItemClicked', itemId: root.itemId, itemEdition: root.itemEdition, upgradeUrl: root.upgradeUrl}); + } + } + } } DropShadow { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index c505baebf4..726e6bd338 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -37,6 +37,8 @@ Rectangle { property bool isDebuggingFirstUseTutorial: false; property int pendingItemCount: 0; property string installedApps; + property bool keyboardRaised: false; + property int numUpdatesAvailable: 0; // Style color: hifi.colors.white; Connections { @@ -64,6 +66,7 @@ Rectangle { root.activeView = "purchasesMain"; root.installedApps = Commerce.getInstalledApps(); Commerce.inventory(); + Commerce.getAvailableUpdates(); } } else { console.log("ERROR in Purchases.qml: Unknown wallet status: " + walletStatus); @@ -119,6 +122,15 @@ Rectangle { root.pendingInventoryReply = false; } + + onAvailableUpdatesResult: { + if (result.status !== 'success') { + console.log("Failed to get Available Updates", result.data.message); + } else { + sendToScript({method: 'purchases_availableUpdatesReceived', numUpdates: result.data.updates.length }); + root.numUpdatesAvailable = result.data.updates.length; + } + } } Timer { @@ -273,6 +285,7 @@ Rectangle { root.activeView = "purchasesMain"; root.installedApps = Commerce.getInstalledApps(); Commerce.inventory(); + Commerce.getAvailableUpdates(); break; } } @@ -296,6 +309,7 @@ Rectangle { // FILTER BAR START // Item { + z: 997; id: filterBarContainer; // Size height: 40; @@ -321,28 +335,61 @@ Rectangle { size: 22; } - HifiControlsUit.TextField { + HifiControlsUit.FilterBar { id: filterBar; property string previousText: ""; + property string previousPrimaryFilter: ""; colorScheme: hifi.colorSchemes.faintGray; - hasClearButton: true; - hasRoundedBorder: true; + anchors.top: parent.top; + anchors.right: parent.right; anchors.left: myText.right; anchors.leftMargin: 16; - height: 39; - anchors.verticalCenter: parent.verticalCenter; - anchors.right: parent.right; + textFieldHeight: 39; + height: textFieldHeight + dropdownHeight; placeholderText: "filter items"; + Component.onCompleted: { + var choices = [ + { + "displayName": "App", + "filterName": "app" + }, + { + "displayName": "Avatar", + "filterName": "avatar" + }, + { + "displayName": "Content Set", + "filterName": "contentSet" + }, + { + "displayName": "Entity", + "filterName": "entity" + }, + { + "displayName": "Wearable", + "filterName": "wearable" + }, + { + "displayName": "Updatable", + "filterName": "updatable" + } + ] + filterBar.primaryFilterChoices.clear(); + filterBar.primaryFilterChoices.append(choices); + } + + onPrimaryFilter_displayNameChanged: { + buildFilteredPurchasesModel(); + purchasesContentsList.positionViewAtIndex(0, ListView.Beginning) + filterBar.previousPrimaryFilter = filterBar.primaryFilter_displayName; + } + onTextChanged: { buildFilteredPurchasesModel(); purchasesContentsList.positionViewAtIndex(0, ListView.Beginning) filterBar.previousText = filterBar.text; } - - onAccepted: { - focus = false; - } } } // @@ -350,6 +397,7 @@ Rectangle { // HifiControlsUit.Separator { + z: 996; id: separator; colorScheme: 2; anchors.left: parent.left; @@ -377,12 +425,11 @@ Rectangle { clip: true; model: filteredPurchasesModel; snapMode: ListView.SnapToItem; - highlightRangeMode: ListView.StrictlyEnforceRange; // Anchors anchors.top: separator.bottom; anchors.topMargin: 12; anchors.left: parent.left; - anchors.bottom: parent.bottom; + anchors.bottom: updatesAvailableBanner.visible ? updatesAvailableBanner.top : parent.bottom; width: parent.width; delegate: PurchasedItem { itemName: title; @@ -398,21 +445,10 @@ Rectangle { displayedItemCount: model.displayedItemCount; permissionExplanationCardVisible: model.permissionExplanationCardVisible; isInstalled: model.isInstalled; - itemType: { - if (model.root_file_url.indexOf(".fst") > -1) { - "avatar"; - } else if (model.categories.indexOf("Wearables") > -1) { - "wearable"; - } else if (model.root_file_url.endsWith('.json.gz')) { - "contentSet"; - } else if (model.root_file_url.endsWith('.app.json')) { - "app"; - } else if (model.root_file_url.endsWith('.json')) { - "entity"; - } else { - "unknown"; - } - } + upgradeUrl: model.upgrade_url; + upgradeTitle: model.upgrade_title; + itemType: model.itemType; + isShowingMyItems: root.isShowingMyItems; anchors.topMargin: 10; anchors.bottomMargin: 10; @@ -485,15 +521,80 @@ Rectangle { filteredPurchasesModel.setProperty(i, "permissionExplanationCardVisible", true); } } + } else if (msg.method === "updateItemClicked") { + sendToScript(msg); } } } } } + Rectangle { + id: updatesAvailableBanner; + visible: root.numUpdatesAvailable > 0 && !root.isShowingMyItems; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 75; + color: "#B5EAFF"; + + Rectangle { + id: updatesAvailableGlyph; + anchors.verticalCenter: parent.verticalCenter; + anchors.left: parent.left; + anchors.leftMargin: 16; + // Size + width: 10; + height: width; + radius: width/2; + // Style + color: "red"; + } + + RalewaySemiBold { + text: "You have " + root.numUpdatesAvailable + " item updates available."; + // Text size + size: 18; + // Anchors + anchors.left: updatesAvailableGlyph.right; + anchors.leftMargin: 12; + height: parent.height; + width: paintedWidth; + // Style + color: hifi.colors.black; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + propagateComposedEvents: false; + } + + HifiControlsUit.Button { + color: hifi.buttons.white; + colorScheme: hifi.colorSchemes.dark; + anchors.verticalCenter: parent.verticalCenter; + anchors.right: parent.right; + anchors.rightMargin: 12; + width: 100; + height: 40; + text: "SHOW ME"; + onClicked: { + filterBar.text = ""; + filterBar.changeFilterByDisplayName("Updatable"); + } + } + } + Item { id: noItemsAlertContainer; - visible: !purchasesContentsList.visible && root.purchasesReceived && root.isShowingMyItems && filterBar.text === ""; + visible: !purchasesContentsList.visible && + root.purchasesReceived && + root.isShowingMyItems && + filterBar.text === "" && + filterBar.primaryFilter_displayName === ""; anchors.top: filterBarContainer.bottom; anchors.topMargin: 12; anchors.left: parent.left; @@ -539,7 +640,11 @@ Rectangle { Item { id: noPurchasesAlertContainer; - visible: !purchasesContentsList.visible && root.purchasesReceived && !root.isShowingMyItems && filterBar.text === ""; + visible: !purchasesContentsList.visible && + root.purchasesReceived && + !root.isShowingMyItems && + filterBar.text === "" && + filterBar.primaryFilter_displayName === ""; anchors.top: filterBarContainer.bottom; anchors.topMargin: 12; anchors.left: parent.left; @@ -589,7 +694,7 @@ Rectangle { HifiControlsUit.Keyboard { id: keyboard; - raised: HMD.mounted && filterBar.focus; + raised: HMD.mounted && parent.keyboardRaised; numeric: parent.punctuationMode; anchors { bottom: parent.bottom; @@ -613,6 +718,7 @@ Rectangle { console.log("Refreshing Purchases..."); root.pendingInventoryReply = true; Commerce.inventory(); + Commerce.getAvailableUpdates(); } } } @@ -660,8 +766,13 @@ Rectangle { var sameItemCount = 0; tempPurchasesModel.clear(); + for (var i = 0; i < purchasesModel.count; i++) { if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) { + if (!purchasesModel.get(i).valid) { + continue; + } + if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) { tempPurchasesModel.insert(0, purchasesModel.get(i)); } else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === "0") || @@ -671,6 +782,35 @@ Rectangle { } } + // primaryFilter filtering and adding of itemType property to model + var currentItemType, currentRootFileUrl, currentCategories; + for (var i = 0; i < tempPurchasesModel.count; i++) { + currentRootFileUrl = tempPurchasesModel.get(i).root_file_url; + currentCategories = tempPurchasesModel.get(i).categories; + + if (currentRootFileUrl.indexOf(".fst") > -1) { + currentItemType = "avatar"; + } else if (currentCategories.indexOf("Wearables") > -1) { + currentItemType = "wearable"; + } else if (currentRootFileUrl.endsWith('.json.gz')) { + currentItemType = "contentSet"; + } else if (currentRootFileUrl.endsWith('.app.json')) { + currentItemType = "app"; + } else if (currentRootFileUrl.endsWith('.json')) { + currentItemType = "entity"; + } else { + currentItemType = "unknown"; + } + if (filterBar.primaryFilter_displayName !== "" && + ((filterBar.primaryFilter_displayName === "Updatable" && tempPurchasesModel.get(i).upgrade_url === "") || + (filterBar.primaryFilter_displayName !== "Updatable" && filterBar.primaryFilter_filterName.toLowerCase() !== currentItemType.toLowerCase()))) { + tempPurchasesModel.remove(i); + i--; + } else { + tempPurchasesModel.setProperty(i, 'itemType', currentItemType); + } + } + for (var i = 0; i < tempPurchasesModel.count; i++) { if (!filteredPurchasesModel.get(i)) { sameItemCount = -1; @@ -682,12 +822,17 @@ Rectangle { } } - if (sameItemCount !== tempPurchasesModel.count || filterBar.text !== filterBar.previousText) { + if (sameItemCount !== tempPurchasesModel.count || + filterBar.text !== filterBar.previousText || + filterBar.primaryFilter !== filterBar.previousPrimaryFilter) { filteredPurchasesModel.clear(); var currentId; for (var i = 0; i < tempPurchasesModel.count; i++) { currentId = tempPurchasesModel.get(i).id; - + + if (!purchasesModel.get(i).valid) { + continue; + } filteredPurchasesModel.append(tempPurchasesModel.get(i)); filteredPurchasesModel.setProperty(i, 'permissionExplanationCardVisible', false); filteredPurchasesModel.setProperty(i, 'isInstalled', ((root.installedApps).indexOf(currentId) > -1)); @@ -736,7 +881,7 @@ Rectangle { function fromScript(message) { switch (message.method) { case 'updatePurchases': - referrerURL = message.referrerURL; + referrerURL = message.referrerURL || ""; titleBarContainer.referrerURL = message.referrerURL; filterBar.text = message.filterText ? message.filterText : ""; break; diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 27660b5e9e..7a14ee060f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -39,6 +39,7 @@ Item { root.noMoreHistoryData = false; root.historyRequestPending = true; Commerce.history(root.currentHistoryPage); + Commerce.getAvailableUpdates(); } else { refreshTimer.stop(); } @@ -133,6 +134,14 @@ Item { refreshTimer.start(); } } + + onAvailableUpdatesResult: { + if (result.status !== 'success') { + console.log("Failed to get Available Updates", result.data.message); + } else { + sendToScript({method: 'wallet_availableUpdatesReceived', numUpdates: result.data.updates.length }); + } + } } Connections { diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index afa210ea8b..9705d84b7b 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -11,44 +11,34 @@ import QtQuick 2.5 import QtQuick.Window 2.2 -Item { - readonly property alias colors: colors - readonly property alias colorSchemes: colorSchemes - readonly property alias dimensions: dimensions - readonly property alias fontSizes: fontSizes - readonly property alias glyphs: glyphs - readonly property alias icons: icons - readonly property alias buttons: buttons - readonly property alias effects: effects +QtObject { function glyphForIcon(icon) { // Translates icon enum to glyph char. var glyph; switch (icon) { - case hifi.icons.information: - glyph = hifi.glyphs.info; + case icons.information: + glyph = glyphs.info; break; - case hifi.icons.question: - glyph = hifi.glyphs.question; + case icons.question: + glyph = glyphs.question; break; - case hifi.icons.warning: - glyph = hifi.glyphs.alert; + case icons.warning: + glyph = glyphs.alert; break; - case hifi.icons.critical: - glyph = hifi.glyphs.error; + case icons.critical: + glyph = glyphs.error; break; - case hifi.icons.placemark: - glyph = hifi.glyphs.placemark; + case icons.placemark: + glyph = glyphs.placemark; break; default: - glyph = hifi.glyphs.noIcon; + glyph = glyphs.noIcon; } return glyph; } - Item { - id: colors - + readonly property QtObject colors: QtObject { // Base colors readonly property color baseGray: "#393939" readonly property color darkGray: "#121212" @@ -134,15 +124,13 @@ Item { readonly property color tabBackgroundLight: "#d4d4d4" } - Item { - id: colorSchemes + readonly property QtObject colorSchemes: QtObject { readonly property int light: 0 readonly property int dark: 1 readonly property int faintGray: 2 } - Item { - id: dimensions + readonly property QtObject dimensions: QtObject { readonly property bool largeScreen: Screen.width >= 1920 && Screen.height >= 1080 readonly property real borderRadius: largeScreen ? 7.5 : 5.0 readonly property real borderWidth: largeScreen ? 2 : 1 @@ -168,8 +156,8 @@ Item { readonly property real buttonWidth: 120 } - Item { - id: fontSizes // In pixels + readonly property QtObject fontSizes: QtObject { + // In pixels readonly property real overlayTitle: dimensions.largeScreen ? 18 : 14 readonly property real tabName: dimensions.largeScreen ? 12 : 10 readonly property real sectionName: dimensions.largeScreen ? 12 : 10 @@ -194,8 +182,7 @@ Item { readonly property real disclosureButton: dimensions.largeScreen ? 30 : 22 } - Item { - id: icons + readonly property QtObject icons: QtObject { // Values per OffscreenUi::Icon readonly property int none: 0 readonly property int question: 1 @@ -205,8 +192,7 @@ Item { readonly property int placemark: 5 } - Item { - id: buttons + readonly property QtObject buttons: QtObject { readonly property int white: 0 readonly property int blue: 1 readonly property int red: 2 @@ -227,12 +213,11 @@ Item { readonly property int radius: 5 } - QtObject { - id: effects + readonly property QtObject effects: QtObject { readonly property int fadeInDuration: 300 } - Item { - id: glyphs + + readonly property QtObject glyphs: QtObject { readonly property string noIcon: "" readonly property string hmd: "b" readonly property string screen: "c" diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1ea2e559cc..968af3e298 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -377,8 +377,6 @@ static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; -static const QString DOMAIN_SPAWNING_POINT = "/0, -10, 0"; - const std::vector> Application::_acceptedExtensions { { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, @@ -513,6 +511,27 @@ std::atomic DeadlockWatchdogThread::_maxElapsed; std::atomic DeadlockWatchdogThread::_maxElapsedAverage; ThreadSafeMovingAverage DeadlockWatchdogThread::_movingAverage; +bool isDomainURL(QUrl url) { + if (!url.isValid()) { + return false; + } + if (url.scheme() == URL_SCHEME_HIFI) { + return true; + } + if (url.scheme() != URL_SCHEME_FILE) { + // TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can + // be loaded over http(s) + // && url.scheme() != URL_SCHEME_HTTP && + // url.scheme() != URL_SCHEME_HTTPS + return false; + } + if (url.path().endsWith(".json", Qt::CaseInsensitive) || + url.path().endsWith(".json.gz", Qt::CaseInsensitive)) { + return true; + } + return false; +} + #ifdef Q_OS_WIN class MyNativeEventFilter : public QAbstractNativeEventFilter { public: @@ -542,7 +561,7 @@ public: if (message->message == WM_COPYDATA) { COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)(message->lParam); QUrl url = QUrl((const char*)(pcds->lpData)); - if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) { + if (isDomainURL(url)) { DependencyManager::get()->handleLookupString(url.toString()); return true; } @@ -940,6 +959,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); setProperty(hifi::properties::CRASHED, _previousSessionCrashed); + _entityClipboard->setIsServerlessMode(true); + { const QString TEST_SCRIPT = "--testScript"; const QString TRACE_FILE = "--traceFile"; @@ -1035,7 +1056,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // setup a timer for domain-server check ins QTimer* domainCheckInTimer = new QTimer(this); - connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn); + connect(domainCheckInTimer, &QTimer::timeout, [this, nodeList] { + if (!isServerlessMode()) { + nodeList->sendDomainServerCheckIn(); + } + }); domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); connect(this, &QCoreApplication::aboutToQuit, [domainCheckInTimer] { domainCheckInTimer->stop(); @@ -1097,9 +1122,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo const DomainHandler& domainHandler = nodeList->getDomainHandler(); - connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); + connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainURLChanged(QUrl))); connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); - connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); + connect(&domainHandler, SIGNAL(connectedToDomain(QUrl)), SLOT(updateWindowTitle())); connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &Application::clearDomainAvatars); connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this]() { @@ -2046,7 +2071,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&_addAssetToWorldErrorTimer, &QTimer::timeout, this, &Application::addAssetToWorldErrorTimeout); connect(this, &QCoreApplication::aboutToQuit, this, &Application::addAssetToWorldMessageClose); - connect(&domainHandler, &DomainHandler::hostnameChanged, this, &Application::addAssetToWorldMessageClose); + connect(&domainHandler, &DomainHandler::domainURLChanged, this, &Application::addAssetToWorldMessageClose); updateSystemTabletMode(); @@ -3023,27 +3048,27 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { QString sentTo; - // If this is a first run we short-circuit the address passed in - if (firstRun.get()) { + // If this is a first run we short-circuit the address passed in + if (firstRun.get()) { #if !defined(Q_OS_ANDROID) - showHelp(); -#endif - if (sandboxIsRunning) { - qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox(); - sentTo = SENT_TO_SANDBOX; - } else { - qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; - DependencyManager::get()->goToEntry(); - sentTo = SENT_TO_ENTRY; - } - firstRun.set(false); - + showHelp(); +#endif + if (sandboxIsRunning) { + qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; + DependencyManager::get()->goToLocalSandbox(); + sentTo = SENT_TO_SANDBOX; } else { - qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); - DependencyManager::get()->loadSettings(addressLookupString); - sentTo = SENT_TO_PREVIOUS_LOCATION; + qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; + DependencyManager::get()->goToEntry(); + sentTo = SENT_TO_ENTRY; } + firstRun.set(false); + + } else { + qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); + DependencyManager::get()->loadSettings(addressLookupString); + sentTo = SENT_TO_PREVIOUS_LOCATION; + } UserActivityLogger::getInstance().logAction("startup_sent_to", { { "sent_to", sentTo }, @@ -3083,6 +3108,57 @@ bool Application::importFromZIP(const QString& filePath) { return true; } +bool Application::isServerlessMode() const { + auto tree = getEntities()->getTree(); + if (tree) { + return tree->isServerlessMode(); + } + return false; +} + +void Application::setIsServerlessMode(bool serverlessDomain) { + auto tree = getEntities()->getTree(); + if (tree) { + tree->setIsServerlessMode(serverlessDomain); + } +} + +void Application::loadServerlessDomain(QUrl domainURL) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadServerlessDomain", Q_ARG(QUrl, domainURL)); + return; + } + + if (domainURL.isEmpty()) { + return; + } + + QUuid serverlessSessionID = QUuid::createUuid(); + getMyAvatar()->setSessionUUID(serverlessSessionID); + auto nodeList = DependencyManager::get(); + nodeList->setSessionUUID(serverlessSessionID); + + // there is no domain-server to tell us our permissions, so enable all + NodePermissions permissions; + permissions.setAll(true); + nodeList->setPermissions(permissions); + + // we can't import directly into the main tree because we would need to lock it, and + // Octree::readFromURL calls loop.exec which can run code which will also attempt to lock the tree. + EntityTreePointer tmpTree(new EntityTree()); + tmpTree->setIsServerlessMode(true); + tmpTree->createRootElement(); + auto myAvatar = getMyAvatar(); + tmpTree->setMyAvatar(myAvatar); + bool success = tmpTree->readFromURL(domainURL.toString()); + if (success) { + tmpTree->reaverageOctreeElements(); + tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), 0, 0, 0); + } + + _fullSceneReceivedCounter++; +} + bool Application::importImage(const QString& urlString) { qCDebug(interfaceapp) << "An image file has been dropped in"; QString filepath(urlString); @@ -4583,7 +4659,7 @@ void Application::initDisplay() { } void Application::init() { - + // Make sure Login state is up to date DependencyManager::get()->toggleLoginDialog(); if (!DISABLE_DEFERRED) { @@ -4608,7 +4684,9 @@ void Application::init() { qCDebug(interfaceapp) << "Loaded settings"; // fire off an immediate domain-server check in now that settings are loaded - DependencyManager::get()->sendDomainServerCheckIn(); + if (!isServerlessMode()) { + DependencyManager::get()->sendDomainServerCheckIn(); + } // This allows collision to be set up properly for shape entities supported by GeometryCache. // This is before entity setup to ensure that it's ready for whenever instance collision is initialized. @@ -5742,10 +5820,15 @@ void Application::updateWindowTitle() const { QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)"; QString username = accountManager->getAccountInfo().getUsername(); - QString currentPlaceName = DependencyManager::get()->getHost(); - if (currentPlaceName.isEmpty()) { - currentPlaceName = nodeList->getDomainHandler().getHostname(); + QString currentPlaceName; + if (isServerlessMode()) { + currentPlaceName = "serverless: " + DependencyManager::get()->getDomainURL().toString(); + } else { + currentPlaceName = DependencyManager::get()->getDomainURL().host(); + if (currentPlaceName.isEmpty()) { + currentPlaceName = nodeList->getDomainHandler().getHostname(); + } } QString title = QString() + (!username.isEmpty() ? username + " @ " : QString()) @@ -5758,7 +5841,7 @@ void Application::updateWindowTitle() const { _window->setWindowTitle(title); // updateTitleWindow gets called whenever there's a change regarding the domain, so rather - // than placing this within domainChanged, it's placed here to cover the other potential cases. + // than placing this within domainURLChanged, it's placed here to cover the other potential cases. DependencyManager::get< MessagesClient >()->sendLocalMessage("Toolbar-DomainChanged", ""); } @@ -5797,15 +5880,22 @@ void Application::clearDomainAvatars() { DependencyManager::get()->clearOtherAvatars(); } -void Application::domainChanged(const QString& domainHostname) { - updateWindowTitle(); +void Application::domainURLChanged(QUrl domainURL) { // disable physics until we have enough information about our new location to not cause craziness. resetPhysicsReadyInformation(); + setIsServerlessMode(domainURL.scheme() != URL_SCHEME_HIFI); + if (isServerlessMode()) { + loadServerlessDomain(domainURL); + } + updateWindowTitle(); } void Application::resettingDomain() { _notifiedPacketVersionMismatchThisDomain = false; + + auto nodeList = DependencyManager::get(); + clearDomainOctreeDetails(); } void Application::nodeAdded(SharedNodePointer node) const { @@ -5922,22 +6012,22 @@ bool Application::nearbyEntitiesAreReadyForPhysics() { AABox avatarBox(getMyAvatar()->getWorldPosition() - glm::vec3(PHYSICS_READY_RANGE), glm::vec3(2 * PHYSICS_READY_RANGE)); // create two functions that use avatarBox (entityScan and elementScan), the second calls the first std::function entityScan = [=](EntityItemPointer& entity) { - if (entity->shouldBePhysical()) { - bool success = false; - AABox entityBox = entity->getAABox(success); - // important: bail for entities that cannot supply a valid AABox - return success && avatarBox.touches(entityBox); - } - return false; - }; + if (entity->shouldBePhysical()) { + bool success = false; + AABox entityBox = entity->getAABox(success); + // important: bail for entities that cannot supply a valid AABox + return success && avatarBox.touches(entityBox); + } + return false; + }; std::function elementScan = [&](const OctreeElementPointer& element, void* unused) { - if (element->getAACube().touches(avatarBox)) { - EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->getEntities(entityScan, entities); - return true; - } - return false; - }; + if (element->getAACube().touches(avatarBox)) { + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + entityTreeElement->getEntities(entityScan, entities); + return true; + } + return false; + }; entityTree->withReadLock([&] { // Pass the second function to the general-purpose EntityTree::findEntities() @@ -6173,7 +6263,7 @@ bool Application::canAcceptURL(const QString& urlString) const { QUrl url(urlString); if (url.query().contains(WEB_VIEW_TAG)) { return false; - } else if (urlString.startsWith(HIFI_URL_SCHEME)) { + } else if (urlString.startsWith(URL_SCHEME_HIFI)) { return true; } QString lowerPath = url.path().toLower(); @@ -6186,15 +6276,14 @@ bool Application::canAcceptURL(const QString& urlString) const { } bool Application::acceptURL(const QString& urlString, bool defaultUpload) { - if (urlString.startsWith(HIFI_URL_SCHEME)) { - // this is a hifi URL - have the AddressManager handle it - emit receivedHifiSchemeURL(urlString); + QUrl url(urlString); + if (isDomainURL(url)) { + // this is a URL for a domain, either hifi:// or serverless - have the AddressManager handle it QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString", Qt::AutoConnection, Q_ARG(const QString&, urlString)); return true; } - QUrl url(urlString); QString lowerPath = url.path().toLower(); for (auto& pair : _acceptedExtensions) { if (lowerPath.endsWith(pair.first, Qt::CaseInsensitive)) { @@ -7033,7 +7122,7 @@ void Application::packageModel() { void Application::openUrl(const QUrl& url) const { if (!url.isEmpty()) { - if (url.scheme() == HIFI_URL_SCHEME) { + if (url.scheme() == URL_SCHEME_HIFI) { DependencyManager::get()->handleLookupString(url.toString()); } else { // address manager did not handle - ask QDesktopServices to handle @@ -7347,10 +7436,35 @@ bool Application::isThrottleRendering() const { } bool Application::hasFocus() const { - if (_displayPlugin) { - return getActiveDisplayPlugin()->hasFocus(); + bool result = (QApplication::activeWindow() != nullptr); +#if defined(Q_OS_WIN) + // On Windows, QWidget::activateWindow() - as called in setFocus() - makes the application's taskbar icon flash but doesn't + // take user focus away from their current window. So also check whether the application is the user's current foreground + // window. + result = result && (HWND)QApplication::activeWindow()->winId() == GetForegroundWindow(); +#endif + return result; +} + +void Application::setFocus() { + // Note: Windows doesn't allow a user focus to be taken away from another application. Instead, it changes the color of and + // flashes the taskbar icon. + auto window = qApp->getWindow(); + window->activateWindow(); +} + +void Application::raise() { + auto windowState = qApp->getWindow()->windowState(); + if (windowState & Qt::WindowMinimized) { + if (windowState & Qt::WindowMaximized) { + qApp->getWindow()->showMaximized(); + } else if (windowState & Qt::WindowFullScreen) { + qApp->getWindow()->showFullScreen(); + } else { + qApp->getWindow()->showNormal(); + } } - return (QApplication::activeWindow() != nullptr); + qApp->getWindow()->raise(); } void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 95aaa9b6cd..812e51e49c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -161,6 +161,8 @@ public: QRect getRecommendedHUDRect() const; glm::vec2 getDeviceSize() const; bool hasFocus() const; + void setFocus(); + void raise(); void showCursor(const Cursor::Icon& cursor); @@ -282,6 +284,8 @@ public: bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; } void saveNextPhysicsStats(QString filename); + bool isServerlessMode() const; + void replaceDomainContent(const QString& url); signals: @@ -293,7 +297,6 @@ signals: void activeDisplayPluginChanged(); void uploadRequest(QString path); - void receivedHifiSchemeURL(const QString& url); public slots: QVector pasteEntities(float x, float y, float z); @@ -389,6 +392,9 @@ public slots: const QString getPreferredCursor() const { return _preferredCursor.get(); } void setPreferredCursor(const QString& cursor); + void setIsServerlessMode(bool serverlessDomain); + void loadServerlessDomain(QUrl domainURL); + Q_INVOKABLE bool askBeforeSetAvatarUrl(const QString& avatarUrl) { return askToSetAvatarUrl(avatarUrl); } private slots: @@ -423,7 +429,7 @@ private slots: void setSessionUUID(const QUuid& sessionUUID) const; - void domainChanged(const QString& domainHostname); + void domainURLChanged(QUrl domainURL); void updateWindowTitle() const; void nodeAdded(SharedNodePointer node) const; void nodeActivated(SharedNodePointer node); diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp index 94fdacb5c0..e2d47bf844 100644 --- a/interface/src/DiscoverabilityManager.cpp +++ b/interface/src/DiscoverabilityManager.cpp @@ -49,7 +49,7 @@ void DiscoverabilityManager::updateLocation() { auto accountManager = DependencyManager::get(); auto addressManager = DependencyManager::get(); auto& domainHandler = DependencyManager::get()->getDomainHandler(); - bool discoverable = (_mode.get() != Discoverability::None); + bool discoverable = (_mode.get() != Discoverability::None) && !domainHandler.isServerless(); if (accountManager->isLoggedIn()) { diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 858af9b13d..0a9e867323 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -52,6 +52,8 @@ Handler(inventory) Handler(transferHfcToNode) Handler(transferHfcToUsername) Handler(alreadyOwned) +Handler(availableUpdates) +Handler(updateItem) void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) { auto accountManager = DependencyManager::get(); @@ -376,3 +378,23 @@ void Ledger::alreadyOwned(const QString& marketplaceId) { qDebug(commerce) << "User attempted to use the alreadyOwned endpoint, but cachedPublicKeys was empty!"; } } + +void Ledger::getAvailableUpdates(const QString& itemId) { + auto wallet = DependencyManager::get(); + QString endpoint = "available_updates"; + QJsonObject request; + request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys()); + if (!itemId.isEmpty()) { + request["marketplace_item_id"] = itemId; + } + send(endpoint, "availableUpdatesSuccess", "availableUpdatesFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request); +} + +void Ledger::updateItem(const QString& hfc_key, const QString& certificate_id) { + QJsonObject transaction; + transaction["public_key"] = hfc_key; + transaction["certificate_id"] = certificate_id; + QJsonDocument transactionDoc{ transaction }; + auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); + signedSend("transaction", transactionString, hfc_key, "update_item", "updateItemSuccess", "updateItemFailure"); +} diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 703ebda2dc..da97206bbc 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -36,6 +36,8 @@ public: void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage); void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage); void alreadyOwned(const QString& marketplaceId); + void getAvailableUpdates(const QString& itemId = ""); + void updateItem(const QString& hfc_key, const QString& certificate_id); enum CertificateStatus { CERTIFICATE_STATUS_UNKNOWN = 0, @@ -57,6 +59,8 @@ signals: void transferHfcToNodeResult(QJsonObject result); void transferHfcToUsernameResult(QJsonObject result); void alreadyOwnedResult(QJsonObject result); + void availableUpdatesResult(QJsonObject result); + void updateItemResult(QJsonObject result); void updateCertificateStatus(const QString& certID, uint certStatus); @@ -83,6 +87,10 @@ public slots: void transferHfcToUsernameFailure(QNetworkReply& reply); void alreadyOwnedSuccess(QNetworkReply& reply); void alreadyOwnedFailure(QNetworkReply& reply); + void availableUpdatesSuccess(QNetworkReply& reply); + void availableUpdatesFailure(QNetworkReply& reply); + void updateItemSuccess(QNetworkReply& reply); + void updateItemFailure(QNetworkReply& reply); private: QJsonObject apiResponse(const QString& label, QNetworkReply& reply); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 53ec59049f..8e956249cc 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -38,7 +38,8 @@ QmlCommerce::QmlCommerce() { connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus); connect(ledger.data(), &Ledger::transferHfcToNodeResult, this, &QmlCommerce::transferHfcToNodeResult); connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult); - connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult); + connect(ledger.data(), &Ledger::availableUpdatesResult, this, &QmlCommerce::availableUpdatesResult); + connect(ledger.data(), &Ledger::updateItemResult, this, &QmlCommerce::updateItemResult); auto accountManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { @@ -349,3 +350,20 @@ bool QmlCommerce::openApp(const QString& itemHref) { return true; } + +void QmlCommerce::getAvailableUpdates(const QString& itemId) { + auto ledger = DependencyManager::get(); + ledger->getAvailableUpdates(itemId); +} + +void QmlCommerce::updateItem(const QString& certificateId) { + auto ledger = DependencyManager::get(); + auto wallet = DependencyManager::get(); + QStringList keys = wallet->listPublicKeys(); + if (keys.count() == 0) { + QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } }; + return emit updateItemResult(result); + } + QString key = keys[0]; + ledger->updateItem(key, certificateId); +} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index b4af4393e3..a3a0ebfd32 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -43,6 +43,8 @@ signals: void accountResult(QJsonObject result); void certificateInfoResult(QJsonObject result); void alreadyOwnedResult(QJsonObject result); + void availableUpdatesResult(QJsonObject result); + void updateItemResult(QJsonObject result); void updateCertificateStatus(const QString& certID, uint certStatus); @@ -89,6 +91,9 @@ protected: Q_INVOKABLE bool uninstallApp(const QString& appHref); Q_INVOKABLE bool openApp(const QString& appHref); + Q_INVOKABLE void getAvailableUpdates(const QString& itemId = ""); + Q_INVOKABLE void updateItem(const QString& certificateId); + private: QString _appsPath; }; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index e80dc1d213..51ec4b1327 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -129,7 +129,7 @@ int main(int argc, const char* argv[]) { if (socket.waitForConnected(LOCAL_SERVER_TIMEOUT_MS)) { if (parser.isSet(urlOption)) { QUrl url = QUrl(parser.value(urlOption)); - if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) { + if (url.isValid() && url.scheme() == URL_SCHEME_HIFI) { qDebug() << "Writing URL to local socket"; socket.write(url.toString().toUtf8()); if (!socket.waitForBytesWritten(5000)) { diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 4bc8ec0bd3..d60a9bd126 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -69,11 +69,11 @@ void AssetMappingsScriptingInterface::getMapping(QString path, QJSValue callback void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping, QJSValue startedCallback, QJSValue completedCallback, bool dropEvent) { static const QString helpText = - "Upload your asset to a specific folder by entering the full path. Specifying\n" + "Upload your asset to a specific folder by entering the full path. Specifying " "a new folder name will automatically create that folder for you."; static const QString dropHelpText = "This file will be added to your Asset Server.\n" - "Use the field below to place your file in a specific folder or to rename it.\n" + "Use the field below to place your file in a specific folder or to rename it. " "Specifying a new folder name will automatically create that folder for you."; auto offscreenUi = DependencyManager::get(); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index e0a008b25e..78f55f0d6e 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -74,16 +74,14 @@ QScriptValue WindowScriptingInterface::hasFocus() { void WindowScriptingInterface::setFocus() { // It's forbidden to call focus() from another thread. qApp->postLambdaEvent([] { - auto window = qApp->getWindow(); - window->activateWindow(); - window->setFocus(); + qApp->setFocus(); }); } void WindowScriptingInterface::raiseMainWindow() { // It's forbidden to call raise() from another thread. qApp->postLambdaEvent([] { - qApp->getWindow()->raise(); + qApp->raise(); }); } @@ -126,7 +124,7 @@ void WindowScriptingInterface::promptAsync(const QString& message, const QString } void WindowScriptingInterface::disconnectedFromDomain() { - emit domainChanged(""); + emit domainChanged(QUrl()); } QString fixupPathForMac(const QString& directory) { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 5a30f44856..997e425e53 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -62,13 +62,14 @@ public slots: QScriptValue hasFocus(); /**jsdoc - * Make the Interface window have focus. + * Make the Interface window have focus. On Windows, if Interface doesn't already have focus, the task bar icon flashes to + * indicate that Interface wants attention but focus isn't taken away from the application that the user is using. * @function Window.setFocus */ void setFocus(); /**jsdoc - * Raise the Interface window if it is minimized, and give it focus. + * Raise the Interface window if it is minimized. If raised, the window gains focus. * @function Window.raiseMainWindow */ void raiseMainWindow(); @@ -523,7 +524,7 @@ signals: * Triggered when you change the domain you're visiting. Warning: Is not emitted if you go to domain that * isn't running. * @function Window.domainChanged - * @param {string} domain - The domain's IP address. + * @param {string} domainURL - The domain's URL. * @returns {Signal} * @example Report when you change domains. * function onDomainChanged(domain) { @@ -532,7 +533,7 @@ signals: * * Window.domainChanged.connect(onDomainChanged); */ - void domainChanged(const QString& domain); + void domainChanged(QUrl domainURL); /**jsdoc * Triggered when you try to navigate to a *.json, *.svo, or *.svo.json URL in a Web browser within Interface. diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 1a23674fa3..87bf09a252 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -45,7 +45,6 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare connect(&domainHandler, &DomainHandler::connectedToDomain, this, &AddressBarDialog::hostChanged); connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &AddressBarDialog::hostChanged); connect(DependencyManager::get().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed); - connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL); } void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) { diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 7806436774..1254fabbd2 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -36,7 +36,6 @@ signals: void backEnabledChanged(); void forwardEnabledChanged(); void useFeedChanged(); - void receivedHifiSchemeURL(const QString& url); void hostChanged(); protected: diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 849ea5ee6b..dc004fe60d 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -882,6 +882,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar //virtual const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { +#ifdef Q_OS_ANDROID + // disable IK on android + return underPoses; +#endif + // allows solutionSource to be overridden by an animVar auto solutionSource = animVars.lookup(_solutionSourceVar, (int)_solutionSource); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 65145d4c88..048b8b1633 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -221,8 +221,12 @@ void Avatar::updateAvatarEntities() { return; } - if (getID() == QUuid() || getID() == AVATAR_SELF_ID) { - return; // wait until MyAvatar gets an ID before doing this. + if (getID().isNull() || + getID() == AVATAR_SELF_ID || + DependencyManager::get()->getSessionUUID() == QUuid()) { + // wait until MyAvatar and this Node gets an ID before doing this. Otherwise, various things go wrong -- + // things get their parent fixed up from AVATAR_SELF_ID to a null uuid which means "no parent". + return; } auto treeRenderer = DependencyManager::get(); @@ -1806,4 +1810,4 @@ scriptable::ScriptableModelBase Avatar::getScriptableModel() { result.appendMaterials(_materials); } return result; -} \ No newline at end of file +} diff --git a/libraries/baking/src/JSBaker.cpp b/libraries/baking/src/JSBaker.cpp index a97a7fe5b3..9932ad633e 100644 --- a/libraries/baking/src/JSBaker.cpp +++ b/libraries/baking/src/JSBaker.cpp @@ -190,7 +190,7 @@ bool JSBaker::handleMultiLineComments(QTextStream& in) { while (!in.atEnd()) { in >> character; if (character == '*') { - if (in.read(1) == '/') { + if (in.read(1) == "/") { return true; } } @@ -228,7 +228,7 @@ bool JSBaker::isSpecialCharacter(QChar c) { // If previous character is a special character, maybe don't omit new line (depends on next character as well) bool JSBaker::isSpecialCharacterPrevious(QChar c) { return (c == '\'' || c == '$' || c == '_' || c == '}' || c == ']' || c == ')' || c == '+' || c == '-' - || c == '"' || c == "'"); + || c == '"' || c == '\''); } // If next character is a special character, maybe don't omit new line (depends on previous character as well) @@ -243,5 +243,5 @@ bool JSBaker::isSpaceOrTab(QChar c) { // Check If the currentCharacter is " or ' or ` bool JSBaker::isQuote(QChar c) { - return (c == '"' || c == "'" || c == '`'); + return (c == '"' || c == '\'' || c == '`'); } diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 29f011fba2..371deec7d5 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -73,7 +73,8 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) { qCDebug(controllers) << "Registered input device <" << device->getName() << "> deviceID = " << deviceID; - for (const auto& inputMapping : device->getAvailableInputs()) { + auto inputs = device->getAvailableInputs(); + for (const auto& inputMapping : inputs) { const auto& input = inputMapping.first; // Ignore aliases if (_endpointsByInput.count(input)) { @@ -126,7 +127,8 @@ void UserInputMapper::removeDevice(int deviceID) { _mappingsByDevice.erase(mappingsEntry); } - for (const auto& inputMapping : device->getAvailableInputs()) { + auto inputs = device->getAvailableInputs(); + for (const auto& inputMapping : inputs) { const auto& input = inputMapping.first; auto endpoint = _endpointsByInput.find(input); if (endpoint != _endpointsByInput.end()) { @@ -171,7 +173,7 @@ InputDevice::Pointer UserInputMapper::getDevice(const Input& input) { } } -QString UserInputMapper::getDeviceName(uint16 deviceID) { +QString UserInputMapper::getDeviceName(uint16 deviceID) { Locker locker(_lock); if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { return _registeredDevices[deviceID]->_name; @@ -181,7 +183,7 @@ QString UserInputMapper::getDeviceName(uint16 deviceID) { int UserInputMapper::findDevice(QString name) const { Locker locker(_lock); - for (auto device : _registeredDevices) { + for (const auto& device : _registeredDevices) { if (device.second->_name == name) { return device.first; } @@ -192,7 +194,7 @@ int UserInputMapper::findDevice(QString name) const { QVector UserInputMapper::getDeviceNames() { Locker locker(_lock); QVector result; - for (auto device : _registeredDevices) { + for (const auto& device : _registeredDevices) { QString deviceName = device.second->_name.split(" (")[0]; result << deviceName; } @@ -218,7 +220,7 @@ Input UserInputMapper::findDeviceInput(const QString& inputName) const { const auto& device = _registeredDevices.at(deviceID); auto deviceInputs = device->getAvailableInputs(); - for (auto input : deviceInputs) { + for (const auto& input : deviceInputs) { if (input.second == inputName) { return input.first; } @@ -321,7 +323,8 @@ QVector UserInputMapper::getAllActions() const { QString UserInputMapper::getActionName(Action action) const { Locker locker(_lock); - for (auto actionPair : getActionInputs()) { + auto inputs = getActionInputs(); + for (const auto& actionPair : inputs) { if (actionPair.first.channel == toInt(action)) { return actionPair.second; } @@ -331,18 +334,20 @@ QString UserInputMapper::getActionName(Action action) const { QString UserInputMapper::getStandardPoseName(uint16_t pose) { Locker locker(_lock); - for (auto posePair : getStandardInputs()) { + auto inputs = getStandardInputs(); + for (const auto& posePair : inputs) { if (posePair.first.channel == pose && posePair.first.getType() == ChannelType::POSE) { return posePair.second; } } return QString(); -} +} QVector UserInputMapper::getActionNames() const { Locker locker(_lock); QVector result; - for (auto actionPair : getActionInputs()) { + auto inputs = getActionInputs(); + for (const auto& actionPair : inputs) { result << actionPair.second; } return result; @@ -357,7 +362,7 @@ Pose UserInputMapper::getPoseState(Action action) const { bool UserInputMapper::triggerHapticPulse(float strength, float duration, controller::Hand hand) { Locker locker(_lock); bool toReturn = false; - for (auto device : _registeredDevices) { + for (const auto& device : _registeredDevices) { toReturn = toReturn || device.second->triggerHapticPulse(strength, duration, hand); } return toReturn; @@ -469,7 +474,7 @@ void UserInputMapper::runMappings() { if (debugRoutes) { qCDebug(controllers) << "Beginning mapping frame"; } - for (auto endpointEntry : this->_endpointsByInput) { + for (const auto& endpointEntry : _endpointsByInput) { endpointEntry.second->reset(); } @@ -542,9 +547,9 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { } - // Most endpoints can only be read once (though a given mapping can route them to + // Most endpoints can only be read once (though a given mapping can route them to // multiple places). Consider... If the default is to wire the A button to JUMP - // and someone else wires it to CONTEXT_MENU, I don't want both to occur when + // and someone else wires it to CONTEXT_MENU, I don't want both to occur when // I press the button. The exception is if I'm wiring a control back to itself // in order to adjust my interface, like inverting the Y axis on an analog stick if (!route->peek && !source->readable()) { @@ -897,7 +902,8 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) if (value.isArray()) { // Support "when" : [ "GamePad.RB", "GamePad.LB" ] Conditional::List children; - for (auto arrayItem : value.toArray()) { + auto array = value.toArray(); + for (const auto& arrayItem : array) { Conditional::Pointer childConditional = parseConditional(arrayItem); if (!childConditional) { return Conditional::Pointer(); @@ -908,7 +914,7 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) } else if (value.isString()) { // Support "when" : "GamePad.RB" auto conditionalToken = value.toString(); - + // Detect for modifier case (Not...) QString conditionalModifier; const QString JSON_CONDITIONAL_MODIFIER_NOT("!"); @@ -943,12 +949,12 @@ Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) { result = Filter::getFactory().create(value.toString()); } else if (value.isObject()) { result = Filter::parse(value.toObject()); - } + } if (!result) { qWarning() << "Invalid filter definition " << value; } - + return result; } @@ -960,7 +966,7 @@ Filter::List UserInputMapper::parseFilters(const QJsonValue& value) { if (value.isArray()) { Filter::List result; auto filtersArray = value.toArray(); - for (auto filterValue : filtersArray) { + for (const auto& filterValue : filtersArray) { Filter::Pointer filter = parseFilter(filterValue); if (!filter) { return Filter::List(); @@ -968,7 +974,7 @@ Filter::List UserInputMapper::parseFilters(const QJsonValue& value) { result.push_back(filter); } return result; - } + } Filter::Pointer filter = parseFilter(value); if (!filter) { @@ -980,7 +986,8 @@ Filter::List UserInputMapper::parseFilters(const QJsonValue& value) { Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) { if (value.isArray()) { ArrayEndpoint::Pointer result = std::make_shared(); - for (auto arrayItem : value.toArray()) { + auto array = value.toArray(); + for (const auto& arrayItem : array) { Endpoint::Pointer destination = parseEndpoint(arrayItem); if (!destination) { return Endpoint::Pointer(); @@ -988,14 +995,14 @@ Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) { result->_children.push_back(destination); } return result; - } - + } + return parseEndpoint(value); } Endpoint::Pointer UserInputMapper::parseAxis(const QJsonValue& value) { if (value.isObject()) { - auto object = value.toObject(); + auto object = value.toObject(); if (object.contains("makeAxis")) { auto axisValue = object.value("makeAxis"); if (axisValue.isArray()) { @@ -1017,7 +1024,8 @@ Endpoint::Pointer UserInputMapper::parseAxis(const QJsonValue& value) { Endpoint::Pointer UserInputMapper::parseAny(const QJsonValue& value) { if (value.isArray()) { Endpoint::List children; - for (auto arrayItem : value.toArray()) { + auto array = value.toArray(); + for (const auto& arrayItem : array) { Endpoint::Pointer destination = parseEndpoint(arrayItem); if (!destination) { return Endpoint::Pointer(); @@ -1162,7 +1170,7 @@ Mapping::Pointer UserInputMapper::parseMapping(const QString& json) { template bool hasDebuggableRoute(const T& routes) { - for (auto route : routes) { + for (const auto& route : routes) { if (route->debug) { return true; } @@ -1174,7 +1182,7 @@ bool hasDebuggableRoute(const T& routes) { void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { Locker locker(_lock); // New routes for a device get injected IN FRONT of existing routes. Routes - // are processed in order so this ensures that the standard -> action processing + // are processed in order so this ensures that the standard -> action processing // takes place after all of the hardware -> standard or hardware -> action processing // because standard -> action is the first set of routes added. Route::List standardRoutes = mapping->routes; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp index ef9f04402b..f73268b41f 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp @@ -16,11 +16,14 @@ using namespace controller; void ActionEndpoint::apply(float newValue, const Pointer& source) { - InputRecorder* inputRecorder = InputRecorder::getInstance(); auto userInputMapper = DependencyManager::get(); - QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); - if(inputRecorder->isPlayingback()) { - newValue = inputRecorder->getActionState(actionName); + InputRecorder* inputRecorder = InputRecorder::getInstance(); + QString actionName; + if (inputRecorder->isPlayingback() || inputRecorder->isRecording()) { + actionName = userInputMapper->getActionName(Action(_input.getChannel())); + if (inputRecorder->isPlayingback()) { + newValue = inputRecorder->getActionState(actionName); + } } _currentValue += newValue; @@ -32,10 +35,12 @@ void ActionEndpoint::apply(float newValue, const Pointer& source) { void ActionEndpoint::apply(const Pose& value, const Pointer& source) { _currentPose = value; - InputRecorder* inputRecorder = InputRecorder::getInstance(); auto userInputMapper = DependencyManager::get(); - QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); - inputRecorder->setActionState(actionName, _currentPose); + InputRecorder* inputRecorder = InputRecorder::getInstance(); + if (inputRecorder->isRecording()) { + QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); + inputRecorder->setActionState(actionName, _currentPose); + } if (!_currentPose.isValid()) { return; diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 6a19a34727..47a213cf71 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -21,10 +21,6 @@ glm::uvec2 NullDisplayPlugin::getRecommendedRenderSize() const { return glm::uvec2(100, 100); } -bool NullDisplayPlugin::hasFocus() const { - return false; -} - void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) { if (frame) { _gpuContext->consumeFrameUpdates(frame); diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index 97b71b5780..11563b3798 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -16,7 +16,6 @@ public: grouping getGrouping() const override { return DEVELOPER; } glm::uvec2 getRecommendedRenderSize() const override; - bool hasFocus() const override; void submitFrame(const gpu::FramePointer& newFrame) override; QImage getScreenshot(float aspectRatio = 0.0f) const override; QImage getSecondaryCameraScreenshot() const override; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index db9b86b9dd..4b33565bfd 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -530,7 +530,11 @@ void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::Textur batch.setStateScissorRect(scissor); batch.setViewportTransform(viewport); batch.setResourceTexture(0, texture); +#ifndef USE_GLES batch.setPipeline(_presentPipeline); +#else + batch.setPipeline(_simplePipeline); +#endif batch.draw(gpu::TRIANGLE_STRIP, 4); if (copyFbo) { gpu::Vec4i copyFboRect(0, 0, copyFbo->getWidth(), copyFbo->getHeight()); @@ -831,11 +835,6 @@ glm::uvec2 OpenGLDisplayPlugin::getSurfaceSize() const { return result; } -bool OpenGLDisplayPlugin::hasFocus() const { - auto window = _container->getPrimaryWidget(); - return window ? window->hasFocus() : false; -} - void OpenGLDisplayPlugin::assertNotPresentThread() const { Q_ASSERT(QThread::currentThread() != _presentThread); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index bde7984ec0..1cc060161b 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -98,8 +98,6 @@ protected: virtual void compositePointer(); virtual void compositeExtra() {}; - virtual bool hasFocus() const override; - // These functions must only be called on the presentation thread virtual void customizeContext(); virtual void uncustomizeContext(); diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index 00879e1380..280b44cec0 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -55,7 +55,7 @@ HTTPConnection::~HTTPConnection() { QHash HTTPConnection::parseUrlEncodedForm() { // make sure we have the correct MIME type - QList elements = _requestHeaders.value("Content-Type").split(';'); + QList elements = requestHeader("Content-Type").split(';'); QString contentType = elements.at(0).trimmed(); if (contentType != "application/x-www-form-urlencoded") { @@ -75,7 +75,7 @@ QHash HTTPConnection::parseUrlEncodedForm() { QList HTTPConnection::parseFormData() const { // make sure we have the correct MIME type - QList elements = _requestHeaders.value("Content-Type").split(';'); + QList elements = requestHeader("Content-Type").split(';'); QString contentType = elements.at(0).trimmed(); @@ -251,7 +251,7 @@ void HTTPConnection::readHeaders() { if (trimmed.isEmpty()) { _socket->disconnect(this, SLOT(readHeaders())); - QByteArray clength = _requestHeaders.value("Content-Length"); + QByteArray clength = requestHeader("Content-Length"); if (clength.isEmpty()) { _parentManager->handleHTTPRequest(this, _requestUrl); @@ -275,7 +275,7 @@ void HTTPConnection::readHeaders() { respond("400 Bad Request", "The header was malformed."); return; } - _lastRequestHeader = trimmed.left(idx); + _lastRequestHeader = trimmed.left(idx).toLower(); QByteArray& value = _requestHeaders[_lastRequestHeader]; if (!value.isEmpty()) { value.append(", "); diff --git a/libraries/embedded-webserver/src/HTTPConnection.h b/libraries/embedded-webserver/src/HTTPConnection.h index 60408d4325..e4d23e3c90 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.h +++ b/libraries/embedded-webserver/src/HTTPConnection.h @@ -72,8 +72,8 @@ public: /// Returns a reference to the request URL. const QUrl& requestUrl () const { return _requestUrl; } - /// Returns a reference to the request headers. - const Headers& requestHeaders () const { return _requestHeaders; } + /// Returns a copy of the request header value. If it does not exist, it will return a default constructed QByteArray. + QByteArray requestHeader(const QString& key) const { return _requestHeaders.value(key.toLower().toLocal8Bit()); } /// Returns a reference to the request content. const QByteArray& requestContent () const { return _requestContent; } diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 119a46b68f..ea8edb8a08 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -9,6 +9,7 @@ #include "RenderableMaterialEntityItem.h" #include "RenderPipelines.h" +#include "GeometryCache.h" using namespace render; using namespace render::entities; @@ -90,138 +91,6 @@ ShapeKey MaterialEntityRenderer::getShapeKey() { return builder.build(); } -glm::vec3 MaterialEntityRenderer::getVertexPos(float phi, float theta) { - return glm::vec3(glm::sin(theta) * glm::cos(phi), glm::cos(theta), glm::sin(theta) * glm::sin(phi)); -} - -glm::vec3 MaterialEntityRenderer::getTangent(float phi, float theta) { - return glm::vec3(-glm::cos(theta) * glm::cos(phi), glm::sin(theta), -glm::cos(theta) * glm::sin(phi)); -} - -void MaterialEntityRenderer::addVertex(std::vector& buffer, const glm::vec3& pos, const glm::vec3& tan, const glm::vec2 uv) { - buffer.push_back(pos.x); buffer.push_back(pos.y); buffer.push_back(pos.z); - buffer.push_back(tan.x); buffer.push_back(tan.y); buffer.push_back(tan.z); - buffer.push_back(uv.x); buffer.push_back(uv.y); -} - -void MaterialEntityRenderer::addTriangleFan(std::vector& buffer, int stack, int step) { - float v1 = ((float)stack) / STACKS; - float theta1 = v1 * (float)M_PI; - glm::vec3 tip = getVertexPos(0, theta1); - float v2 = ((float)(stack + step)) / STACKS; - float theta2 = v2 * (float)M_PI; - for (int i = 0; i < SLICES; i++) { - float u1 = ((float)i) / SLICES; - float u2 = ((float)(i + step)) / SLICES; - float phi1 = u1 * M_PI_TIMES_2; - float phi2 = u2 * M_PI_TIMES_2; - /* (flipped for negative step) - p1 - / \ - / \ - / \ - p3 ------ p2 - */ - - glm::vec3 pos2 = getVertexPos(phi2, theta2); - glm::vec3 pos3 = getVertexPos(phi1, theta2); - - glm::vec3 tan1 = getTangent(0, theta1); - glm::vec3 tan2 = getTangent(phi2, theta2); - glm::vec3 tan3 = getTangent(phi1, theta2); - - glm::vec2 uv1 = glm::vec2((u1 + u2) / 2.0f, v1); - glm::vec2 uv2 = glm::vec2(u2, v2); - glm::vec2 uv3 = glm::vec2(u1, v2); - - addVertex(buffer, tip, tan1, uv1); - addVertex(buffer, pos2, tan2, uv2); - addVertex(buffer, pos3, tan3, uv3); - - _numVertices += 3; - } -} - -int MaterialEntityRenderer::_numVertices = 0; -std::shared_ptr MaterialEntityRenderer::_streamFormat = nullptr; -std::shared_ptr MaterialEntityRenderer::_stream = nullptr; -std::shared_ptr MaterialEntityRenderer::_verticesBuffer = nullptr; - -void MaterialEntityRenderer::generateMesh() { - _streamFormat = std::make_shared(); - _stream = std::make_shared(); - _verticesBuffer = std::make_shared(); - - const int NUM_POS_COORDS = 3; - const int NUM_TANGENT_COORDS = 3; - const int VERTEX_TANGENT_OFFSET = NUM_POS_COORDS * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = VERTEX_TANGENT_OFFSET + NUM_TANGENT_COORDS * sizeof(float); - - _streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - _streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - _streamFormat->setAttribute(gpu::Stream::TANGENT, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_TANGENT_OFFSET); - _streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); - - _stream->addBuffer(_verticesBuffer, 0, _streamFormat->getChannels().at(0)._stride); - - std::vector vertexBuffer; - - // Top - addTriangleFan(vertexBuffer, 0, 1); - - // Middle section - for (int j = 1; j < STACKS - 1; j++) { - float v1 = ((float)j) / STACKS; - float v2 = ((float)(j + 1)) / STACKS; - float theta1 = v1 * (float)M_PI; - float theta2 = v2 * (float)M_PI; - for (int i = 0; i < SLICES; i++) { - float u1 = ((float)i) / SLICES; - float u2 = ((float)(i + 1)) / SLICES; - float phi1 = u1 * M_PI_TIMES_2; - float phi2 = u2 * M_PI_TIMES_2; - - /* - p2 ---- p3 - | / | - | / | - | / | - p1 ---- p4 - */ - - glm::vec3 pos1 = getVertexPos(phi1, theta2); - glm::vec3 pos2 = getVertexPos(phi1, theta1); - glm::vec3 pos3 = getVertexPos(phi2, theta1); - glm::vec3 pos4 = getVertexPos(phi2, theta2); - - glm::vec3 tan1 = getTangent(phi1, theta2); - glm::vec3 tan2 = getTangent(phi1, theta1); - glm::vec3 tan3 = getTangent(phi2, theta1); - glm::vec3 tan4 = getTangent(phi2, theta2); - - glm::vec2 uv1 = glm::vec2(u1, v2); - glm::vec2 uv2 = glm::vec2(u1, v1); - glm::vec2 uv3 = glm::vec2(u2, v1); - glm::vec2 uv4 = glm::vec2(u2, v2); - - addVertex(vertexBuffer, pos1, tan1, uv1); - addVertex(vertexBuffer, pos2, tan2, uv2); - addVertex(vertexBuffer, pos3, tan3, uv3); - - addVertex(vertexBuffer, pos3, tan3, uv3); - addVertex(vertexBuffer, pos4, tan4, uv4); - addVertex(vertexBuffer, pos1, tan1, uv1); - - _numVertices += 6; - } - } - - // Bottom - addTriangleFan(vertexBuffer, STACKS, -1); - - _verticesBuffer->append(vertexBuffer.size() * sizeof(float), (gpu::Byte*) vertexBuffer.data()); -} - void MaterialEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderableMaterialEntityItem::render"); Q_ASSERT(args->_batch); @@ -252,14 +121,7 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) { args->_details._materialSwitches++; // Draw! - if (_numVertices == 0) { - generateMesh(); - } + DependencyManager::get()->renderSphere(batch); - batch.setInputFormat(_streamFormat); - batch.setInputStream(0, *_stream); - batch.draw(gpu::TRIANGLES, _numVertices, 0); - - const int NUM_VERTICES_PER_TRIANGLE = 3; - args->_details._trianglesRendered += _numVertices / NUM_VERTICES_PER_TRIANGLE; + args->_details._trianglesRendered += (int)DependencyManager::get()->getSphereTriangleCount(); } diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h index fef1a41138..8de2190a0c 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h @@ -40,20 +40,6 @@ private: Transform _renderTransform; std::shared_ptr _drawMaterial; - - static int _numVertices; - static std::shared_ptr _streamFormat; - static std::shared_ptr _stream; - static std::shared_ptr _verticesBuffer; - - void generateMesh(); - void addTriangleFan(std::vector& buffer, int stack, int step); - static glm::vec3 getVertexPos(float phi, float theta); - static glm::vec3 getTangent(float phi, float theta); - static void addVertex(std::vector& buffer, const glm::vec3& pos, const glm::vec3& tan, const glm::vec2 uv); - const int SLICES = 15; - const int STACKS = 9; - const float M_PI_TIMES_2 = 2.0f * (float)M_PI; }; } } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index e5cbfdb59d..b05854da4e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -19,6 +19,8 @@ #include "render-utils/simple_vert.h" #include "render-utils/simple_frag.h" +#include "RenderPipelines.h" + //#define SHAPE_ENTITY_USE_FADE_EFFECT #ifdef SHAPE_ENTITY_USE_FADE_EFFECT #include @@ -108,11 +110,94 @@ bool ShapeEntityRenderer::isTransparent() const { if (_procedural.isEnabled() && _procedural.isFading()) { return Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) < 1.0f; } - - // return _entity->getLocalRenderAlpha() < 1.0f || Parent::isTransparent(); + + auto mat = _materials.find("0"); + if (mat != _materials.end()) { + if (mat->second.top().material) { + auto matKey = mat->second.top().material->getKey(); + if (matKey.isTranslucent()) { + return true; + } + } + } + return Parent::isTransparent(); } +ItemKey ShapeEntityRenderer::getKey() { + ItemKey::Builder builder; + builder.withTypeShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); + + withReadLock([&] { + if (isTransparent()) { + builder.withTransparent(); + } + }); + + return builder.build(); +} + +bool ShapeEntityRenderer::useMaterialPipeline() const { + bool proceduralReady = resultWithReadLock([&] { + return _procedural.isReady(); + }); + if (proceduralReady) { + return false; + } + + graphics::MaterialKey drawMaterialKey; + auto mat = _materials.find("0"); + if (mat != _materials.end() && mat->second.top().material) { + drawMaterialKey = mat->second.top().material->getKey(); + } + + if (drawMaterialKey.isEmissive() || drawMaterialKey.isUnlit() || drawMaterialKey.isMetallic() || drawMaterialKey.isScattering()) { + return true; + } + + // If the material is using any map, we need to use a material ShapeKey + for (int i = 0; i < graphics::Material::MapChannel::NUM_MAP_CHANNELS; i++) { + if (drawMaterialKey.isMapChannel(graphics::Material::MapChannel(i))) { + return true; + } + } + return false; +} + +ShapeKey ShapeEntityRenderer::getShapeKey() { + if (useMaterialPipeline()) { + graphics::MaterialKey drawMaterialKey; + if (_materials["0"].top().material) { + drawMaterialKey = _materials["0"].top().material->getKey(); + } + + bool isTranslucent = drawMaterialKey.isTranslucent(); + bool hasTangents = drawMaterialKey.isNormalMap(); + bool hasLightmap = drawMaterialKey.isLightmapMap(); + bool isUnlit = drawMaterialKey.isUnlit(); + + ShapeKey::Builder builder; + builder.withMaterial(); + + if (isTranslucent) { + builder.withTranslucent(); + } + if (hasTangents) { + builder.withTangents(); + } + if (hasLightmap) { + builder.withLightmap(); + } + if (isUnlit) { + builder.withUnlit(); + } + + return builder.build(); + } else { + return Parent::getShapeKey(); + } +} + void ShapeEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); Q_ASSERT(args->_batch); @@ -149,7 +234,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } else { geometryCache->renderShape(batch, geometryShape, outColor); } - } else { + } else if (!useMaterialPipeline()) { // FIXME, support instanced multi-shape rendering using multidraw indirect outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; auto pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline(); @@ -158,6 +243,11 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } else { geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline); } + } else { + RenderPipelines::bindMaterial(mat, batch, args->_enableTexturing); + args->_details._materialSwitches++; + + geometryCache->renderShape(batch, geometryShape); } const auto triCount = geometryCache->getShapeTriangleCount(geometryShape); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index de855ce0c6..463ef187fc 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -24,6 +24,10 @@ public: virtual scriptable::ScriptableModelBase getScriptableModel() override; +protected: + ItemKey getKey() override; + ShapeKey getShapeKey() override; + private: virtual bool needsRenderUpdate() const override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; @@ -32,6 +36,8 @@ private: virtual void doRender(RenderArgs* args) override; virtual bool isTransparent() const override; + bool useMaterialPipeline() const; + Procedural _procedural; QString _lastUserData; Transform _renderTransform; diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 90740948ce..5d7bd61854 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -91,6 +91,11 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, return; } + if (entityTree && entityTree->isServerlessMode()) { + // if we are in a serverless domain, don't send edit packets + return; + } + QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0); if (type == PacketType::EntityAdd) { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c01ebe8ca4..5d056e17d8 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -963,7 +963,11 @@ void EntityItem::setHref(QString value) { // If the string has something and doesn't start with with "hifi://" it shouldn't be set // We allow the string to be empty, because that's the initial state of this property - if ( !(value.toLower().startsWith("hifi://")) && !value.isEmpty()) { + if (!value.isEmpty() && + !(value.toLower().startsWith("hifi://")) && + !(value.toLower().startsWith("file://")) + // TODO: serverless-domains will eventually support http and https also + ) { return; } withWriteLock([&] { diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index d348101b66..f9a96d2293 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -451,8 +451,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Entities.EntityType} type - The entity type. You cannot change the type of an entity after it's created. (Though * its value may switch among "Box", "Shape", and "Sphere" depending on changes to * the shape property set for entities of these types.) Read-only. - * @property {boolean} clientOnly=false - If true then the entity is an avatar entity, otherwise it is a server - * entity. Read-only. + * @property {boolean} clientOnly=false - If true then the entity is an avatar entity; otherwise it is a server + * entity. An avatar entity follows you to each domain you visit, rendering at the same world coordinates unless it's + * parented to your avatar. Value cannot be changed after the entity is created.
+ * The value can also be set at entity creation by using the clientOnly parameter in + * {@link Entities.addEntity}. * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if clientOnly is * true, otherwise {@link Uuid|Uuid.NULL}. Read-only. * @@ -1413,7 +1416,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_DIMENSIONS, localDimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); // Gettable but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); // Gettable but not settable except at entity creation COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); // Gettable but not settable // Rendering info @@ -2864,6 +2867,9 @@ void EntityItemProperties::markAllChanged() { _ambientLight.markAllChanged(); _skybox.markAllChanged(); + _keyLightModeChanged = true; + _skyboxModeChanged = true; + _ambientLightModeChanged = true; _hazeModeChanged = true; _animation.markAllChanged(); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 266ef41cff..7e15e78624 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -596,7 +596,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { shouldDelete = false; } else { // only delete local entities, server entities will round trip through the server filters - if (entity->getClientOnly()) { + if (entity->getClientOnly() || _entityTree->isServerlessMode()) { _entityTree->deleteEntity(entityID); } } @@ -1285,10 +1285,10 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, } doTransmit = actor(simulation, entity); + _entityTree->entityChanged(entity); if (doTransmit) { properties.setClientOnly(entity->getClientOnly()); properties.setOwningAvatarID(entity->getOwningAvatarID()); - _entityTree->entityChanged(entity); } }); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index b483225390..9613a7a310 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -203,9 +203,9 @@ public slots: * Add a new entity with specified properties. * @function Entities.addEntity * @param {Entities.EntityProperties} properties - The properties of the entity to create. - * @param {boolean} [clientOnly=false] - If true, the entity is created as an avatar entity, otherwise it - * is created on the server. An avatar entity follows you to each domain you visit, rendering at the same world - * coordinates unless it's parented to your avatar. + * @param {boolean} [clientOnly=false] - If true, or if clientOnly is set true in + * the properties, the entity is created as an avatar entity; otherwise it is created on the server. An avatar entity + * follows you to each domain you visit, rendering at the same world coordinates unless it's parented to your avatar. * @returns {Uuid} The ID of the entity if successfully created, otherwise {@link Uuid|Uuid.NULL}. * @example Create a box entity in front of your avatar. * var entityID = Entities.addEntity({ diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index c3b4716ccf..75f024d0b9 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -493,7 +493,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti if (!properties.getClientOnly() && getIsClient() && !nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() && - !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified()) { + !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain) { return nullptr; } @@ -1509,7 +1509,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } if (isAdd && properties.getLocked() && !senderNode->isAllowedEditor()) { - // if a node can't change locks, don't allow them to create an already-locked entity + // if a node can't change locks, don't allow it to create an already-locked entity -- automatically + // clear the locked property and allow the unlocked entity to be created. properties.setLocked(false); bumpTimestamp(properties); } @@ -2181,23 +2182,25 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen localTree->recurseTreeWithOperator(&moveOperator); } - // send add-entity packets to the server - i = map.begin(); - while (i != map.end()) { - EntityItemID newID = i.value(); - EntityItemPointer entity = localTree->findEntityByEntityItemID(newID); - if (entity) { - // queue the packet to send to the server - entity->updateQueryAACube(); - EntityItemProperties properties = entity->getProperties(); - properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity - packetSender->queueEditEntityMessage(PacketType::EntityAdd, localTree, newID, properties); - i++; - } else { - i = map.erase(i); + if (!_serverlessDomain) { + // send add-entity packets to the server + i = map.begin(); + while (i != map.end()) { + EntityItemID newID = i.value(); + EntityItemPointer entity = localTree->findEntityByEntityItemID(newID); + if (entity) { + // queue the packet to send to the server + entity->updateQueryAACube(); + EntityItemProperties properties = entity->getProperties(); + properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity + packetSender->queueEditEntityMessage(PacketType::EntityAdd, localTree, newID, properties); + i++; + } else { + i = map.erase(i); + } } + packetSender->releaseQueuedMessages(); } - packetSender->releaseQueuedMessages(); return map.values().toVector(); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index cb3d6d57e4..5f69714432 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -283,6 +283,9 @@ public: void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } + void setIsServerlessMode(bool value) { _serverlessDomain = value; } + bool isServerlessMode() const { return _serverlessDomain; } + static void setAddMaterialToEntityOperator(std::function addMaterialToEntityOperator) { _addMaterialToEntityOperator = addMaterialToEntityOperator; } static void setRemoveMaterialFromEntityOperator(std::function removeMaterialFromEntityOperator) { _removeMaterialFromEntityOperator = removeMaterialFromEntityOperator; } static bool addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName); @@ -325,7 +328,7 @@ protected: void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode); bool isScriptInWhitelist(const QString& scriptURL); - + QReadWriteLock _newlyCreatedHooksLock; QVector _newlyCreatedHooks; @@ -412,6 +415,8 @@ private: static std::function _removeMaterialFromAvatarOperator; static std::function _addMaterialToOverlayOperator; static std::function _removeMaterialFromOverlayOperator; + + bool _serverlessDomain { false }; }; #endif // hifi_EntityTree_h diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 1bde9e289e..4a2c5fd7f7 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -69,7 +69,7 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { qFatal("Offscreen surface is invalid"); } #endif - + if (gl::Context::enableDebugLogger()) { _context->makeCurrent(_offscreenSurface); QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this); diff --git a/libraries/gpu-gl-common/CMakeLists.txt b/libraries/gpu-gl-common/CMakeLists.txt new file mode 100644 index 0000000000..2b6f8d4d40 --- /dev/null +++ b/libraries/gpu-gl-common/CMakeLists.txt @@ -0,0 +1,6 @@ +set(TARGET_NAME gpu-gl-common) +setup_hifi_library(Concurrent) +link_hifi_libraries(shared gl gpu) +GroupSources("src") +target_opengl() + diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp similarity index 93% rename from libraries/gpu-gles/src/gpu/gl/GLBackend.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 5dc2b1446c..2089b8a378 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -1,9 +1,9 @@ // // GLBackend.cpp -// libraries/gpu-gl-android/src/gpu/gl +// libraries/gpu/src/gpu // -// Created by Cristian Duarte & Gabriel Calero on 9/21/2016. -// Copyright 2016 High Fidelity, Inc. +// Created by Sam Gateau on 10/27/2014. +// 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 @@ -16,16 +16,11 @@ #include #include -#include "../gles/GLESBackend.h" - #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" #endif -#include #include -#include -#include #include "GLTexture.h" #include "GLShader.h" @@ -33,39 +28,6 @@ using namespace gpu; using namespace gpu::gl; -static GLBackend* INSTANCE{ nullptr }; - -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(); - result->initTextureManagementStage(); - - INSTANCE = result.get(); - void* voidInstance = &(*result); - qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); - return result; -} - -GLBackend& getBackend() { - if (!INSTANCE) { - INSTANCE = static_cast(qApp->property(hifi::properties::gl::BACKEND).value()); - } - return *INSTANCE; -} - -bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { - return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); -} - - GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = { (&::gpu::gl::GLBackend::do_draw), @@ -151,6 +113,9 @@ void GLBackend::init() { qCDebug(gpugllogging) << "\tcard:" << gpu->getName(); qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver(); qCDebug(gpugllogging) << "\tdedicated memory:" << gpu->getMemory() << "MB"; +#if !defined(USE_GLES) + qCDebug(gpugllogging, "V-Sync is %s\n", (::gl::getSwapInterval() > 0 ? "ON" : "OFF")); +#endif #if THREADED_TEXTURE_BUFFERING // This has to happen on the main thread in order to give the thread // pool a reasonable parent object @@ -228,7 +193,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { } { // Sync the transform buffers - PROFILE_RANGE(render_gpu_gl_detail, "transferGPUTransform"); + PROFILE_RANGE(render_gpu_gl_detail, "syncGPUTransform"); transferTransformState(batch); } @@ -296,7 +261,7 @@ void GLBackend::render(const Batch& batch) { #ifdef GPU_STEREO_DRAWCALL_INSTANCED if (_stereo.isStereo()) { - glEnable(GL_CLIP_DISTANCE0_EXT); + glEnable(GL_CLIP_DISTANCE0); } #endif { @@ -305,7 +270,7 @@ void GLBackend::render(const Batch& batch) { } #ifdef GPU_STEREO_DRAWCALL_INSTANCED if (_stereo.isStereo()) { - glDisable(GL_CLIP_DISTANCE0_EXT); + glDisable(GL_CLIP_DISTANCE0); } #endif diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h similarity index 99% rename from libraries/gpu-gl/src/gpu/gl/GLBackend.h rename to libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 14a3d202f2..f2e2271552 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -32,9 +32,13 @@ // Different versions for the stereo drawcall // Current preferred is "instanced" which draw the shape twice but instanced and rely on clipping plane to draw left/right side only -//#define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE +#if defined(USE_GLES) +#define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE +#else //#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER #define GPU_STEREO_TECHNIQUE_INSTANCED +#endif + // Let these be configured by the one define picked above diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp similarity index 100% rename from libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendOutput.cpp similarity index 97% rename from libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendOutput.cpp index 59e77ebe90..2285b0e486 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendOutput.cpp @@ -17,6 +17,11 @@ using namespace gpu; using namespace gpu::gl; +#if defined(USE_GLES) +#define GL_FRAMEBUFFER_SRGB GL_FRAMEBUFFER_SRGB_EXT +#define glClearDepth glClearDepthf +#endif + void GLBackend::syncOutputStateCache() { GLint currentFBO; glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO); @@ -88,7 +93,7 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { if (masks & Framebuffer::BUFFER_STENCIL) { glClearStencil(stencil); glmask |= GL_STENCIL_BUFFER_BIT; - + cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront(); if (cacheStencilMask != 0xFF) { restoreStencilMask = true; @@ -182,7 +187,11 @@ void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, co return; } +#if defined(USE_GLES) + GLenum format = GL_RGBA; +#else GLenum format = GL_BGRA; +#endif if (destImage.format() != QImage::Format_ARGB32) { qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer"; return; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendQuery.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp similarity index 97% rename from libraries/gpu-gl/src/gpu/gl/GLBackendQuery.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp index bdea67a99a..b64804fe7c 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendQuery.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp @@ -25,6 +25,7 @@ static bool timeElapsed = false; #endif void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -43,9 +44,11 @@ void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { glquery->_rangeQueryDepth = _queryStage._rangeQueryDepth; (void)CHECK_GL_ERROR(); } +#endif } void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -66,9 +69,11 @@ void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } +#endif } void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -90,6 +95,7 @@ void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } } +#endif } void GLBackend::resetQueryStage() { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp similarity index 63% rename from libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp index 93c9b0d2ff..35dc8a3fbd 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp @@ -14,15 +14,32 @@ using namespace gpu::gl; // GLSL version std::string GLBackend::getBackendShaderHeader() const { - return std::string("#version 410 core"); + +#if defined(USE_GLES) + static const std::string header( +R"SHADER(#version 310 es +#extension GL_EXT_texture_buffer : enable +precision lowp float; // check precision 2 +precision lowp samplerBuffer; +precision lowp sampler2DShadow; +)SHADER"); +#else + static const std::string header( +R"SHADER(#version 410 core +)SHADER"); +#endif + + return header; } + // Shader domain static const size_t NUM_SHADER_DOMAINS = 3; +static_assert(Shader::Type::NUM_DOMAINS == NUM_SHADER_DOMAINS, "GL shader domains must equal defined GPU shader domains"); // GL Shader type enums // Must match the order of type specified in gpu::Shader::Type -static const std::array SHADER_DOMAINS { { +static const std::array SHADER_DOMAINS{ { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, @@ -30,22 +47,33 @@ static const std::array SHADER_DOMAINS { { // Domain specific defines // Must match the order of type specified in gpu::Shader::Type -static const std::array DOMAIN_DEFINES { { +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 { +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" +R"SHADER( +#define GPU_TRANSFORM_IS_STEREO +#define GPU_TRANSFORM_STEREO_CAMERA +#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED +#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN +)SHADER" #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" +R"SHADER( +#define GPU_TRANSFORM_IS_STEREO +#define GPU_TRANSFORM_STEREO_CAMERA +#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED +)SHADER" #else - "#define GPU_TRANSFORM_IS_STEREO" +R"SHADER( +#define GPU_TRANSFORM_IS_STEREO +)SHADER" #endif #endif }; @@ -67,7 +95,10 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co for (int version = 0; version < GLShader::NumVersions; version++) { auto& shaderObject = shaderObjects[version]; - std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version]; + std::string shaderDefines = getBackendShaderHeader() + "\n" + + DOMAIN_DEFINES[shader.getType()] + "\n" + + VERSION_DEFINES[version]; + if (handler) { bool retest = true; std::string currentSrc = shaderSource; @@ -154,149 +185,173 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader:: return object; } + GLBackend::ElementResource GLBackend::getFormatFromGLUniform(GLenum gltype) { switch (gltype) { - case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - /* - case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - */ - case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_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_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); + case GL_INT: + return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC2: + return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC3: + return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC4: + return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); + + case GL_UNSIGNED_INT: + return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC2: + return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC3: + return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC4: + return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); + + case GL_BOOL: + return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC2: + return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC3: + return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC4: + return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); + + case GL_FLOAT_MAT2: + return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT3: + return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT4: + return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + + case GL_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); + case GL_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); + case GL_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); + case GL_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_2D_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); + case GL_SAMPLER_CUBE_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); + case GL_SAMPLER_2D_ARRAY_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_BUFFER: + return ElementResource(Element(SCALAR, gpu::FLOAT, RESOURCE_BUFFER), Resource::BUFFER); + case GL_INT_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); + case GL_INT_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); + case GL_INT_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); + case GL_UNSIGNED_INT_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + +#if !defined(USE_GLES) + case GL_SAMPLER_1D: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); + case GL_SAMPLER_1D_ARRAY: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_INT_SAMPLER_1D: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); + case GL_INT_SAMPLER_1D_ARRAY: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_1D: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); #endif - case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); - - - case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - - /* {GL_FLOAT_MAT2x3 mat2x3}, - {GL_FLOAT_MAT2x4 mat2x4}, - {GL_FLOAT_MAT3x2 mat3x2}, - {GL_FLOAT_MAT3x4 mat3x4}, - {GL_FLOAT_MAT4x2 mat4x2}, - {GL_FLOAT_MAT4x3 mat4x3}, - {GL_DOUBLE_MAT2 dmat2}, - {GL_DOUBLE_MAT3 dmat3}, - {GL_DOUBLE_MAT4 dmat4}, - {GL_DOUBLE_MAT2x3 dmat2x3}, - {GL_DOUBLE_MAT2x4 dmat2x4}, - {GL_DOUBLE_MAT3x2 dmat3x2}, - {GL_DOUBLE_MAT3x4 dmat3x4}, - {GL_DOUBLE_MAT4x2 dmat4x2}, - {GL_DOUBLE_MAT4x3 dmat4x3}, - */ - - case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); - case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); - - case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); - case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); - -#if defined(Q_OS_WIN) - case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); -#endif - - case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); -#if defined(Q_OS_WIN) - case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); - - case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); -#endif - - // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, - // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, - - case GL_SAMPLER_BUFFER: return ElementResource(Element(SCALAR, gpu::FLOAT, RESOURCE_BUFFER), Resource::BUFFER); - - // {GL_SAMPLER_2D_RECT sampler2DRect}, - // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, - -#if defined(Q_OS_WIN) - case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); - case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); - case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - - // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, - // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, - - case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); - case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); - case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); -#endif - // {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, - // {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, - /* - {GL_IMAGE_1D image1D}, - {GL_IMAGE_2D image2D}, - {GL_IMAGE_3D image3D}, - {GL_IMAGE_2D_RECT image2DRect}, - {GL_IMAGE_CUBE imageCube}, - {GL_IMAGE_BUFFER imageBuffer}, - {GL_IMAGE_1D_ARRAY image1DArray}, - {GL_IMAGE_2D_ARRAY image2DArray}, - {GL_IMAGE_2D_MULTISAMPLE image2DMS}, - {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, - {GL_INT_IMAGE_1D iimage1D}, - {GL_INT_IMAGE_2D iimage2D}, - {GL_INT_IMAGE_3D iimage3D}, - {GL_INT_IMAGE_2D_RECT iimage2DRect}, - {GL_INT_IMAGE_CUBE iimageCube}, - {GL_INT_IMAGE_BUFFER iimageBuffer}, - {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, - {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, - {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, - {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, - {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, - {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, - {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, - {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, - {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot - - {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, - {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, - {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} - */ default: return ElementResource(Element(), Resource::BUFFER); } + // Non-covered types + //{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}, + //{GL_SAMPLER_1D_SHADOW sampler1DShadow}, + //{GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, + //{GL_SAMPLER_2D_RECT sampler2DRect}, + //{GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, + //{GL_INT_SAMPLER_BUFFER isamplerBuffer}, + //{GL_INT_SAMPLER_2D_RECT isampler2DRect}, + //{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}, + //{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} + + }; int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, @@ -550,4 +605,3 @@ void GLBackend::makeProgramBindings(ShaderObject& shaderObject) { qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; } } - diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendState.cpp similarity index 97% rename from libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendState.cpp index 24f90395d7..8363af9b00 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendState.cpp @@ -29,18 +29,19 @@ void GLBackend::resetPipelineState(State::Signature nextSignature) { } } + // Default line width accross the board + glLineWidth(1.0f); +#if !defined(USE_GLES) // force a few states regardless glEnable(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); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); - // Default line width accross the board - glLineWidth(1.0f); glEnable(GL_LINE_SMOOTH); +#endif } @@ -48,17 +49,19 @@ void GLBackend::syncPipelineStateCache() { State::Data state; // force a few states regardless - glEnable(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); - glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); // Default line width accross the board glLineWidth(1.0f); + +#if !defined(USE_GLES) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + + // Point size is always on + //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + glEnable(GL_PROGRAM_POINT_SIZE_EXT); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); glEnable(GL_LINE_SMOOTH); +#endif getCurrentGLState(state); State::Signature signature = State::evalSignature(state); @@ -70,11 +73,13 @@ void GLBackend::syncPipelineStateCache() { void GLBackend::do_setStateFillMode(int32 mode) { if (_pipeline._stateCache.fillMode != mode) { +#if !defined(USE_GLES) static GLenum GL_FILL_MODES[] = { GL_POINT, GL_LINE, GL_FILL }; glPolygonMode(GL_FRONT_AND_BACK, GL_FILL_MODES[mode]); (void)CHECK_GL_ERROR(); _pipeline._stateCache.fillMode = State::FillMode(mode); +#endif } } @@ -106,14 +111,15 @@ void GLBackend::do_setStateFrontFaceClockwise(bool isClockwise) { void GLBackend::do_setStateDepthClampEnable(bool enable) { if (_pipeline._stateCache.depthClampEnable != enable) { +#if !defined(USE_GLES) if (enable) { glEnable(GL_DEPTH_CLAMP); } else { glDisable(GL_DEPTH_CLAMP); } (void)CHECK_GL_ERROR(); - _pipeline._stateCache.depthClampEnable = enable; +#endif } } @@ -132,6 +138,7 @@ void GLBackend::do_setStateScissorEnable(bool enable) { void GLBackend::do_setStateMultisampleEnable(bool enable) { if (_pipeline._stateCache.multisampleEnable != enable) { +#if !defined(USE_GLES) if (enable) { glEnable(GL_MULTISAMPLE); } else { @@ -140,11 +147,13 @@ void GLBackend::do_setStateMultisampleEnable(bool enable) { (void)CHECK_GL_ERROR(); _pipeline._stateCache.multisampleEnable = enable; +#endif } } void GLBackend::do_setStateAntialiasedLineEnable(bool enable) { if (_pipeline._stateCache.antialisedLineEnable != enable) { +#if !defined(USE_GLES) if (enable) { glEnable(GL_LINE_SMOOTH); } else { @@ -153,6 +162,7 @@ void GLBackend::do_setStateAntialiasedLineEnable(bool enable) { (void)CHECK_GL_ERROR(); _pipeline._stateCache.antialisedLineEnable = enable; +#endif } } @@ -160,13 +170,17 @@ void GLBackend::do_setStateDepthBias(Vec2 bias) { if ((bias.x != _pipeline._stateCache.depthBias) || (bias.y != _pipeline._stateCache.depthBiasSlopeScale)) { if ((bias.x != 0.0f) || (bias.y != 0.0f)) { glEnable(GL_POLYGON_OFFSET_FILL); +#if !defined(USE_GLES) glEnable(GL_POLYGON_OFFSET_LINE); glEnable(GL_POLYGON_OFFSET_POINT); +#endif glPolygonOffset(bias.x, bias.y); } else { glDisable(GL_POLYGON_OFFSET_FILL); +#if !defined(USE_GLES) glDisable(GL_POLYGON_OFFSET_LINE); glDisable(GL_POLYGON_OFFSET_POINT); +#endif } (void)CHECK_GL_ERROR(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLBuffer.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBuffer.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLBuffer.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBuffer.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLBuffer.h b/libraries/gpu-gl-common/src/gpu/gl/GLBuffer.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLBuffer.h rename to libraries/gpu-gl-common/src/gpu/gl/GLBuffer.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLFramebuffer.cpp similarity index 96% rename from libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLFramebuffer.cpp index 2ac7e9d060..8d5fb6b2ef 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLFramebuffer.cpp @@ -33,14 +33,18 @@ bool GLFramebuffer::checkStatus() const { case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT."; break; + case GL_FRAMEBUFFER_UNSUPPORTED: + qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; + break; +#if !defined(USE_GLES) case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER."; break; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER."; break; - case GL_FRAMEBUFFER_UNSUPPORTED: - qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; +#endif + default: break; } return false; diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gl-common/src/gpu/gl/GLFramebuffer.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h rename to libraries/gpu-gl-common/src/gpu/gl/GLFramebuffer.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLInputFormat.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLInputFormat.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLInputFormat.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLInputFormat.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLInputFormat.h b/libraries/gpu-gl-common/src/gpu/gl/GLInputFormat.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLInputFormat.h rename to libraries/gpu-gl-common/src/gpu/gl/GLInputFormat.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLPipeline.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLPipeline.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLPipeline.h b/libraries/gpu-gl-common/src/gpu/gl/GLPipeline.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLPipeline.h rename to libraries/gpu-gl-common/src/gpu/gl/GLPipeline.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLQuery.h b/libraries/gpu-gl-common/src/gpu/gl/GLQuery.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLQuery.h rename to libraries/gpu-gl-common/src/gpu/gl/GLQuery.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLShader.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLShader.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLShader.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.h b/libraries/gpu-gl-common/src/gpu/gl/GLShader.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLShader.h rename to libraries/gpu-gl-common/src/gpu/gl/GLShader.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLShared.cpp similarity index 97% rename from libraries/gpu-gl/src/gpu/gl/GLShared.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLShared.cpp index dc1c8036b0..9ba85b8418 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLShared.cpp @@ -24,6 +24,7 @@ namespace gpu { namespace gl { gpu::Size getFreeDedicatedMemory() { Size result { 0 }; +#if !defined(USE_GLES) static bool nvidiaMemorySupported { true }; static bool atiMemorySupported { true }; if (nvidiaMemorySupported) { @@ -45,6 +46,7 @@ gpu::Size getFreeDedicatedMemory() { atiMemorySupported = false; } } +#endif return result; } @@ -144,6 +146,9 @@ State::BlendArg blendArgFromGL(GLenum blendArg) { void getCurrentGLState(State::Data& state) { { +#if defined(USE_GLES) + state.fillMode = State::FILL_FACE; +#else GLint modes[2]; glGetIntegerv(GL_POLYGON_MODE, modes); if (modes[0] == GL_FILL) { @@ -155,6 +160,7 @@ void getCurrentGLState(State::Data& state) { state.fillMode = State::FILL_POINT; } } +#endif } { if (glIsEnabled(GL_CULL_FACE)) { @@ -169,10 +175,16 @@ void getCurrentGLState(State::Data& state) { GLint winding; glGetIntegerv(GL_FRONT_FACE, &winding); state.frontFaceClockwise = (winding == GL_CW); - state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP); - state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST); +#if defined(USE_GLES) + state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE_EXT); + state.antialisedLineEnable = false; + state.depthClampEnable = false; +#else state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE); state.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH); + state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP); +#endif + state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST); } { if (glIsEnabled(GL_POLYGON_OFFSET_FILL)) { @@ -269,6 +281,7 @@ void getCurrentGLState(State::Data& state) { (void)CHECK_GL_ERROR(); } + void serverWait() { auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); assert(fence); diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.h b/libraries/gpu-gl-common/src/gpu/gl/GLShared.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLShared.h rename to libraries/gpu-gl-common/src/gpu/gl/GLShared.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLState.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLState.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLState.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLState.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLState.h b/libraries/gpu-gl-common/src/gpu/gl/GLState.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLState.h rename to libraries/gpu-gl-common/src/gpu/gl/GLState.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.cpp similarity index 91% rename from libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.cpp index 560b7337c7..99a9afb4c4 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.cpp @@ -11,15 +11,81 @@ using namespace gpu; using namespace gpu::gl; +#if defined(USE_GLES) +// Missing GL formats +#define GL_R16 GL_R16_EXT +#define GL_R16_SNORM GL_R16_SNORM_EXT +#define GL_RG16 GL_RG16_EXT +#define GL_RG16_SNORM GL_RG16_SNORM_EXT +#define GL_RGBA2 GL_RGBA8 +#define GL_RGBA16 GL_RGBA16_EXT +#define GL_RGBA16_SNORM GL_RGBA16_SNORM_EXT +#define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_OES +#define GL_SLUMINANCE8_EXT GL_SLUMINANCE8_NV +// Missing GL compressed formats +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#endif + bool GLTexelFormat::isCompressed() const { switch (internalFormat) { case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + + case GL_COMPRESSED_R11_EAC: + case GL_COMPRESSED_SIGNED_R11_EAC: + case GL_COMPRESSED_RG11_EAC: + case GL_COMPRESSED_SIGNED_RG11_EAC: + case GL_COMPRESSED_RGB8_ETC2: + case GL_COMPRESSED_SRGB8_ETC2: + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_RGBA8_ETC2_EAC: + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: case GL_COMPRESSED_RED_RGTC1: case GL_COMPRESSED_RG_RGTC2: case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: + +#if defined(USE_GLES) + case GL_COMPRESSED_RGBA_ASTC_4x4: + case GL_COMPRESSED_RGBA_ASTC_5x4: + case GL_COMPRESSED_RGBA_ASTC_5x5: + case GL_COMPRESSED_RGBA_ASTC_6x5: + case GL_COMPRESSED_RGBA_ASTC_6x6: + case GL_COMPRESSED_RGBA_ASTC_8x5: + case GL_COMPRESSED_RGBA_ASTC_8x6: + case GL_COMPRESSED_RGBA_ASTC_8x8: + case GL_COMPRESSED_RGBA_ASTC_10x5: + case GL_COMPRESSED_RGBA_ASTC_10x6: + case GL_COMPRESSED_RGBA_ASTC_10x8: + case GL_COMPRESSED_RGBA_ASTC_10x10: + case GL_COMPRESSED_RGBA_ASTC_12x10: + case GL_COMPRESSED_RGBA_ASTC_12x12: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12: +#endif + + return true; default: return false; @@ -390,7 +456,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (srcFormat.getSemantic()) { case gpu::BGRA: case gpu::SBGRA: +#if defined(USE_GLES) + texel.format = GL_RGBA; +#else texel.format = GL_BGRA; +#endif break; case gpu::RGB: case gpu::RGBA: @@ -427,8 +497,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (srcFormat.getSemantic()) { case gpu::BGRA: case gpu::SBGRA: +#if !defined(USE_GLES) texel.format = GL_BGRA; break; +#endif case gpu::RGB: case gpu::RGBA: case gpu::SRGB: @@ -478,6 +550,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; switch (dstFormat.getSemantic()) { + case gpu::RED: case gpu::RGB: case gpu::RGBA: @@ -788,8 +861,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (srcFormat.getSemantic()) { case gpu::BGRA: case gpu::SBGRA: +#if !defined(USE_GLES) texel.format = GL_BGRA; break; +#endif case gpu::RGB: case gpu::RGBA: case gpu::SRGB: diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h rename to libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLTexture.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLTexture.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLTexture.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLTexture.h rename to libraries/gpu-gl-common/src/gpu/gl/GLTexture.h diff --git a/libraries/gpu-gl/CMakeLists.txt b/libraries/gpu-gl/CMakeLists.txt index dc744e73f2..faddab8531 100644 --- a/libraries/gpu-gl/CMakeLists.txt +++ b/libraries/gpu-gl/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME gpu-gl) setup_hifi_library(Concurrent) -link_hifi_libraries(shared gl gpu) +link_hifi_libraries(shared gl gpu gpu-gl-common) if (UNIX) target_link_libraries(${TARGET_NAME} pthread) endif(UNIX) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp deleted file mode 100644 index b4e4656dc7..0000000000 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ /dev/null @@ -1,772 +0,0 @@ -// -// GLBackend.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 10/27/2014. -// 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 -#include -#include -#include -#include - -#include "../gl41/GL41Backend.h" -#include "../gl45/GL45Backend.h" - -#if defined(NSIGHT_FOUND) -#include "nvToolsExt.h" -#endif - -#include -#include -#include -#include - -#include "GLTexture.h" -#include "GLShader.h" - -using namespace gpu; -using namespace gpu::gl; - -static const QString DEBUG_FLAG("HIFI_DISABLE_OPENGL_45"); -static bool disableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); - -static GLBackend* INSTANCE{ nullptr }; - -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; - if (!disableOpenGL45 && version >= 0x0405) { - qCDebug(gpugllogging) << "Using OpenGL 4.5 backend"; - result = std::make_shared(); - } else { - qCDebug(gpugllogging) << "Using OpenGL 4.1 backend"; - result = std::make_shared(); - } - result->initInput(); - result->initTransform(); - result->initTextureManagementStage(); - - INSTANCE = result.get(); - void* voidInstance = &(*result); - qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); - return result; -} - -GLBackend& getBackend() { - if (!INSTANCE) { - INSTANCE = static_cast(qApp->property(hifi::properties::gl::BACKEND).value()); - } - return *INSTANCE; -} - -bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { - return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); -} - -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_setResourceBuffer), - (&::gpu::gl::GLBackend::do_setResourceTexture), - (&::gpu::gl::GLBackend::do_setResourceFramebufferSwapChainTexture), - - (&::gpu::gl::GLBackend::do_setFramebuffer), - (&::gpu::gl::GLBackend::do_setFramebufferSwapChain), - (&::gpu::gl::GLBackend::do_clearFramebuffer), - (&::gpu::gl::GLBackend::do_blit), - (&::gpu::gl::GLBackend::do_generateTextureMips), - - (&::gpu::gl::GLBackend::do_advance), - - (&::gpu::gl::GLBackend::do_beginQuery), - (&::gpu::gl::GLBackend::do_endQuery), - (&::gpu::gl::GLBackend::do_getQuery), - - (&::gpu::gl::GLBackend::do_resetStages), - - (&::gpu::gl::GLBackend::do_disableContextViewCorrection), - (&::gpu::gl::GLBackend::do_restoreContextViewCorrection), - (&::gpu::gl::GLBackend::do_disableContextStereo), - (&::gpu::gl::GLBackend::do_restoreContextStereo), - - (&::gpu::gl::GLBackend::do_runLambda), - - (&::gpu::gl::GLBackend::do_startNamedCall), - (&::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"; - qCDebug(gpugllogging, "V-Sync is %s\n", (::gl::getSwapInterval() > 0 ? "ON" : "OFF")); -#if THREADED_TEXTURE_BUFFERING - // This has to happen on the main thread in order to give the thread - // pool a reasonable parent object - GLVariableAllocationSupport::TransferJob::startBufferingThread(); -#endif - }); -} - -GLBackend::GLBackend() { - _pipeline._cameraCorrectionBuffer._buffer->flush(); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment); -} - - -GLBackend::~GLBackend() { - 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 - PROFILE_RANGE(render_gpu_gl_detail, "syncGPUBuffer"); - - for (auto& cached : batch._buffers._items) { - if (cached._data) { - syncGPUObject(*cached._data); - } - } - } - - { // Sync all the transform states - PROFILE_RANGE(render_gpu_gl_detail, "syncCPUTransform"); - _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_disableContextStereo: - _stereo._contextDisable = true; - break; - - case Batch::COMMAND_restoreContextStereo: - _stereo._contextDisable = false; - break; - - case Batch::COMMAND_setViewportTransform: - case Batch::COMMAND_setViewTransform: - case Batch::COMMAND_setProjectionTransform: { - CommandCall call = _commandCalls[(*command)]; - (this->*(call))(batch, *offset); - break; - } - - default: - break; - } - command++; - offset++; - } - } - - { // Sync the transform buffers - PROFILE_RANGE(render_gpu_gl_detail, "syncGPUTransform"); - 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(); - - CommandCall call = _commandCalls[(*command)]; - (this->*(call))(batch, *offset); - break; - } - default: { - CommandCall call = _commandCalls[(*command)]; - (this->*(call))(batch, *offset); - break; - } - } - - command++; - offset++; - } -} - -void GLBackend::render(const Batch& batch) { - _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_detail, "Transfer"); - renderPassTransfer(batch); - } - -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - if (_stereo.isStereo()) { - glEnable(GL_CLIP_DISTANCE0); - } -#endif - { - PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render"); - renderPassDraw(batch); - } -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - if (_stereo.isStereo()) { - glDisable(GL_CLIP_DISTANCE0); - } -#endif - // Restore the saved stereo state for the next batch - _stereo._enable = savedStereo; -} - - -void GLBackend::syncCache() { - PROFILE_RANGE(render_gpu_gl_detail, __FUNCTION__); - - syncTransformStateCache(); - syncPipelineStateCache(); - syncInputStateCache(); - syncOutputStateCache(); -} - -#ifdef GPU_STEREO_DRAWCALL_DOUBLED -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); -#endif -#else - _transform.bindCurrentCamera(side); -#endif - -} -#else -#endif - -void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { - resetStages(); -} - -void GLBackend::do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) { - _transform._viewCorrectionEnabled = false; -} - -void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) { - _transform._viewCorrectionEnabled = true; -} - -void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) { - -} - -void GLBackend::do_restoreContextStereo(const Batch& batch, size_t paramOffset) { - -} - -void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) { - std::function f = batch._lambdas.get(batch._params[paramOffset]._uint); - f(); -} - -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) { - if (trace_render_gpu_gl_detail().isDebugEnabled()) { - auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); - profileRanges.push_back(name); -#if defined(NSIGHT_FOUND) - nvtxRangePush(name.c_str()); -#endif - } -} - -void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) { - if (trace_render_gpu_gl_detail().isDebugEnabled()) { - 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 -#ifdef GPU_STEREO_CAMERA_BUFFER -#define GET_UNIFORM_LOCATION(shaderUniformLoc) ((_pipeline._programShader) ? _pipeline._programShader->getUniformLocation(shaderUniformLoc, (GLShader::Version) isStereo()) : -1) -#else -#define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc -#endif - -void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // 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(); - - glUniform1i( - 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); - // Color has been changed and is not white. To prevent colors from bleeding - // between different objects, we need to set the _hadColorAttribute flag - // as if a previous render call had potential colors - _input._hadColorAttribute = (newColor != glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); - } - (void)CHECK_GL_ERROR(); -} - -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 { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__) - { - 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()); - } - } - - GLVariableAllocationSupport::manageMemory(); - GLVariableAllocationSupport::_frameTexturesCreated = 0; - Texture::KtxStorage::releaseOpenKtxFiles(); -} - -void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset) { - auto invCorrection = glm::inverse(correction); - auto invPrevView = glm::inverse(prevRenderView); - _transform._correction.prevView = (reset ? Mat4() : prevRenderView); - _transform._correction.prevViewInverse = (reset ? Mat4() : invPrevView); - _transform._correction.correction = correction; - _transform._correction.correctionInverse = invCorrection; - _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); - _pipeline._cameraCorrectionBuffer._buffer->flush(); -} diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp deleted file mode 100644 index cac214b01e..0000000000 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// -// 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); - } - glBindVertexArray(_input._defaultVAO); - (void) CHECK_GL_ERROR(); -} - -void GLBackend::killInput() { - 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 - glBindVertexArray(_input._defaultVAO); -} - -void GLBackend::resetInputStage() { - // Reset index buffer - _input._indexBufferType = UINT32; - _input._indexBufferOffset = 0; - _input._indexBuffer.reset(); - 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(); -} - diff --git a/libraries/gpu-gl/src/gpu/gl/GLDesktopBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLDesktopBackend.cpp new file mode 100644 index 0000000000..72a76f8f90 --- /dev/null +++ b/libraries/gpu-gl/src/gpu/gl/GLDesktopBackend.cpp @@ -0,0 +1,63 @@ +// +// GLBackend.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 10/27/2014. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include + +#include + +#include + +#include +#include +#include + +#include "../gl41/GL41Backend.h" +#include "../gl45/GL45Backend.h" + +using namespace gpu; +using namespace gpu::gl; + +static const QString DEBUG_FLAG("HIFI_DISABLE_OPENGL_45"); +static bool disableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); + +static GLBackend* INSTANCE{ nullptr }; + +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; + if (!disableOpenGL45 && version >= 0x0405) { + qCDebug(gpugllogging) << "Using OpenGL 4.5 backend"; + result = std::make_shared(); + } else { + qCDebug(gpugllogging) << "Using OpenGL 4.1 backend"; + result = std::make_shared(); + } + result->initInput(); + result->initTransform(); + result->initTextureManagementStage(); + + INSTANCE = result.get(); + void* voidInstance = &(*result); + qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); + return result; +} + +GLBackend& getBackend() { + if (!INSTANCE) { + INSTANCE = static_cast(qApp->property(hifi::properties::gl::BACKEND).value()); + } + return *INSTANCE; +} + +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { + return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); +} diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 42926fdb1c..3c59781f78 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -13,8 +13,8 @@ #include -#include "../gl/GLBackend.h" -#include "../gl/GLTexture.h" +#include +#include #define GPU_CORE_41 410 #define GPU_CORE_43 430 diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp index eb66cc4544..62ade673b4 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL41Backend.h" -#include "../gl/GLBuffer.h" +#include namespace gpu { namespace gl41 { diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp index 195b155bf3..a5ef2d92e1 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp @@ -12,8 +12,8 @@ #include -#include "../gl/GLFramebuffer.h" -#include "../gl/GLTexture.h" +#include +#include namespace gpu { namespace gl41 { diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp index f712550973..0f75c29f38 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp @@ -10,7 +10,7 @@ // #include "GL41Backend.h" -#include "../gl/GLQuery.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp index ff9ddaae63..35bfafdc50 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL41Backend.h" -#include "../gl/GLShader.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 2834a8463c..61c3da391b 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -13,7 +13,7 @@ #include #include -#include "../gl/GLTexelFormat.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 1a4b63d35f..b90c0b1857 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -12,8 +12,9 @@ #ifndef hifi_gpu_45_GL45Backend_h #define hifi_gpu_45_GL45Backend_h -#include "../gl/GLBackend.h" -#include "../gl/GLTexture.h" +#include +#include + #include #define INCREMENTAL_TRANSFER 0 diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp index b4a6410612..2afbea3876 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL45Backend.h" -#include "../gl/GLBuffer.h" +#include namespace gpu { namespace gl45 { using namespace gpu::gl; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp index 4a43fc988c..34bf6774f7 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL45Backend.h" -#include "../gl/GLShared.h" +#include using namespace gpu; using namespace gpu::gl45; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp index 9648af9b21..ca53d6c624 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL45Backend.h" -#include "../gl/GLFramebuffer.h" -#include "../gl/GLTexture.h" +#include +#include #include diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp index df81d7914e..62f87ed913 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp @@ -10,7 +10,7 @@ // #include "GL45Backend.h" -#include "../gl/GLQuery.h" +#include namespace gpu { namespace gl45 { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp index c2490524ae..c17a4a1524 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL45Backend.h" -#include "../gl/GLShader.h" +#include //#include using namespace gpu; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 1d415c7ca3..5e64e1845e 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -20,7 +20,7 @@ #include #include -#include "../gl/GLTexelFormat.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 0f1de0f868..d2d7b2bf55 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -21,7 +21,7 @@ #include #include -#include "../gl/GLTexelFormat.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gles/CMakeLists.txt b/libraries/gpu-gles/CMakeLists.txt index ea69919f6d..82bf670781 100644 --- a/libraries/gpu-gles/CMakeLists.txt +++ b/libraries/gpu-gles/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME gpu-gles) setup_hifi_library(Gui Concurrent) -link_hifi_libraries(shared gl gpu) +link_hifi_libraries(shared gl gpu gpu-gl-common) GroupSources("src") target_opengl() diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.h b/libraries/gpu-gles/src/gpu/gl/GLBackend.h deleted file mode 100644 index 081bd78e85..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.h +++ /dev/null @@ -1,482 +0,0 @@ -// -// 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_SIMPLE -//#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER -//#define GPU_STEREO_TECHNIQUE_INSTANCED - - -// Let these be configured by the one define picked above -#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE -#define GPU_STEREO_DRAWCALL_DOUBLED -#endif - -#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER -#define GPU_STEREO_DRAWCALL_DOUBLED -#define GPU_STEREO_CAMERA_BUFFER -#endif - -#ifdef GPU_STEREO_TECHNIQUE_INSTANCED -#define GPU_STEREO_DRAWCALL_INSTANCED -#define GPU_STEREO_CAMERA_BUFFER -#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(), const Shader::CompilationHandler& handler = nullptr); - - virtual ~GLBackend(); - - void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset = false); - 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_BUFFERS = 16; - size_t getMaxNumResourceBuffers() const { return MAX_NUM_RESOURCE_BUFFERS; } - static const int MAX_NUM_RESOURCE_TEXTURES = 16; - size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; } - - // Draw Stage - virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0; - virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0; - virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0; - - // 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_setResourceBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; - virtual void do_setResourceFramebufferSwapChainTexture(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_setFramebufferSwapChain(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; - - virtual void do_advance(const Batch& batch, size_t paramOffset) final; - - // Query section - virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final; - virtual void do_endQuery(const Batch& batch, size_t paramOffset) final; - virtual void do_getQuery(const Batch& batch, size_t paramOffset) final; - - // Reset stages - virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; - - - virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final; - virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final; - - virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final; - virtual void do_restoreContextStereo(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) final; - virtual GLuint getBufferID(const Buffer& buffer) = 0; - virtual GLuint getQueryID(const QueryPointer& query) = 0; - - virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; - virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; - virtual GLTexture* syncGPUObject(const TexturePointer& texture); - virtual GLQuery* syncGPUObject(const Query& query) = 0; - //virtual bool isTextureReady(const TexturePointer& texture); - - 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; - - 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); - -#ifdef GPU_STEREO_DRAWCALL_DOUBLED - void setupStereoSide(int side); -#endif - - virtual void setResourceTexture(unsigned int slot, const TexturePointer& resourceTexture); - virtual void setFramebuffer(const FramebufferPointer& framebuffer); - virtual void initInput() final; - virtual void killInput() final; - virtual void syncInputStateCache() final; - virtual void resetInputStage(); - virtual void updateInput() = 0; - - struct InputStageState { - bool _invalidFormat { true }; - bool _hadColorAttribute{ 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(); - virtual void updateTransform(const Batch& batch) = 0; - virtual void resetTransformStage(); - - // Allows for correction of the camera pose to account for changes - // between the time when a was recorded and the time(s) when it is - // executed - struct CameraCorrection { - Mat4 correction; - Mat4 correctionInverse; - Mat4 prevView; - Mat4 prevViewInverse; - }; - - 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; - bool _viewCorrectionEnabled{ true }; - - - Mat4 _projection; - Vec4i _viewport { 0, 0, 1, 1 }; - Vec2 _depthRange { 0.0f, 1.0f }; - bool _invalidView { false }; - bool _invalidProj { false }; - bool _invalidViewport { false }; - - bool _enabledDrawcallInfoBuffer{ false }; - - 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 bind/unbind call with the current gpu::Buffer cached at slot s - // This is using different gl object depending on the gl version - virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0; - virtual void releaseResourceBuffer(uint32_t slot) = 0; - - // 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 _buffers; - 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 )) }; - BufferView _cameraCorrectionBufferIdentity { 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(); - _cameraCorrectionBufferIdentity.edit() = CameraCorrection(); - _cameraCorrectionBufferIdentity._buffer->flush(); - } - } _pipeline; - - // Backend dependant compilation of the shader - virtual GLShader* compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler); - virtual GLShader* compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler); - virtual std::string getBackendShaderHeader() const; - virtual void makeProgramBindings(ShaderObject& shaderObject); - class ElementResource { - public: - gpu::Element _element; - uint16 _resource; - ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} - }; - ElementResource getFormatFromGLUniform(GLenum gltype); - static const GLint UNUSED_SLOT {-1}; - static bool isUnusedSlot(GLint binding) { return (binding == UNUSED_SLOT); } - virtual int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, - Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers); - virtual int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); - virtual int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers) = 0; - virtual int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); - virtual int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); - - - // 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; - friend class GLShader; -}; - -} } - -#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp deleted file mode 100644 index 0f36f86c88..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// -// 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); - setFramebuffer(framebuffer); -} - -void GLBackend::do_setFramebufferSwapChain(const Batch& batch, size_t paramOffset) { - auto swapChain = batch._swapChains.get(batch._params[paramOffset]._uint); - if (swapChain) { - auto index = batch._params[paramOffset + 1]._uint; - FramebufferPointer framebuffer = static_cast(swapChain.get())->get(index); - setFramebuffer(framebuffer); - } -} - -void GLBackend::setFramebuffer(const FramebufferPointer& framebuffer) { - 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_advance(const Batch& batch, size_t paramOffset) { - auto ringbuffer = batch._swapChains.get(batch._params[paramOffset]._uint); - if (ringbuffer) { - ringbuffer->advance(); - } -} - -void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { - if (_stereo.isStereo() && !_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; - bool restoreStencilMask = false; - uint8_t cacheStencilMask = 0xFF; - if (masks & Framebuffer::BUFFER_STENCIL) { - glClearStencil(stencil); - glmask |= GL_STENCIL_BUFFER_BIT; - cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront(); - if (cacheStencilMask != 0xFF) { - restoreStencilMask = true; - glStencilMask(0xFF); - } - } - - bool restoreDepthMask = false; - if (masks & Framebuffer::BUFFER_DEPTH) { - glClearDepthf(depth); - glmask |= GL_DEPTH_BUFFER_BIT; - - bool cacheDepthMask = _pipeline._stateCache.depthTest.getWriteMask(); - 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 Stencil write mask - if (restoreStencilMask) { - glStencilMask(cacheStencilMask); - } - - // 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))) { - qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : srcFramebuffer is too small to provide the region queried"; - return; - } - } - - if ((destImage.width() < region.z) || (destImage.height() < region.w)) { - qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : destImage is too small to receive the region of the framebuffer"; - return; - } - - GLenum format = GL_RGBA; - if (destImage.format() != QImage::Format_ARGB32) { - qCWarning(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 deleted file mode 100644 index 7fdf6eca41..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp +++ /dev/null @@ -1,316 +0,0 @@ -// -// 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) { - _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) { - gl::GLBuffer* cameraCorrectionBuffer = nullptr; - if (_transform._viewCorrectionEnabled) { - cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); - } else { - cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBufferIdentity._buffer); - } - glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection)); - } - (void) CHECK_GL_ERROR(); - _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; - if (slot >(GLuint)MAX_NUM_UNIFORM_BUFFERS) { - qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot << " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers(); - return; - } - BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); - GLintptr rangeStart = batch._params[paramOffset + 1]._uint; - GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; - - 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._buffers.size(); i++) { - releaseResourceBuffer(i); - } - for (uint32_t i = 0; i < _resource._textures.size(); i++) { - releaseResourceTexture(i); - } -} - -void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) { - GLuint slot = batch._params[paramOffset + 1]._uint; - if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) { - qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot << " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers(); - return; - } - - auto resourceBuffer = batch._buffers.get(batch._params[paramOffset + 0]._uint); - - if (!resourceBuffer) { - releaseResourceBuffer(slot); - return; - } - // check cache before thinking - if (_resource._buffers[slot] == resourceBuffer) { - return; - } - - // One more True Buffer bound - _stats._RSNumResourceBufferBounded++; - - // If successful bind then cache it - if (bindResourceBuffer(slot, resourceBuffer)) { - _resource._buffers[slot] = resourceBuffer; - } else { // else clear slot and cache - releaseResourceBuffer(slot); - return; - } -} - -void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { - GLuint slot = batch._params[paramOffset + 1]._uint; - if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) { - qCDebug(gpugllogging) << "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; - } - setResourceTexture(slot, resourceTexture); -} - -void GLBackend::do_setResourceFramebufferSwapChainTexture(const Batch& batch, size_t paramOffset) { - GLuint slot = batch._params[paramOffset + 1]._uint; - if (slot >= (GLuint)MAX_NUM_RESOURCE_TEXTURES) { - qCDebug(gpugllogging) << "GLBackend::do_setResourceFramebufferSwapChainTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures(); - return; - } - - SwapChainPointer swapChain = batch._swapChains.get(batch._params[paramOffset + 0]._uint); - - if (!swapChain) { - releaseResourceTexture(slot); - return; - } - auto index = batch._params[paramOffset + 2]._uint; - auto renderBufferSlot = batch._params[paramOffset + 3]._uint; - FramebufferPointer resourceFramebuffer = static_cast(swapChain.get())->get(index); - TexturePointer resourceTexture = resourceFramebuffer->getRenderBuffer(renderBufferSlot); - - setResourceTexture(slot, resourceTexture); -} - -void GLBackend::setResourceTexture(unsigned int slot, const TexturePointer& resourceTexture) { - // 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 += (int)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 deleted file mode 100644 index 43c8f8f465..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// 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) { -#if !defined(USE_GLES) - 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(); - } -#endif -} - -void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { -#if !defined(USE_GLES) - auto query = batch._queries.get(batch._params[paramOffset]._uint); - GLQuery* glquery = syncGPUObject(*query); - if (glquery) { - 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(); - } -#endif -} - -void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { -#if !defined(USE_GLES) - auto query = batch._queries.get(batch._params[paramOffset]._uint); - if (glGetQueryObjectui64vEXT == NULL) - return; - 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(); - } -#endif -} - -void GLBackend::resetQueryStage() { -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp deleted file mode 100644 index 677bba97ca..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp +++ /dev/null @@ -1,583 +0,0 @@ -// -// Created by Gabriel Calero & Cristian Duarte on 2017/12/28 -// Copyright 2013-2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#include "GLBackend.h" -#include "GLShader.h" -#include - -using namespace gpu; -using namespace gpu::gl; - -// GLSL version -std::string GLBackend::getBackendShaderHeader() const { - return std::string("#version 310 es"); -} - -// Shader domain -static const size_t NUM_SHADER_DOMAINS = 2; - -// GL Shader type enums -// Must match the order of type specified in gpu::Shader::Type -static const std::array SHADER_DOMAINS{ { - GL_VERTEX_SHADER, - GL_FRAGMENT_SHADER, - // GL_GEOMETRY_SHADER, -} }; - -// Domain specific defines -// Must match the order of type specified in gpu::Shader::Type -static const std::array DOMAIN_DEFINES{ { - "#define GPU_VERTEX_SHADER", - "#define GPU_PIXEL_SHADER", - // "#define GPU_GEOMETRY_SHADER", -} }; - -// Stereo specific defines -static const std::string stereoVersion{ -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN" -#endif -#ifdef GPU_STEREO_DRAWCALL_DOUBLED -#ifdef GPU_STEREO_CAMERA_BUFFER - "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED" -#else - "#define GPU_TRANSFORM_IS_STEREO" -#endif -#endif -}; - -// Versions specific of the shader -static const std::array VERSION_DEFINES { { - "", - stereoVersion -} }; - -GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler) { - // Any GLSLprogram ? normally yes... - const std::string& shaderSource = shader.getSource().getCode(); - GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; - GLShader::ShaderObjects shaderObjects; - Shader::CompilationLogs compilationLogs(GLShader::NumVersions); - - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& shaderObject = shaderObjects[version]; - - std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version] - + "\n#extension GL_EXT_texture_buffer : enable" - + "\nprecision lowp float; // check precision 2" - + "\nprecision lowp samplerBuffer;" - + "\nprecision lowp sampler2DShadow;"; - if (handler) { - bool retest = true; - std::string currentSrc = shaderSource; - // When a Handler is specified, we can try multiple times to build the shader and let the handler change the source if the compilation fails. - // The retest bool is set to false as soon as the compilation succeed to wexit the while loop. - // The handler tells us if we should retry or not while returning a modified version of the source. - while (retest) { - bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderDefines, shaderObject.glshader, compilationLogs[version].message); - compilationLogs[version].compiled = result; - if (!result) { - std::string newSrc; - retest = handler(shader, currentSrc, compilationLogs[version], newSrc); - currentSrc = newSrc; - } else { - retest = false; - } - } - } else { - compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message); - } - - if (!compilationLogs[version].compiled) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str(); - shader.setCompilationLogs(compilationLogs); - return nullptr; - } - } - // Compilation feedback - shader.setCompilationLogs(compilationLogs); - - // So far so good, the shader is created successfully - GLShader* object = new GLShader(this->shared_from_this()); - object->_shaderObjects = shaderObjects; - - return object; -} - -GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler) { - if (!program.isProgram()) { - return nullptr; - } - - GLShader::ShaderObjects programObjects; - - Shader::CompilationLogs compilationLogs(GLShader::NumVersions); - - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& programObject = programObjects[version]; - - // Let's go through every shaders and make sure they are ready to go - std::vector< GLuint > shaderGLObjects; - for (auto subShader : program.getShaders()) { - auto object = GLShader::sync((*this), *subShader, handler); - if (object) { - shaderGLObjects.push_back(object->_shaderObjects[version].glshader); - } else { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; - program.setCompilationLogs(compilationLogs); - return nullptr; - } - } - - GLuint glprogram = ::gl::compileProgram(shaderGLObjects, compilationLogs[version].message, compilationLogs[version].binary); - if (glprogram == 0) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); - program.setCompilationLogs(compilationLogs); - return nullptr; - } - - programObject.glprogram = glprogram; - - makeProgramBindings(programObject); - } - // Compilation feedback - program.setCompilationLogs(compilationLogs); - - // So far so good, the program versions have all been created successfully - GLShader* object = new GLShader(this->shared_from_this()); - object->_shaderObjects = programObjects; - - return object; -} - -GLBackend::ElementResource GLBackend::getFormatFromGLUniform(GLenum gltype) { - switch (gltype) { - case GL_FLOAT: - return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC2: - return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC3: - return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC4: - return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - - case GL_INT: - return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC2: - return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC3: - return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC4: - return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); - - case GL_UNSIGNED_INT: - return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC2: - return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC3: - return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC4: - return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); - - case GL_BOOL: - return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC2: - return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC3: - return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC4: - return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); - - case GL_FLOAT_MAT2: - return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT3: - return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT4: - return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - - //{GL_FLOAT_MAT2x3 mat2x3}, - //{GL_FLOAT_MAT2x4 mat2x4}, - //{GL_FLOAT_MAT3x2 mat3x2}, - //{GL_FLOAT_MAT3x4 mat3x4}, - //{GL_FLOAT_MAT4x2 mat4x2}, - //{GL_FLOAT_MAT4x3 mat4x3}, - //{GL_DOUBLE_MAT2 dmat2}, - //{GL_DOUBLE_MAT3 dmat3}, - //{GL_DOUBLE_MAT4 dmat4}, - //{GL_DOUBLE_MAT2x3 dmat2x3}, - //{GL_DOUBLE_MAT2x4 dmat2x4}, - //{GL_DOUBLE_MAT3x2 dmat3x2}, - //{GL_DOUBLE_MAT3x4 dmat3x4}, - //{GL_DOUBLE_MAT4x2 dmat4x2}, - //{GL_DOUBLE_MAT4x3 dmat4x3}, - - case GL_SAMPLER_2D: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); - - case GL_SAMPLER_3D: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); - case GL_SAMPLER_CUBE: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_SAMPLER_2D_MULTISAMPLE: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_SAMPLER_2D_ARRAY: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - - case GL_SAMPLER_2D_SHADOW: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); - case GL_SAMPLER_CUBE_SHADOW: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); - - case GL_SAMPLER_2D_ARRAY_SHADOW: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); - - // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, - // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, - - case GL_SAMPLER_BUFFER: - return ElementResource(Element(SCALAR, gpu::FLOAT, RESOURCE_BUFFER), Resource::BUFFER); - - // {GL_SAMPLER_2D_RECT sampler2DRect}, - // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, - - case GL_INT_SAMPLER_2D: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_2D_MULTISAMPLE: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_3D: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); - case GL_INT_SAMPLER_CUBE: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_INT_SAMPLER_2D_ARRAY: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - - // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, - // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, - - case GL_UNSIGNED_INT_SAMPLER_2D: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_3D: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); - case GL_UNSIGNED_INT_SAMPLER_CUBE: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - //{GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, - //{GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, - - //{GL_IMAGE_1D image1D}, - //{GL_IMAGE_2D image2D}, - //{GL_IMAGE_3D image3D}, - //{GL_IMAGE_2D_RECT image2DRect}, - //{GL_IMAGE_CUBE imageCube}, - //{GL_IMAGE_BUFFER imageBuffer}, - //{GL_IMAGE_1D_ARRAY image1DArray}, - //{GL_IMAGE_2D_ARRAY image2DArray}, - //{GL_IMAGE_2D_MULTISAMPLE image2DMS}, - //{GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, - //{GL_INT_IMAGE_1D iimage1D}, - //{GL_INT_IMAGE_2D iimage2D}, - //{GL_INT_IMAGE_3D iimage3D}, - //{GL_INT_IMAGE_2D_RECT iimage2DRect}, - //{GL_INT_IMAGE_CUBE iimageCube}, - //{GL_INT_IMAGE_BUFFER iimageBuffer}, - //{GL_INT_IMAGE_1D_ARRAY iimage1DArray}, - //{GL_INT_IMAGE_2D_ARRAY iimage2DArray}, - //{GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, - //{GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, - //{GL_UNSIGNED_INT_IMAGE_1D uimage1D}, - //{GL_UNSIGNED_INT_IMAGE_2D uimage2D}, - //{GL_UNSIGNED_INT_IMAGE_3D uimage3D}, - //{GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, - //{GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot - - //{GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, - //{GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, - //{GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, - //{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, - //{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, - //{GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} -#if 0 - case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); - case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); - case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); -#endif - - default: - return ElementResource(Element(), Resource::BUFFER); - } -}; - -int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, - Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { - GLint uniformsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); - - for (int i = 0; i < uniformsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - GLint location = glGetUniformLocation(glprogram, name); - const GLint INVALID_UNIFORM_LOCATION = -1; - - // Try to make sense of the gltype - auto elementResource = getFormatFromGLUniform(type); - - // The uniform as a standard var type - if (location != INVALID_UNIFORM_LOCATION) { - // Let's make sure the name doesn't contains an array element - std::string sname(name); - auto foundBracket = sname.find_first_of('['); - if (foundBracket != std::string::npos) { - // std::string arrayname = sname.substr(0, foundBracket); - - if (sname[foundBracket + 1] == '0') { - sname = sname.substr(0, foundBracket); - } else { - // skip this uniform since it's not the first element of an array - continue; - } - } - - if (elementResource._resource == Resource::BUFFER) { - uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); - } else { - // For texture/Sampler, the location is the actual binding value - GLint binding = -1; - glGetUniformiv(glprogram, location, &binding); - - auto requestedBinding = slotBindings.find(std::string(sname)); - if (requestedBinding != slotBindings.end()) { - if (binding != (*requestedBinding)._location) { - binding = (*requestedBinding)._location; - for (auto i = 0; i < size; i++) { - // If we are working with an array of textures, reserve for each elemet - glProgramUniform1i(glprogram, location + i, binding + i); - } - } - } - - textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); - samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); - } - } - } - - return uniformsCount; -} - -int GLBackend::makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { - GLint buffersCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); - - // fast exit - if (buffersCount == 0) { - return 0; - } - - GLint maxNumUniformBufferSlots = 0; - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); - std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); - - struct UniformBlockInfo { - using Vector = std::vector; - const GLuint index{ 0 }; - const std::string name; - GLint binding{ -1 }; - GLint size{ 0 }; - - static std::string getName(GLuint glprogram, GLuint i) { - static const GLint NAME_LENGTH = 256; - GLint length = 0; - GLchar nameBuffer[NAME_LENGTH]; - glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); - glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); - return std::string(nameBuffer); - } - - UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { - glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); - glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); - } - }; - - UniformBlockInfo::Vector uniformBlocks; - uniformBlocks.reserve(buffersCount); - for (int i = 0; i < buffersCount; i++) { - uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); - } - - for (auto& info : uniformBlocks) { - auto requestedBinding = slotBindings.find(info.name); - if (requestedBinding != slotBindings.end()) { - info.binding = (*requestedBinding)._location; - glUniformBlockBinding(glprogram, info.index, info.binding); - uniformBufferSlotMap[info.binding] = info.index; - } - } - - for (auto& info : uniformBlocks) { - if (slotBindings.count(info.name)) { - continue; - } - - // If the binding is 0, or the binding maps to an already used binding - if (info.binding == 0 || !isUnusedSlot(uniformBufferSlotMap[info.binding])) { - // If no binding was assigned then just do it finding a free slot - auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), GLBackend::isUnusedSlot); - if (slotIt != uniformBufferSlotMap.end()) { - info.binding = slotIt - uniformBufferSlotMap.begin(); - glUniformBlockBinding(glprogram, info.index, info.binding); - } else { - // This should neve happen, an active ubo cannot find an available slot among the max available?! - info.binding = -1; - } - } - - uniformBufferSlotMap[info.binding] = info.index; - } - - for (auto& info : uniformBlocks) { - static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); - buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); - } - return buffersCount; -} - -int GLBackend::makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { - GLint inputsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); - - for (int i = 0; i < inputsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - - GLint binding = glGetAttribLocation(glprogram, name); - - auto elementResource = getFormatFromGLUniform(type); - inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); - } - - return inputsCount; -} - -int GLBackend::makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { - /* GLint outputsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); - - for (int i = 0; i < inputsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - - auto element = getFormatFromGLUniform(type); - outputs.insert(Shader::Slot(name, i, element)); - } - */ - return 0; //inputsCount; -} - -void GLBackend::makeProgramBindings(ShaderObject& shaderObject) { - if (!shaderObject.glprogram) { - return; - } - GLuint glprogram = shaderObject.glprogram; - GLint loc = -1; - - //Check for gpu specific attribute slotBindings - loc = glGetAttribLocation(glprogram, "inPosition"); - if (loc >= 0 && loc != gpu::Stream::POSITION) { - glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); - } - - loc = glGetAttribLocation(glprogram, "inNormal"); - if (loc >= 0 && loc != gpu::Stream::NORMAL) { - glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); - } - - loc = glGetAttribLocation(glprogram, "inColor"); - if (loc >= 0 && loc != gpu::Stream::COLOR) { - glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); - } - - loc = glGetAttribLocation(glprogram, "inTexCoord0"); - if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { - glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); - } - - loc = glGetAttribLocation(glprogram, "inTangent"); - if (loc >= 0 && loc != gpu::Stream::TANGENT) { - glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); - } - - char attribName[] = "inTexCoordn"; - for (auto i = 0; i < 4; i++) { - auto streamId = gpu::Stream::TEXCOORD1 + i; - - attribName[strlen(attribName) - 1] = '1' + i; - loc = glGetAttribLocation(glprogram, attribName); - if (loc >= 0 && loc != streamId) { - glBindAttribLocation(glprogram, streamId, attribName); - } - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); - } - - loc = glGetAttribLocation(glprogram, "_drawCallInfo"); - if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { - glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); - } - - // Link again to take into account the assigned attrib location - glLinkProgram(glprogram); - - GLint linked = 0; - glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) { - qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; - } -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp deleted file mode 100644 index 4a5c772b8b..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp +++ /dev/null @@ -1,334 +0,0 @@ -// -// 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" - -#include - -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*/ }; - //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL_MODES[mode]); - qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_POINT"; - qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_LINE"; - qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_FILL"; - 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) { - // glEnable(GL_DEPTH_CLAMP); - //} else { - // glDisable(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 !defined(USE_GLES) - if (enable) { - glEnable(GL_MULTISAMPLE); - } else { - glDisable(GL_MULTISAMPLE); - } - (void)CHECK_GL_ERROR(); -#endif - - _pipeline._stateCache.multisampleEnable = enable; - } -} - -void GLBackend::do_setStateAntialiasedLineEnable(bool enable) { - if (_pipeline._stateCache.antialisedLineEnable != enable) { -#if !defined(USE_GLES) - if (enable) { - glEnable(GL_LINE_SMOOTH); - } else { - glDisable(GL_LINE_SMOOTH); - } - (void)CHECK_GL_ERROR(); -#endif - - _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); -#if !defined(USE_GLES) - glEnable(GL_POLYGON_OFFSET_LINE); - glEnable(GL_POLYGON_OFFSET_POINT); -#endif - glPolygonOffset(bias.x, bias.y); - } else { - glDisable(GL_POLYGON_OFFSET_FILL); -#if !defined(USE_GLES) - glDisable(GL_POLYGON_OFFSET_LINE); - glDisable(GL_POLYGON_OFFSET_POINT); -#endif - } - (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()) { - qCDebug(gpulogging) << "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 deleted file mode 100644 index ace33766ce..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// -// -// 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; - - -GLuint GLBackend::getTextureID(const TexturePointer& texture) { - GLTexture* object = syncGPUObject(texture); - - if (!object) { - return 0; - } - - return object->_id; -} - -GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { - const Texture& texture = *texturePointer; - // Special case external textures - if (TextureUsageType::EXTERNAL == texture.getUsageType()) { - Texture::ExternalUpdates updates = texture.getUpdates(); - if (!updates.empty()) { - Texture::ExternalRecycler recycler = texture.getExternalRecycler(); - Q_ASSERT(recycler); - // Discard any superfluous updates - while (updates.size() > 1) { - const auto& update = updates.front(); - // Superfluous updates will never have been read, but we want to ensure the previous - // writes to them are complete before they're written again, so return them with the - // same fences they arrived with. This can happen on any thread because no GL context - // work is involved - recycler(update.first, update.second); - updates.pop_front(); - } - - // The last texture remaining is the one we'll use to create the GLTexture - const auto& update = updates.front(); - // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence - if (update.second) { - GLsync fence = static_cast(update.second); - glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync(fence); - } - - // Create the new texture object (replaces any previous texture object) - new GLExternalTexture(shared_from_this(), texture, update.first); - } - - // Return the texture object (if any) associated with the texture, without extensive logic - // (external textures are - return Backend::getGPUObject(texture); - } - - return nullptr; -} - -void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { - TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); - if (!resourceTexture) { - return; - } - - // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(resourceTexture); - 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 deleted file mode 100644 index 72aaa5aa66..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp +++ /dev/null @@ -1,171 +0,0 @@ -// -// 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) { -} - -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.isStereo()) { - 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 && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) { - // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? - Transform result; - _view.mult(result, _view, _correction.correctionInverse); - 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.isStereo()) { -#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.isStereo()) { - bindCurrentCamera(0); - } -#endif - } - (void)CHECK_GL_ERROR(); -} - -void GLBackend::TransformStageState::bindCurrentCamera(int eye) const { - if (_currentCameraOffset != INVALID_OFFSET) { - glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _cameraBuffer, _currentCameraOffset + eye * _cameraUboSize, sizeof(CameraBufferElement)); - } -} - -void GLBackend::resetTransformStage() { - glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); - _transform._enabledDrawcallInfoBuffer = false; -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp b/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp deleted file mode 100644 index 4f7d0a8632..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// -// 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 deleted file mode 100644 index 182014e764..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBuffer.h +++ /dev/null @@ -1,66 +0,0 @@ -// -// 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/GLESBackend.cpp b/libraries/gpu-gles/src/gpu/gl/GLESBackend.cpp new file mode 100644 index 0000000000..2e2c988e77 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLESBackend.cpp @@ -0,0 +1,56 @@ +// +// 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 + +#include + + +#include +#include +#include +#include + +#include "../gles/GLESBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +static GLBackend* INSTANCE{ nullptr }; + +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(); + result->initTextureManagementStage(); + + INSTANCE = result.get(); + void* voidInstance = &(*result); + qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); + return result; +} + +GLBackend& getBackend() { + if (!INSTANCE) { + INSTANCE = static_cast(qApp->property(hifi::properties::gl::BACKEND).value()); + } + return *INSTANCE; +} + +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { + return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp deleted file mode 100644 index 0bca9e86d9..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// -// 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() const { - switch (_status) { - case GL_FRAMEBUFFER_COMPLETE: - // Success ! - return true; - - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; - break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT."; - break; - //case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - // qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER."; - // break; - //case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - // qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER."; - // break; - case GL_FRAMEBUFFER_UNSUPPORTED: - qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; - break; - } - return false; -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h deleted file mode 100644 index 5a388e1965..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h +++ /dev/null @@ -1,77 +0,0 @@ -// -// 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() 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 deleted file mode 100644 index 7f42350c3b..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// -// 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 deleted file mode 100644 index a14e3d4d91..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLInputFormat.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// 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 deleted file mode 100644 index ebf1a55232..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// 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. - object->_cameraCorrection = shader->getUniformBuffers().findLocation("cameraCorrectionBuffer"); - 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 deleted file mode 100644 index a298f149d9..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLPipeline.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// 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 deleted file mode 100644 index 23b1f38621..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLQuery.h +++ /dev/null @@ -1,67 +0,0 @@ -// -// 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 deleted file mode 100644 index 010a7c479c..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// -// 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); - } - } - } -} - -GLShader* GLShader::sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler) { - 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 = backend.compileBackendProgram(shader, handler); - if (tempObject) { - object = tempObject; - Backend::setGPUObject(shader, object); - } - } else if (shader.isDomain()) { - GLShader* tempObject = backend.compileBackendShader(shader, handler); - if (tempObject) { - object = tempObject; - Backend::setGPUObject(shader, object); - } - } - - glFinish(); - return object; -} - -bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { - - // First make sure the Shader has been compiled - GLShader* object = sync(backend, shader, handler); - 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; - backend.makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers); - - Shader::SlotSet uniforms; - Shader::SlotSet textures; - Shader::SlotSet samplers; - backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); - - Shader::SlotSet resourceBuffers; - backend.makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); - - Shader::SlotSet inputs; - backend.makeInputSlots(shaderObject.glprogram, slotBindings, inputs); - - Shader::SlotSet outputs; - backend.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 deleted file mode 100644 index f2a144a81c..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.h +++ /dev/null @@ -1,66 +0,0 @@ -// -// 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 { - -struct ShaderObject { - GLuint glshader { 0 }; - GLuint glprogram { 0 }; - GLint transformCameraSlot { -1 }; - GLint transformObjectSlot { -1 }; -}; - -class GLShader : public GPUObject { -public: - static GLShader* sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler = nullptr); - static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler = nullptr); - - 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 deleted file mode 100644 index f818a221b2..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLShared.cpp +++ /dev/null @@ -1,335 +0,0 @@ -// -// 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") -Q_LOGGING_CATEGORY(trace_render_gpu_gl_detail, "trace.render.gpu.gl.detail") - -namespace gpu { namespace gl { - -bool checkGLError(const char* name) { - GLenum error = glGetError(); - if (!error) { - return false; - } else { - switch (error) { - case GL_INVALID_ENUM: - qCWarning(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; - break; - case GL_INVALID_VALUE: - qCWarning(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; - break; - case GL_INVALID_OPERATION: - qCWarning(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - qCWarning(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; - break; - case GL_OUT_OF_MEMORY: - qCWarning(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; - break; - case GL_STACK_UNDERFLOW: - qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to underflow."; - break; - case GL_STACK_OVERFLOW: - qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to overflow."; - break; - } - return true; - } -} - -bool checkGLErrorDebug(const char* name) { - // FIXME, disable in debug mode when near release - return checkGLError(name); -} - -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; -} - -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); - //if (modes[0] == GL_FILL) { - // state.fillMode = State::FILL_FACE; - //} else { - // if (modes[0] == GL_LINE) { - // state.fillMode = State::FILL_LINE; - // } else { - // state.fillMode = State::FILL_POINT; - // } - //} - } - { - if (glIsEnabled(GL_CULL_FACE)) { - 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 = false; //glIsEnabled(GL_DEPTH_CLAMP_EXT); - state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST); - state.multisampleEnable = false; //glIsEnabled(GL_MULTISAMPLE_EXT); - state.antialisedLineEnable = false; //glIsEnabled(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(); -} - - -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 deleted file mode 100644 index 1341dd16fa..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLShared.h +++ /dev/null @@ -1,152 +0,0 @@ -// -// 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 -#include - - -Q_DECLARE_LOGGING_CATEGORY(gpugllogging) -Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl) -Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail) - -#define BUFFER_OFFSET(bytes) ((GLubyte*) nullptr + (bytes)) - -namespace gpu { namespace gl { - -// 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 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); - -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, - GL_UNSIGNED_BYTE, - GL_INT_2_10_10_10_REV, -}; - -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; -struct ShaderObject; - -} } // namespace gpu::gl - -#endif - - - diff --git a/libraries/gpu-gles/src/gpu/gl/GLState.cpp b/libraries/gpu-gles/src/gpu/gl/GLState.cpp deleted file mode 100644 index b6d917b928..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLState.cpp +++ /dev/null @@ -1,248 +0,0 @@ -// -// 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 deleted file mode 100644 index 82635db893..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLState.h +++ /dev/null @@ -1,73 +0,0 @@ -// -// 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 deleted file mode 100644 index bad6c3456c..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp +++ /dev/null @@ -1,733 +0,0 @@ -// -// 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; - -bool GLTexelFormat::isCompressed() const { - switch (internalFormat) { - case GL_COMPRESSED_R11_EAC: - case GL_COMPRESSED_SIGNED_R11_EAC: - case GL_COMPRESSED_RG11_EAC: - case GL_COMPRESSED_SIGNED_RG11_EAC: - case GL_COMPRESSED_RGB8_ETC2: - case GL_COMPRESSED_SRGB8_ETC2: - case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: - case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: - case GL_COMPRESSED_RGBA8_ETC2_EAC: - case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: - case GL_COMPRESSED_RGBA_ASTC_4x4: - case GL_COMPRESSED_RGBA_ASTC_5x4: - case GL_COMPRESSED_RGBA_ASTC_5x5: - case GL_COMPRESSED_RGBA_ASTC_6x5: - case GL_COMPRESSED_RGBA_ASTC_6x6: - case GL_COMPRESSED_RGBA_ASTC_8x5: - case GL_COMPRESSED_RGBA_ASTC_8x6: - case GL_COMPRESSED_RGBA_ASTC_8x8: - case GL_COMPRESSED_RGBA_ASTC_10x5: - case GL_COMPRESSED_RGBA_ASTC_10x6: - case GL_COMPRESSED_RGBA_ASTC_10x8: - case GL_COMPRESSED_RGBA_ASTC_10x10: - case GL_COMPRESSED_RGBA_ASTC_12x10: - case GL_COMPRESSED_RGBA_ASTC_12x12: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12: - return true; - default: - return false; - } -} - -GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { - GLenum result = GL_RGBA8; - switch (dstFormat.getDimension()) { - case gpu::SCALAR: { - switch (dstFormat.getSemantic()) { - case gpu::RED: - case gpu::RGB: - case gpu::RGBA: - case gpu::SRGB: - 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::NUINT16: - //result = GL_R16_EXT; - break; - case gpu::NINT16: - //result = GL_R16_SNORM_EXT; - 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_NV; - } else { - result = GL_R8; - } - break; - case gpu::NINT8: - result = GL_R8_SNORM; - break; - default: - Q_UNREACHABLE(); - break; - } - break; - case gpu::R11G11B10: - // the type should be float - result = GL_R11F_G11F_B10F; - break; - case gpu::RGB9E5: - // the type should be float - result = GL_RGB9_E5; - break; - case gpu::DEPTH: - result = GL_DEPTH_COMPONENT16; - switch (dstFormat.getType()) { - case gpu::UINT32: - case gpu::INT32: - case gpu::NUINT32: - case gpu::NINT32: - result = GL_DEPTH_COMPONENT32_OES; - break; - case gpu::FLOAT: - result = GL_DEPTH_COMPONENT32F; - break; - 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: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - break; - } - - case gpu::VEC2: { - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - case gpu::XY: - switch (dstFormat.getType()) { - case gpu::UINT32: - result = GL_RG32UI; - break; - case gpu::INT32: - result = GL_RG32I; - break; - case gpu::FLOAT: - result = GL_RG32F; - break; - case gpu::UINT16: - result = GL_RG16UI; - break; - case gpu::INT16: - result = GL_RG16I; - break; - case gpu::HALF: - result = GL_RG16F; - break; - case gpu::UINT8: - result = GL_RG8UI; - break; - case gpu::INT8: - result = GL_RG8I; - break; - case gpu::NUINT8: - result = GL_RG8; - break; - case gpu::NINT8: - result = GL_RG8_SNORM; - break; - case gpu::NUINT32: - case gpu::NINT32: - case gpu::NUINT2: - case gpu::NINT2_10_10_10: - case gpu::COMPRESSED: - case gpu::NUINT16: - case gpu::NINT16: - case gpu::NUM_TYPES: // quiet compiler - Q_UNREACHABLE(); - } - break; - default: - qCWarning(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 - break; - default: - qCWarning(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::NUINT16: - //result = GL_RGBA16_EXT; - break; - case gpu::NINT16: - //result = GL_RGBA16_SNORM_EXT; - 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::NUINT2: - //result = GL_RGBA2; - break; - case gpu::NINT8: - result = GL_RGBA8_SNORM; - break; - case gpu::NINT2_10_10_10: - case gpu::NUINT32: - case gpu::NINT32: - case gpu::COMPRESSED: - case gpu::NUM_TYPES: // quiet compiler - Q_UNREACHABLE(); - } - break; - case gpu::SRGB: - result = GL_SRGB8; - break; - case gpu::SRGBA: - result = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - break; - } - default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; - } - - //qDebug() << "GLTexelFormat::evalGLTexelFormatInternal result " << result; - return result; -} - -GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) { - - 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::RED: - case gpu::RGB: - case gpu::RGBA: - texel.internalFormat = GL_R8; - break; - - case gpu::DEPTH: - texel.format = GL_DEPTH_COMPONENT; - texel.internalFormat = GL_DEPTH_COMPONENT32F; - break; - case gpu::DEPTH_STENCIL: - texel.type = GL_UNSIGNED_INT_24_8; - texel.format = GL_DEPTH_STENCIL; - texel.internalFormat = GL_DEPTH24_STENCIL8; - break; - default: - qCWarning(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: - case gpu::XY: - switch (dstFormat.getType()) { - case gpu::UINT32: - texel.internalFormat = GL_RG32UI; - break; - case gpu::INT32: - texel.internalFormat = GL_RG32I; - break; - case gpu::FLOAT: - texel.internalFormat = GL_RG32F; - break; - case gpu::UINT16: - texel.internalFormat = GL_RG16UI; - break; - case gpu::INT16: - texel.internalFormat = GL_RG16I; - break; - case gpu::HALF: - texel.type = GL_FLOAT; - texel.internalFormat = GL_RG16F; - break; - case gpu::UINT8: - texel.internalFormat = GL_RG8UI; - break; - case gpu::INT8: - texel.internalFormat = GL_RG8I; - break; - case gpu::NUINT8: - texel.internalFormat = GL_RG8; - break; - case gpu::NINT8: - texel.internalFormat = GL_RG8_SNORM; - break; - case gpu::NUINT32: - case gpu::NINT32: - case gpu::NUINT2: - case gpu::NINT2_10_10_10: - case gpu::COMPRESSED: - case gpu::NUINT16: - case gpu::NINT16: - case gpu::NUM_TYPES: // quiet compiler - Q_UNREACHABLE(); - } - break; - default: - qCWarning(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; - default: - qCWarning(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: - texel.format = GL_RGBA; // GL_BGRA_EXT; - 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; - break; - case gpu::SRGBA: - texel.internalFormat = GL_SRGB8_ALPHA8; - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - break; - } - - default: - qCWarning(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()) { - - case gpu::RED: - 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_EXT; - break; - } - case gpu::NINT16: { - texel.internalFormat = GL_R16_SNORM_EXT; - 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_NV; - } else { - texel.internalFormat = GL_R8; - } - break; - } - case gpu::NINT8: { - texel.internalFormat = GL_R8_SNORM; - break; - } - case gpu::COMPRESSED: - case gpu::NUINT2: - case gpu::NUM_TYPES: { // quiet compiler - Q_UNREACHABLE(); - } - - } - break; - - case gpu::R11G11B10: - texel.format = GL_RGB; - texel.type = GL_UNSIGNED_INT_10F_11F_11F_REV; - texel.internalFormat = GL_R11F_G11F_B10F; - break; - - case gpu::RGB9E5: - texel.format = GL_RGB; - texel.type = GL_UNSIGNED_INT_5_9_9_9_REV; - texel.internalFormat = GL_RGB9_E5; - break; - - case gpu::DEPTH: - texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it - texel.internalFormat = GL_DEPTH_COMPONENT32_OES; - 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::COMPRESSED: - case gpu::NUINT2: - case gpu::NINT2_10_10_10: - 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: - qCWarning(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: - case gpu::XY: - texel.internalFormat = GL_RG8; - break; - default: - qCWarning(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 - break; - default: - qCWarning(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_EXT; - break; - case gpu::NINT16: - texel.format = GL_RGBA; - //texel.internalFormat = GL_RGBA16_SNORM_EXT; - 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::NUINT2: - texel.format = GL_RGBA; - texel.internalFormat = GL_RGBA8; - break; - case gpu::NUINT32: - case gpu::NINT32: - case gpu::NINT2_10_10_10: - case gpu::COMPRESSED: - case gpu::NUM_TYPES: // quiet compiler - Q_UNREACHABLE(); - } - break; - case gpu::SRGB: - texel.internalFormat = GL_SRGB8; - break; - case gpu::SRGBA: - texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - break; - } - default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; - } - return texel; - } -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h deleted file mode 100644 index 8f37f6b604..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// 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; - - GLTexelFormat(GLenum glinternalFormat, GLenum glformat, GLenum gltype) : internalFormat(glinternalFormat), format(glformat), type(gltype) {} - GLTexelFormat(GLenum glinternalFormat) : internalFormat(glinternalFormat) {} - - bool isCompressed() const; - - static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) { - return evalGLTexelFormat(dstFormat, dstFormat); - } - 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 deleted file mode 100644 index 08b3f87094..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp +++ /dev/null @@ -1,701 +0,0 @@ -// -// 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 - -#include "GLBackend.h" - -using namespace gpu; -using namespace gpu::gl; - - -const GLenum GLTexture::CUBE_FACE_LAYOUT[GLTexture::TEXTURE_CUBE_NUM_FACES] = { - GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, - GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, - GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z -}; - - -const GLenum GLTexture::WRAP_MODES[Sampler::NUM_WRAP_MODES] = { - GL_REPEAT, // WRAP_REPEAT, - GL_MIRRORED_REPEAT, // WRAP_MIRROR, - GL_CLAMP_TO_EDGE, // WRAP_CLAMP, - GL_CLAMP_TO_BORDER, // WRAP_BORDER, - GL_MIRRORED_REPEAT //GL_MIRROR_CLAMP_TO_EDGE_EXT // WRAP_MIRROR_ONCE, -}; - -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; -} - -uint8_t GLTexture::getFaceCount(GLenum target) { - switch (target) { - case GL_TEXTURE_2D: - return TEXTURE_2D_NUM_FACES; - case GL_TEXTURE_CUBE_MAP: - return TEXTURE_CUBE_NUM_FACES; - default: - Q_UNREACHABLE(); - break; - } -} - -const std::vector& GLTexture::getFaceTargets(GLenum target) { - static std::vector cubeFaceTargets { - 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; -} - -GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : - GLObject(backend, texture, id), - _source(texture.source()), - _target(getGLTextureType(texture)), - _texelFormat(GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())) -{ - Backend::setGPUObject(texture, this); -} - -GLTexture::~GLTexture() { - auto backend = _backend.lock(); - if (backend && _id) { - backend->releaseTexture(_id, 0); - } -} - -Size GLTexture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { - if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { - return 0; - } - auto dim = _gpuObject.evalMipDimensions(sourceMip); - auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); - auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); - if (mipData) { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - return copyMipFaceLinesFromTexture(targetMip, face, dim, 0, texelFormat.internalFormat, texelFormat.format, texelFormat.type, mipSize, mipData->readData()); - } else { - qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); - } - return 0; -} - - -GLExternalTexture::GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) - : Parent(backend, texture, id) { - Backend::textureExternalCount.increment(); -} - -GLExternalTexture::~GLExternalTexture() { - auto backend = _backend.lock(); - if (backend) { - auto recycler = _gpuObject.getExternalRecycler(); - if (recycler) { - backend->releaseExternalTexture(_id, recycler); - } else { - qCWarning(gpugllogging) << "No recycler available for texture " << _id << " possible leak"; - } - const_cast(_id) = 0; - } - Backend::textureExternalCount.decrement(); -} - - -// Variable sized textures -using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState; -using WorkQueue = GLVariableAllocationSupport::WorkQueue; -using TransferJobPointer = GLVariableAllocationSupport::TransferJobPointer; - -std::list GLVariableAllocationSupport::_memoryManagedTextures; -MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle }; -std::atomic GLVariableAllocationSupport::_memoryPressureStateStale { false }; -const uvec3 GLVariableAllocationSupport::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; -WorkQueue GLVariableAllocationSupport::_transferQueue; -WorkQueue GLVariableAllocationSupport::_promoteQueue; -WorkQueue GLVariableAllocationSupport::_demoteQueue; -size_t GLVariableAllocationSupport::_frameTexturesCreated { 0 }; - -#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f -#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f -#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) - -static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); - -using TransferJob = GLVariableAllocationSupport::TransferJob; - -const uvec3 GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; -const size_t GLVariableAllocationSupport::MAX_TRANSFER_SIZE = GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.x * GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.y * 4; - -#if THREADED_TEXTURE_BUFFERING - -TexturePointer GLVariableAllocationSupport::_currentTransferTexture; -TransferJobPointer GLVariableAllocationSupport::_currentTransferJob; -QThreadPool* TransferJob::_bufferThreadPool { nullptr }; - -void TransferJob::startBufferingThread() { - static std::once_flag once; - std::call_once(once, [&] { - _bufferThreadPool = new QThreadPool(qApp); - _bufferThreadPool->setMaxThreadCount(1); - }); -} - -#endif - -TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) - : _parent(parent) { - - auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - GLenum format; - GLenum internalFormat; - GLenum type; - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); - format = texelFormat.format; - internalFormat = texelFormat.internalFormat; - type = texelFormat.type; - _transferSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face); - - // If we're copying a subsection of the mip, do additional calculations to find the size and offset of the segment - if (0 != lines) { - transferDimensions.y = lines; - auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - auto bytesPerLine = (uint32_t)_transferSize / dimensions.y; - _transferOffset = bytesPerLine * lineOffset; - _transferSize = bytesPerLine * lines; - } - - Backend::texturePendingGPUTransferMemSize.update(0, _transferSize); - - if (_transferSize > GLVariableAllocationSupport::MAX_TRANSFER_SIZE) { - qCWarning(gpugllogging) << "Transfer size of " << _transferSize << " exceeds theoretical maximum transfer size"; - } - - // Buffering can invoke disk IO, so it should be off of the main and render threads - _bufferingLambda = [=] { - auto mipStorage = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - if (mipStorage) { - _mipData = mipStorage->createView(_transferSize, _transferOffset); - } else { - qCWarning(gpugllogging) << "Buffering failed because mip could not be retrieved from texture " << _parent._source.c_str() ; - } - }; - - _transferLambda = [=] { - if (_mipData) { - _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _mipData->size(), _mipData->readData()); - _mipData.reset(); - } else { - qCWarning(gpugllogging) << "Transfer failed because mip could not be retrieved from texture " << _parent._source.c_str(); - } - }; -} - -TransferJob::TransferJob(const GLTexture& parent, std::function transferLambda) - : _parent(parent), _bufferingRequired(false), _transferLambda(transferLambda) { -} - -TransferJob::~TransferJob() { - Backend::texturePendingGPUTransferMemSize.update(_transferSize, 0); -} - -bool TransferJob::tryTransfer() { -#if THREADED_TEXTURE_BUFFERING - // Are we ready to transfer - if (!bufferingCompleted()) { - startBuffering(); - return false; - } -#else - if (_bufferingRequired) { - _bufferingLambda(); - } -#endif - _transferLambda(); - return true; -} - -#if THREADED_TEXTURE_BUFFERING -bool TransferJob::bufferingRequired() const { - if (!_bufferingRequired) { - return false; - } - - // The default state of a QFuture is with status Canceled | Started | Finished, - // so we have to check isCancelled before we check the actual state - if (_bufferingStatus.isCanceled()) { - return true; - } - - return !_bufferingStatus.isStarted(); -} - -bool TransferJob::bufferingCompleted() const { - if (!_bufferingRequired) { - return true; - } - - // The default state of a QFuture is with status Canceled | Started | Finished, - // so we have to check isCancelled before we check the actual state - if (_bufferingStatus.isCanceled()) { - return false; - } - - return _bufferingStatus.isFinished(); -} - -void TransferJob::startBuffering() { - if (bufferingRequired()) { - assert(_bufferingStatus.isCanceled()); - _bufferingStatus = QtConcurrent::run(_bufferThreadPool, [=] { - _bufferingLambda(); - }); - assert(!_bufferingStatus.isCanceled()); - assert(_bufferingStatus.isStarted()); - } -} -#endif - -GLVariableAllocationSupport::GLVariableAllocationSupport() { - _memoryPressureStateStale = true; -} - -GLVariableAllocationSupport::~GLVariableAllocationSupport() { - _memoryPressureStateStale = true; -} - -void GLVariableAllocationSupport::addMemoryManagedTexture(const TexturePointer& texturePointer) { - _memoryManagedTextures.push_back(texturePointer); - if (MemoryPressureState::Idle != _memoryPressureState) { - addToWorkQueue(texturePointer); - } -} - -void GLVariableAllocationSupport::addToWorkQueue(const TexturePointer& texturePointer) { - GLTexture* gltexture = Backend::getGPUObject(*texturePointer); - GLVariableAllocationSupport* vargltexture = dynamic_cast(gltexture); - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - if (vargltexture->canDemote()) { - // Demote largest first - _demoteQueue.push({ texturePointer, (float)gltexture->size() }); - } - break; - - case MemoryPressureState::Undersubscribed: - if (vargltexture->canPromote()) { - // Promote smallest first - _promoteQueue.push({ texturePointer, 1.0f / (float)gltexture->size() }); - } - break; - - case MemoryPressureState::Transfer: - if (vargltexture->hasPendingTransfers()) { - // Transfer priority given to smaller mips first - _transferQueue.push({ texturePointer, 1.0f / (float)gltexture->_gpuObject.evalMipSize(vargltexture->_populatedMip) }); - } - break; - - case MemoryPressureState::Idle: - Q_UNREACHABLE(); - break; - } -} - -WorkQueue& GLVariableAllocationSupport::getActiveWorkQueue() { - static WorkQueue empty; - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - return _demoteQueue; - - case MemoryPressureState::Undersubscribed: - return _promoteQueue; - - case MemoryPressureState::Transfer: - return _transferQueue; - - case MemoryPressureState::Idle: - Q_UNREACHABLE(); - break; - } - return empty; -} - -// FIXME hack for stats display -QString getTextureMemoryPressureModeString() { - switch (GLVariableAllocationSupport::_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - return "Oversubscribed"; - - case MemoryPressureState::Undersubscribed: - return "Undersubscribed"; - - case MemoryPressureState::Transfer: - return "Transfer"; - - case MemoryPressureState::Idle: - return "Idle"; - } - Q_UNREACHABLE(); - return "Unknown"; -} - -void GLVariableAllocationSupport::updateMemoryPressure() { - static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); - - size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); - if (0 == allowedMemoryAllocation) { - allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; - } - - // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale - // so that we react - if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { - _memoryPressureStateStale = true; - lastAllowedMemoryAllocation = allowedMemoryAllocation; - } - - if (!_memoryPressureStateStale.exchange(false)) { - return; - } - - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - - // Clear any defunct textures (weak pointers that no longer have a valid texture) - _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { - return weakPointer.expired(); - }); - - // Convert weak pointers to strong. This new list may still contain nulls if a texture was - // deleted on another thread between the previous line and this one - std::vector strongTextures; { - strongTextures.reserve(_memoryManagedTextures.size()); - std::transform( - _memoryManagedTextures.begin(), _memoryManagedTextures.end(), - std::back_inserter(strongTextures), - [](const TextureWeakPointer& p) { return p.lock(); }); - } - - size_t totalVariableMemoryAllocation = 0; - size_t idealMemoryAllocation = 0; - bool canDemote = false; - bool canPromote = false; - bool hasTransfers = false; - for (const auto& texture : strongTextures) { - // Race conditions can still leave nulls in the list, so we need to check - if (!texture) { - continue; - } - GLTexture* gltexture = Backend::getGPUObject(*texture); - GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); - // Track how much the texture thinks it should be using - idealMemoryAllocation += texture->evalTotalSize(); - // Track how much we're actually using - totalVariableMemoryAllocation += gltexture->size(); - canDemote |= vartexture->canDemote(); - canPromote |= vartexture->canPromote(); - hasTransfers |= vartexture->hasPendingTransfers(); - } - - size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; - float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; - - auto newState = MemoryPressureState::Idle; - if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) { - newState = MemoryPressureState::Undersubscribed; - } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { - newState = MemoryPressureState::Oversubscribed; - } else if (hasTransfers) { - newState = MemoryPressureState::Transfer; - } - - if (newState != _memoryPressureState) { - _memoryPressureState = newState; - // Clear the existing queue - _transferQueue = WorkQueue(); - _promoteQueue = WorkQueue(); - _demoteQueue = WorkQueue(); - - // Populate the existing textures into the queue - if (_memoryPressureState != MemoryPressureState::Idle) { - for (const auto& texture : strongTextures) { - // Race conditions can still leave nulls in the list, so we need to check - if (!texture) { - continue; - } - addToWorkQueue(texture); - } - } - } -} - -TexturePointer GLVariableAllocationSupport::getNextWorkQueueItem(WorkQueue& workQueue) { - while (!workQueue.empty()) { - auto workTarget = workQueue.top(); - - auto texture = workTarget.first.lock(); - if (!texture) { - workQueue.pop(); - continue; - } - - // Check whether the resulting texture can actually have work performed - GLTexture* gltexture = Backend::getGPUObject(*texture); - GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - if (vartexture->canDemote()) { - return texture; - } - break; - - case MemoryPressureState::Undersubscribed: - if (vartexture->canPromote()) { - return texture; - } - break; - - case MemoryPressureState::Transfer: - if (vartexture->hasPendingTransfers()) { - return texture; - } - break; - - case MemoryPressureState::Idle: - Q_UNREACHABLE(); - break; - } - - // If we got here, then the texture has no work to do in the current state, - // so pop it off the queue and continue - workQueue.pop(); - } - - return TexturePointer(); -} - -void GLVariableAllocationSupport::processWorkQueue(WorkQueue& workQueue) { - if (workQueue.empty()) { - return; - } - - // Get the front of the work queue to perform work - auto texture = getNextWorkQueueItem(workQueue); - if (!texture) { - return; - } - - // Grab the first item off the demote queue - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - - GLTexture* gltexture = Backend::getGPUObject(*texture); - GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - vartexture->demote(); - workQueue.pop(); - addToWorkQueue(texture); - _memoryPressureStateStale = true; - break; - - case MemoryPressureState::Undersubscribed: - vartexture->promote(); - workQueue.pop(); - addToWorkQueue(texture); - _memoryPressureStateStale = true; - break; - - case MemoryPressureState::Transfer: - if (vartexture->executeNextTransfer(texture)) { - workQueue.pop(); - addToWorkQueue(texture); - -#if THREADED_TEXTURE_BUFFERING - // Eagerly start the next buffering job if possible - texture = getNextWorkQueueItem(workQueue); - if (texture) { - gltexture = Backend::getGPUObject(*texture); - vartexture = dynamic_cast(gltexture); - vartexture->executeNextBuffer(texture); - } -#endif - } - break; - - case MemoryPressureState::Idle: - Q_UNREACHABLE(); - break; - } -} - -void GLVariableAllocationSupport::processWorkQueues() { - if (MemoryPressureState::Idle == _memoryPressureState) { - return; - } - - auto& workQueue = getActiveWorkQueue(); - // Do work on the front of the queue - processWorkQueue(workQueue); - - if (workQueue.empty()) { - _memoryPressureState = MemoryPressureState::Idle; - _memoryPressureStateStale = true; - } -} - -void GLVariableAllocationSupport::manageMemory() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - updateMemoryPressure(); - processWorkQueues(); -} - -bool GLVariableAllocationSupport::executeNextTransfer(const TexturePointer& currentTexture) { -#if THREADED_TEXTURE_BUFFERING - // If a transfer job is active on the buffering thread, but has not completed it's buffering lambda, - // then we need to exit early, since we don't want to have the transfer job leave scope while it's - // being used in another thread -- See https://highfidelity.fogbugz.com/f/cases/4626 - if (_currentTransferJob && !_currentTransferJob->bufferingCompleted()) { - return false; - } -#endif - - if (_populatedMip <= _allocatedMip) { -#if THREADED_TEXTURE_BUFFERING - _currentTransferJob.reset(); - _currentTransferTexture.reset(); -#endif - return true; - } - - // If the transfer queue is empty, rebuild it - if (_pendingTransfers.empty()) { - populateTransferQueue(); - } - - bool result = false; - if (!_pendingTransfers.empty()) { -#if THREADED_TEXTURE_BUFFERING - // If there is a current transfer, but it's not the top of the pending transfer queue, then it's an orphan, so we want to abandon it. - if (_currentTransferJob && _currentTransferJob != _pendingTransfers.front()) { - _currentTransferJob.reset(); - } - - if (!_currentTransferJob) { - // Keeping hold of a strong pointer to the transfer job ensures that if the pending transfer queue is rebuilt, the transfer job - // doesn't leave scope, causing a crash in the buffering thread - _currentTransferJob = _pendingTransfers.front(); - - // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture - _currentTransferTexture = currentTexture; - } - - // transfer jobs use asynchronous buffering of the texture data because it may involve disk IO, so we execute a try here to determine if the buffering - // is complete - if (_currentTransferJob->tryTransfer()) { - _pendingTransfers.pop(); - // Once a given job is finished, release the shared pointers keeping them alive - _currentTransferTexture.reset(); - _currentTransferJob.reset(); - result = true; - } -#else - if (_pendingTransfers.front()->tryTransfer()) { - _pendingTransfers.pop(); - result = true; - } -#endif - } - return result; -} - -#if THREADED_TEXTURE_BUFFERING -void GLVariableAllocationSupport::executeNextBuffer(const TexturePointer& currentTexture) { - if (_currentTransferJob && !_currentTransferJob->bufferingCompleted()) { - return; - } - - // If the transfer queue is empty, rebuild it - if (_pendingTransfers.empty()) { - populateTransferQueue(); - } - - if (!_pendingTransfers.empty()) { - if (!_currentTransferJob) { - _currentTransferJob = _pendingTransfers.front(); - _currentTransferTexture = currentTexture; - } - - _currentTransferJob->startBuffering(); - } -} -#endif - -void GLVariableAllocationSupport::incrementPopulatedSize(Size delta) const { - _populatedSize += delta; - // Keep the 2 code paths to be able to debug - if (_size < _populatedSize) { - Backend::textureResourcePopulatedGPUMemSize.update(0, delta); - } else { - Backend::textureResourcePopulatedGPUMemSize.update(0, delta); - } -} -void GLVariableAllocationSupport::decrementPopulatedSize(Size delta) const { - _populatedSize -= delta; - // Keep the 2 code paths to be able to debug - if (_size < _populatedSize) { - Backend::textureResourcePopulatedGPUMemSize.update(delta, 0); - } else { - Backend::textureResourcePopulatedGPUMemSize.update(delta, 0); - } -} \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.h b/libraries/gpu-gles/src/gpu/gl/GLTexture.h deleted file mode 100644 index ce27d02033..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTexture.h +++ /dev/null @@ -1,211 +0,0 @@ -// -// 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 -#include - -#include "GLShared.h" -#include "GLBackend.h" -#include "GLTexelFormat.h" -#include - -#define THREADED_TEXTURE_BUFFERING 1 - -namespace gpu { namespace gl { - -struct GLFilterMode { - GLint minFilter; - GLint magFilter; -}; - -class GLVariableAllocationSupport { - friend class GLBackend; - -public: - GLVariableAllocationSupport(); - virtual ~GLVariableAllocationSupport(); - - enum class MemoryPressureState { - Idle, - Transfer, - Oversubscribed, - Undersubscribed, - }; - - using QueuePair = std::pair; - struct QueuePairLess { - bool operator()(const QueuePair& a, const QueuePair& b) { - return a.second < b.second; - } - }; - using WorkQueue = std::priority_queue, QueuePairLess>; - - class TransferJob { - using VoidLambda = std::function; - using VoidLambdaQueue = std::queue; - const GLTexture& _parent; - Texture::PixelsPointer _mipData; - size_t _transferOffset { 0 }; - size_t _transferSize { 0 }; - - bool _bufferingRequired { true }; - VoidLambda _transferLambda; - VoidLambda _bufferingLambda; - -#if THREADED_TEXTURE_BUFFERING - // Indicates if a transfer from backing storage to interal storage has started - QFuture _bufferingStatus; - static QThreadPool* _bufferThreadPool; -#endif - - public: - TransferJob(const TransferJob& other) = delete; - TransferJob(const GLTexture& parent, std::function transferLambda); - TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); - ~TransferJob(); - bool tryTransfer(); - -#if THREADED_TEXTURE_BUFFERING - void startBuffering(); - bool bufferingRequired() const; - bool bufferingCompleted() const; - static void startBufferingThread(); -#endif - - private: - void transfer(); - }; - - using TransferJobPointer = std::shared_ptr; - using TransferQueue = std::queue; - static MemoryPressureState _memoryPressureState; - -public: - static void addMemoryManagedTexture(const TexturePointer& texturePointer); - -protected: - static size_t _frameTexturesCreated; - static std::atomic _memoryPressureStateStale; - static std::list _memoryManagedTextures; - static WorkQueue _transferQueue; - static WorkQueue _promoteQueue; - static WorkQueue _demoteQueue; -#if THREADED_TEXTURE_BUFFERING - static TexturePointer _currentTransferTexture; - static TransferJobPointer _currentTransferJob; -#endif - static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; - static const uvec3 MAX_TRANSFER_DIMENSIONS; - static const size_t MAX_TRANSFER_SIZE; - - - static void updateMemoryPressure(); - static void processWorkQueues(); - static void processWorkQueue(WorkQueue& workQueue); - static TexturePointer getNextWorkQueueItem(WorkQueue& workQueue); - static void addToWorkQueue(const TexturePointer& texture); - static WorkQueue& getActiveWorkQueue(); - - static void manageMemory(); - - //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - bool canPromote() const { return _allocatedMip > _minAllocatedMip; } - bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } -#if THREADED_TEXTURE_BUFFERING - void executeNextBuffer(const TexturePointer& currentTexture); -#endif - bool executeNextTransfer(const TexturePointer& currentTexture); - virtual void populateTransferQueue() = 0; - virtual void promote() = 0; - virtual void demote() = 0; - - // THe amount of memory currently allocated - Size _size { 0 }; - - // The amount of memory currnently populated - void incrementPopulatedSize(Size delta) const; - void decrementPopulatedSize(Size delta) const; - mutable Size _populatedSize { 0 }; - - // The allocated mip level, relative to the number of mips in the gpu::Texture object - // The relationship between a given glMip to the original gpu::Texture mip is always - // glMip + _allocatedMip - uint16 _allocatedMip { 0 }; - // The populated mip level, relative to the number of mips in the gpu::Texture object - // This must always be >= the allocated mip - uint16 _populatedMip { 0 }; - // The highest (lowest resolution) mip that we will support, relative to the number - // of mips in the gpu::Texture object - uint16 _maxAllocatedMip { 0 }; - // The lowest (highest resolution) mip that we will support, relative to the number - // of mips in the gpu::Texture object - uint16 _minAllocatedMip { 0 }; - // Contains a series of lambdas that when executed will transfer data to the GPU, modify - // the _populatedMip and update the sampler in order to fully populate the allocated texture - // until _populatedMip == _allocatedMip - TransferQueue _pendingTransfers; -}; - -class GLTexture : public GLObject { - using Parent = GLObject; - friend class GLBackend; - friend class GLVariableAllocationSupport; -public: - static const uint16_t INVALID_MIP { (uint16_t)-1 }; - static const uint8_t INVALID_FACE { (uint8_t)-1 }; - - ~GLTexture(); - - const GLuint& _texture { _id }; - const std::string _source; - const GLenum _target; - GLTexelFormat _texelFormat; - - static const std::vector& getFaceTargets(GLenum textureType); - static uint8_t getFaceCount(GLenum textureType); - static GLenum getGLTextureType(const Texture& texture); - - static const uint8_t TEXTURE_2D_NUM_FACES = 1; - static const uint8_t TEXTURE_CUBE_NUM_FACES = 6; - static const GLenum CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES]; - static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS]; - static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; - -protected: - virtual Size size() const = 0; - virtual void generateMips() const = 0; - virtual void syncSampler() const = 0; - - virtual Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const = 0; - virtual Size copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const final; - virtual void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {} // Only relevant for Variable Allocation textures - - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); -}; - -class GLExternalTexture : public GLTexture { - using Parent = GLTexture; - friend class GLBackend; -public: - ~GLExternalTexture(); -protected: - GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); - void generateMips() const override {} - void syncSampler() const override {} - Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override { return 0;} - - Size size() const override { return 0; } -}; - - -} } - -#endif diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h index aeda054e72..38e28e630a 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h @@ -13,8 +13,8 @@ #include -#include "../gl/GLBackend.h" -#include "../gl/GLTexture.h" +#include +#include namespace gpu { namespace gles { diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp index 05bda34d7f..17fdad8377 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GLESBackend.h" -#include "../gl/GLBuffer.h" +#include namespace gpu { namespace gles { diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp index dc4025247e..0bf1548a4b 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp @@ -12,8 +12,8 @@ #include -#include "../gl/GLFramebuffer.h" -#include "../gl/GLTexture.h" +#include +#include namespace gpu { namespace gles { diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp index db541b07bc..434fbcb04f 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp @@ -10,7 +10,7 @@ // #include "GLESBackend.h" -#include "../gl/GLQuery.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp index 01a87978c2..16cf1559dd 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GLESBackend.h" -#include "../gl/GLShader.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp index d2fa2aabab..6bc55a23d4 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp @@ -13,7 +13,7 @@ #include #include -#include "../gl/GLTexelFormat.h" +#include using namespace gpu; using namespace gpu::gl; @@ -251,9 +251,6 @@ void GLESFixedAllocationTexture::allocateStorage() const { void GLESFixedAllocationTexture::syncSampler() const { Parent::syncSampler(); const Sampler& sampler = _gpuObject.getSampler(); - auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); - - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, baseMip); glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip())); } @@ -459,7 +456,6 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize; sourceMip._offset = bufferOffset; bufferOffset += sourceMip._size; - gpu::gl::checkGLError(); } (void)CHECK_GL_ERROR(); @@ -505,7 +501,6 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui #endif glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat, sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); - gpu::gl::checkGLError(); } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index b03195041b..1954f40b44 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -38,11 +38,13 @@ #include #include #include +#include #include #include #include "NetworkLogging.h" #include "ModelNetworkingLogging.h" +#include "NetworkingConstants.h" #include #include @@ -467,7 +469,7 @@ void NetworkTexture::makeLocalRequest() { const QString scheme = _url.scheme(); QString path; if (scheme == URL_SCHEME_FILE) { - path = _url.toLocalFile(); + path = PathUtils::expandToLocalDataAbsolutePath(_url).toLocalFile(); } else { path = ":" + _url.path(); } diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index d0f175d3c2..e5e213f5c5 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "AddressManager.h" #include "NodeList.h" @@ -40,27 +41,14 @@ const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; Setting::Handle currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_HIFI_ADDRESS); -AddressManager::AddressManager() : - _port(0) -{ - -} - bool AddressManager::isConnected() { return DependencyManager::get()->getDomainHandler().isConnected(); } QUrl AddressManager::currentAddress(bool domainOnly) const { - QUrl hifiURL; + QUrl hifiURL = _domainURL; - hifiURL.setScheme(HIFI_URL_SCHEME); - hifiURL.setHost(_host); - - if (_port != 0 && _port != DEFAULT_DOMAIN_SERVER_PORT) { - hifiURL.setPort(_port); - } - - if (!domainOnly) { + if (!domainOnly && hifiURL.scheme() == URL_SCHEME_HIFI) { hifiURL.setPath(currentPath()); } @@ -69,7 +57,9 @@ QUrl AddressManager::currentAddress(bool domainOnly) const { QUrl AddressManager::currentFacingAddress() const { auto hifiURL = currentAddress(); - hifiURL.setPath(currentFacingPath()); + if (hifiURL.scheme() == URL_SCHEME_HIFI) { + hifiURL.setPath(currentFacingPath()); + } return hifiURL; } @@ -79,7 +69,7 @@ QUrl AddressManager::currentShareableAddress(bool domainOnly) const { // if we have a shareable place name use that instead of whatever the current host is QUrl hifiURL; - hifiURL.setScheme(HIFI_URL_SCHEME); + hifiURL.setScheme(URL_SCHEME_HIFI); hifiURL.setHost(_shareablePlaceName); if (!domainOnly) { @@ -94,7 +84,9 @@ QUrl AddressManager::currentShareableAddress(bool domainOnly) const { QUrl AddressManager::currentFacingShareableAddress() const { auto hifiURL = currentShareableAddress(); - hifiURL.setPath(currentFacingPath()); + if (hifiURL.scheme() == URL_SCHEME_HIFI) { + hifiURL.setPath(currentFacingPath()); + } return hifiURL; } @@ -139,11 +131,16 @@ void AddressManager::goForward() { void AddressManager::storeCurrentAddress() { auto url = currentAddress(); - - if (!url.host().isEmpty()) { + + if (url.scheme() == URL_SCHEME_FILE || + (url.scheme() == URL_SCHEME_HIFI && !url.host().isEmpty())) { + // TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can + // be loaded over http(s) + // url.scheme() == URL_SCHEME_HTTP || + // url.scheme() == URL_SCHEME_HTTPS || currentAddressHandle.set(url); } else { - qCWarning(networking) << "Ignoring attempt to save current address with an empty host" << url; + qCWarning(networking) << "Ignoring attempt to save current address with an invalid url:" << url; } } @@ -209,7 +206,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { static QString URL_TYPE_DOMAIN_ID = "domain_id"; static QString URL_TYPE_PLACE = "place"; static QString URL_TYPE_NETWORK_ADDRESS = "network_address"; - if (lookupUrl.scheme() == HIFI_URL_SCHEME) { + if (lookupUrl.scheme() == URL_SCHEME_HIFI) { qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); @@ -289,12 +286,36 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { handlePath(lookupUrl.path(), trigger, true); emit lookupResultsFinished(); + return true; + } else if (lookupUrl.scheme() == URL_SCHEME_FILE) { + // TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can + // be loaded over http(s) + // lookupUrl.scheme() == URL_SCHEME_HTTP || + // lookupUrl.scheme() == URL_SCHEME_HTTPS || + _previousLookup.clear(); + QUrl domainURL = PathUtils::expandToLocalDataAbsolutePath(lookupUrl); + setDomainInfo(domainURL, trigger); + emit lookupResultsFinished(); + handlePath(DOMAIN_SPAWNING_POINT, LookupTrigger::Internal, false); return true; } return false; } +bool isPossiblePlaceName(QString possiblePlaceName) { + bool result { false }; + int length = possiblePlaceName.length(); + static const int MINIMUM_PLACENAME_LENGTH = 1; + static const int MAXIMUM_PLACENAME_LENGTH = 64; + if (possiblePlaceName.toLower() != "localhost" && + length >= MINIMUM_PLACENAME_LENGTH && length <= MAXIMUM_PLACENAME_LENGTH) { + const QRegExp PLACE_NAME_REGEX = QRegExp("^[0-9A-Za-z](([0-9A-Za-z]|-(?!-))*[^\\W_]$|$)"); + result = PLACE_NAME_REGEX.indexIn(possiblePlaceName) == 0; + } + return result; +} + void AddressManager::handleLookupString(const QString& lookupString, bool fromSuggestions) { if (!lookupString.isEmpty()) { // make this a valid hifi URL and handle it off to handleUrl @@ -302,12 +323,16 @@ void AddressManager::handleLookupString(const QString& lookupString, bool fromSu QUrl lookupURL; if (!lookupString.startsWith('/')) { - const QRegExp HIFI_SCHEME_REGEX = QRegExp(HIFI_URL_SCHEME + ":\\/{1,2}", Qt::CaseInsensitive); + // sometimes we need to handle lookupStrings like hifi:/somewhere + const QRegExp HIFI_SCHEME_REGEX = QRegExp(URL_SCHEME_HIFI + ":\\/{1,2}", Qt::CaseInsensitive); sanitizedString = sanitizedString.remove(HIFI_SCHEME_REGEX); - lookupURL = QUrl(HIFI_URL_SCHEME + "://" + sanitizedString); + lookupURL = QUrl(sanitizedString); + if (lookupURL.scheme().isEmpty()) { + lookupURL = QUrl("hifi://" + sanitizedString); + } } else { - lookupURL = QUrl(lookupString); + lookupURL = QUrl(sanitizedString); } handleUrl(lookupURL, fromSuggestions ? Suggestions : UserInput); @@ -385,7 +410,11 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const qCDebug(networking) << "Possible domain change required to connect to" << domainHostname << "on" << domainPort; - emit possibleDomainChangeRequired(domainHostname, domainPort, domainID); + QUrl domainURL; + domainURL.setScheme(URL_SCHEME_HIFI); + domainURL.setHost(domainHostname); + domainURL.setPort(domainPort); + emit possibleDomainChangeRequired(domainURL, domainID); } else { QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString(); @@ -422,15 +451,10 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const if (setHost(placeName, trigger)) { trigger = LookupTrigger::Internal; } - - _placeName = placeName; } else { if (setHost(domainIDString, trigger)) { trigger = LookupTrigger::Internal; } - - // this isn't a place, so clear the place name - _placeName.clear(); } // check if we had a path to override the path returned @@ -551,13 +575,17 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTri if (ipAddressRegex.indexIn(lookupString) != -1) { QString domainIPString = ipAddressRegex.cap(1); - qint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT; + quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT; if (!ipAddressRegex.cap(2).isEmpty()) { - domainPort = (qint16) ipAddressRegex.cap(2).toInt(); + domainPort = (quint16) ipAddressRegex.cap(2).toInt(); } emit lookupResultsFinished(); - hostChanged = setDomainInfo(domainIPString, domainPort, trigger); + QUrl domainURL; + domainURL.setScheme(URL_SCHEME_HIFI); + domainURL.setHost(domainIPString); + domainURL.setPort(domainPort); + hostChanged = setDomainInfo(domainURL, trigger); return true; } @@ -570,11 +598,15 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTri quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT; if (!hostnameRegex.cap(2).isEmpty()) { - domainPort = (qint16)hostnameRegex.cap(2).toInt(); + domainPort = (quint16)hostnameRegex.cap(2).toInt(); } emit lookupResultsFinished(); - hostChanged = setDomainInfo(domainHostname, domainPort, trigger); + QUrl domainURL; + domainURL.setScheme(URL_SCHEME_HIFI); + domainURL.setHost(domainHostname); + domainURL.setPort(domainPort); + hostChanged = setDomainInfo(domainURL, trigger); return true; } @@ -643,7 +675,7 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should addCurrentAddressToHistory(trigger); } - if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) { + if (!isNaN(newPosition)) { glm::quat newOrientation; QRegExp orientationRegex(QUAT_REGEX_STRING); @@ -663,11 +695,11 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should && !isNaN(newOrientation.w)) { orientationChanged = true; } else { - qCDebug(networking) << "Orientation parsed from lookup string is invalid. Will not use for location change."; + qCDebug(networking) << "Orientation parsed from lookup string is invalid. Won't use for location change."; } } - - emit locationChangeRequired(newPosition, orientationChanged, + + emit locationChangeRequired(newPosition, orientationChanged, trigger == LookupTrigger::VisitUserFromPAL ? cancelOutRollAndPitch(newOrientation): newOrientation, shouldFace ); @@ -698,18 +730,20 @@ bool AddressManager::handleUsername(const QString& lookupString) { } bool AddressManager::setHost(const QString& host, LookupTrigger trigger, quint16 port) { - if (host != _host || port != _port) { - + if (host != _domainURL.host() || port != _domainURL.port()) { addCurrentAddressToHistory(trigger); - _port = port; + bool emitHostChanged = host != _domainURL.host(); + _domainURL = QUrl(); + _domainURL.setScheme(URL_SCHEME_HIFI); + _domainURL.setHost(host); + _domainURL.setPort(port); // any host change should clear the shareable place name _shareablePlaceName.clear(); - if (host != _host) { - _host = host; - emit hostChanged(_host); + if (emitHostChanged) { + emit hostChanged(host); } return true; @@ -718,20 +752,43 @@ bool AddressManager::setHost(const QString& host, LookupTrigger trigger, quint16 return false; } -bool AddressManager::setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger) { - bool hostChanged = setHost(hostname, trigger, port); +QString AddressManager::getHost() const { + if (isPossiblePlaceName(_domainURL.host())) { + return QString(); + } + + return _domainURL.host(); +} + +bool AddressManager::setDomainInfo(const QUrl& domainURL, LookupTrigger trigger) { + const QString hostname = domainURL.host(); + quint16 port = domainURL.port(); + bool emitHostChanged { false }; + + if (domainURL != _domainURL) { + addCurrentAddressToHistory(trigger); + emitHostChanged = true; + } + + _domainURL = domainURL; // clear any current place information _rootPlaceID = QUuid(); - _placeName.clear(); - qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port; + if (_domainURL.scheme() == URL_SCHEME_HIFI) { + qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port; + } else { + qCDebug(networking) << "Possible domain change required to serverless domain: " << domainURL.toString(); + } DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); - emit possibleDomainChangeRequired(hostname, port, QUuid()); + if (emitHostChanged) { + emit hostChanged(domainURL.host()); + } + emit possibleDomainChangeRequired(_domainURL, QUuid()); - return hostChanged; + return emitHostChanged; } void AddressManager::goToUser(const QString& username, bool shouldMatchOrientation) { @@ -820,7 +877,7 @@ void AddressManager::lookupShareableNameForDomainID(const QUuid& domainID) { // then use that for Steam join/invite or copiable address // it only makes sense to lookup a shareable default name if we don't have a place name - if (_placeName.isEmpty()) { + if (getPlaceName().isEmpty()) { JSONCallbackParameters callbackParams; // no error callback handling @@ -872,3 +929,12 @@ void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) { } } +QString AddressManager::getPlaceName() const { + if (!_shareablePlaceName.isEmpty()) { + return _shareablePlaceName; + } + if (isPossiblePlaceName(_domainURL.host())) { + return _domainURL.host(); + } + return QString(); +} diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 1b550df693..dc1046bf51 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -22,8 +22,6 @@ #include "AccountManager.h" -const QString HIFI_URL_SCHEME = "hifi"; - extern const QString DEFAULT_HIFI_ADDRESS; const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost"; @@ -147,7 +145,7 @@ public: }; bool isConnected(); - const QString& getProtocol() { return HIFI_URL_SCHEME; }; + const QString& getProtocol() { return URL_SCHEME_HIFI; }; QUrl currentAddress(bool domainOnly = false) const; QUrl currentFacingAddress() const; @@ -157,10 +155,10 @@ public: QString currentFacingPath() const; const QUuid& getRootPlaceID() const { return _rootPlaceID; } - const QString& getPlaceName() const { return _shareablePlaceName.isEmpty() ? _placeName : _shareablePlaceName; } + QString getPlaceName() const; QString getDomainID() const; - const QString& getHost() const { return _host; } + QString getHost() const; void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } @@ -170,6 +168,8 @@ public: const QStack& getBackStack() const { return _backStack; } const QStack& getForwardStack() const { return _forwardStack; } + QUrl getDomainURL() { return _domainURL; } + public slots: /**jsdoc * Go to a specified metaverse address. @@ -302,13 +302,12 @@ signals: /**jsdoc * Triggered when a request is made to go to an IP address. * @function location.possibleDomainChangeRequired - * @param {string} hostName - The name of the domain to go do. - * @param {number} port - The integer number of the network port to connect to. + * @param {Url} domainURL - URL for domain * @param {Uuid} domainID - The UUID of the domain to go to. * @returns {Signal} */ // No example because this function isn't typically used in scripts. - void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort, const QUuid& domainID); + void possibleDomainChangeRequired(QUrl domainURL, QUuid domainID); /**jsdoc * Triggered when a request is made to go to a named domain or user. @@ -360,7 +359,7 @@ signals: * location.pathChangeRequired.connect(onPathChangeRequired); */ void pathChangeRequired(const QString& newPath); - + /**jsdoc * Triggered when you navigate to a new domain. * @function location.hostChanged @@ -392,7 +391,7 @@ signals: void goBackPossible(bool isPossible); /**jsdoc - * Triggered when there's a change in whether or not there's a forward location that can be navigated to using + * Triggered when there's a change in whether or not there's a forward location that can be navigated to using * {@link location.goForward|goForward}. (Reflects changes in the state of the "Goto" dialog's forward arrow.) * @function location.goForwardPossible * @param {boolean} isPossible - true if there's a forward location to navigate to, otherwise @@ -407,8 +406,6 @@ signals: */ void goForwardPossible(bool isPossible); -protected: - AddressManager(); private slots: void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); @@ -420,7 +417,7 @@ private: // Set host and port, and return `true` if it was changed. bool setHost(const QString& host, LookupTrigger trigger, quint16 port = 0); - bool setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger); + bool setDomainInfo(const QUrl& domainURL, LookupTrigger trigger); const JSONCallbackParameters& apiCallbackParameters(); @@ -438,9 +435,8 @@ private: void addCurrentAddressToHistory(LookupTrigger trigger); - QString _host; - quint16 _port; - QString _placeName; + QUrl _domainURL; + QUuid _rootPlaceID; PositionGetter _positionGetter; OrientationGetter _orientationGetter; @@ -452,7 +448,7 @@ private: quint64 _lastBackPush = 0; QString _newHostLookupPath; - + QUrl _previousLookup; }; diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index d302c6fac6..8d3d313ff9 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -20,6 +20,7 @@ #include "NetworkAccessManager.h" #include "NetworkLogging.h" +#include "NetworkingConstants.h" #include "ResourceManager.h" diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 411f8f5be2..7a5ecb2602 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -38,7 +38,7 @@ DomainHandler::DomainHandler(QObject* parent) : // if we get a socket that make sure our NetworkPeer ping timer stops connect(this, &DomainHandler::completedSocketDiscovery, &_icePeer, &NetworkPeer::stopPingTimer); - + // setup a timeout for failure on settings requests static const int DOMAIN_SETTINGS_TIMEOUT_MS = 5000; _settingsTimer.setInterval(DOMAIN_SETTINGS_TIMEOUT_MS); // 5s, Qt::CoarseTimer acceptable @@ -60,11 +60,11 @@ void DomainHandler::disconnect() { if (_isConnected) { sendDisconnectPacket(); } - + // clear member variables that hold the connection state to a domain _uuid = QUuid(); _connectionToken = QUuid(); - + _icePeer.reset(); if (requiresICE()) { @@ -78,10 +78,10 @@ void DomainHandler::disconnect() { void DomainHandler::sendDisconnectPacket() { // The DomainDisconnect packet is not verified - we're relying on the eventual addition of DTLS to the // domain-server connection to stop greifing here - + // construct the disconnect packet once (an empty packet but sourced with our current session UUID) static auto disconnectPacket = NLPacket::create(PacketType::DomainDisconnectRequest, 0); - + // send the disconnect packet to the current domain server auto nodeList = DependencyManager::get(); nodeList->sendUnreliablePacket(*disconnectPacket, _sockAddr); @@ -94,7 +94,7 @@ void DomainHandler::clearSettings() { void DomainHandler::softReset() { qCDebug(networking) << "Resetting current domain connection information."; disconnect(); - + clearSettings(); _connectionDenialsSinceKeypairRegen = 0; @@ -115,8 +115,8 @@ void DomainHandler::hardReset() { qCDebug(networking) << "Hard reset in NodeList DomainHandler."; _pendingDomainID = QUuid(); _iceServerSockAddr = HifiSockAddr(); - _hostname = QString(); _sockAddr.clear(); + _domainURL = QUrl(); _domainConnectionRefusals.clear(); @@ -139,7 +139,10 @@ void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hos } // some callers may pass a hostname, this is not to be used for lookup but for DTLS certificate verification - _hostname = hostname; + _domainURL = QUrl(); + _domainURL.setScheme(URL_SCHEME_HIFI); + _domainURL.setHost(hostname); + _domainURL.setPort(_sockAddr.getPort()); } void DomainHandler::setUUID(const QUuid& uuid) { @@ -149,36 +152,45 @@ void DomainHandler::setUUID(const QUuid& uuid) { } } -void DomainHandler::setSocketAndID(const QString& hostname, quint16 port, const QUuid& domainID) { - +void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) { _pendingDomainID = domainID; - if (hostname != _hostname || _sockAddr.getPort() != port) { + if (domainURL.scheme() != URL_SCHEME_HIFI) { + _sockAddr.clear(); + } + + if (_domainURL != domainURL || _sockAddr.getPort() != domainURL.port()) { // re-set the domain info so that auth information is reloaded hardReset(); - if (hostname != _hostname) { - // set the new hostname - _hostname = hostname; + QString previousHost = _domainURL.host(); + _domainURL = domainURL; - qCDebug(networking) << "Updated domain hostname to" << _hostname; + if (domainURL.scheme() != URL_SCHEME_HIFI) { + setIsConnected(true); + } else if (previousHost != domainURL.host()) { + qCDebug(networking) << "Updated domain hostname to" << domainURL.host(); - // re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname - qCDebug(networking, "Looking up DS hostname %s.", _hostname.toLocal8Bit().constData()); - QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&))); + if (!domainURL.host().isEmpty()) { + // re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname + qCDebug(networking, "Looking up DS hostname %s.", domainURL.host().toLocal8Bit().constData()); + QHostInfo::lookupHost(domainURL.host(), this, SLOT(completedHostnameLookup(const QHostInfo&))); - DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainHostname); + DependencyManager::get()->flagTimeForConnectionStep( + LimitedNodeList::ConnectionStep::SetDomainHostname); - UserActivityLogger::getInstance().changedDomain(_hostname); - emit hostnameChanged(_hostname); + UserActivityLogger::getInstance().changedDomain(domainURL.host()); + } } - if (_sockAddr.getPort() != port) { - qCDebug(networking) << "Updated domain port to" << port; + emit domainURLChanged(_domainURL); + + if (_sockAddr.getPort() != domainURL.port()) { + qCDebug(networking) << "Updated domain port to" << domainURL.port(); } // grab the port by reading the string after the colon - _sockAddr.setPort(port); + _sockAddr.setPort(domainURL.port()); } } @@ -187,10 +199,10 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, if (_iceServerSockAddr.getAddress().toString() != iceServerHostname || id != _pendingDomainID) { // re-set the domain info to connect to new domain hardReset(); - + // refresh our ICE client UUID to something new _iceClientID = QUuid::createUuid(); - + _pendingDomainID = id; HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr; @@ -216,14 +228,18 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, void DomainHandler::activateICELocalSocket() { DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket); _sockAddr = _icePeer.getLocalSocket(); - _hostname = _sockAddr.getAddress().toString(); + _domainURL.setScheme(URL_SCHEME_HIFI); + _domainURL.setHost(_sockAddr.getAddress().toString()); + emit domainURLChanged(_domainURL); emit completedSocketDiscovery(); } void DomainHandler::activateICEPublicSocket() { DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket); _sockAddr = _icePeer.getPublicSocket(); - _hostname = _sockAddr.getAddress().toString(); + _domainURL.setScheme(URL_SCHEME_HIFI); + _domainURL.setHost(_sockAddr.getAddress().toString()); + emit domainURLChanged(_domainURL); emit completedSocketDiscovery(); } @@ -234,7 +250,7 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket); - qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(), + qCDebug(networking, "DS at %s is at %s", _domainURL.host().toLocal8Bit().constData(), _sockAddr.getAddress().toString().toLocal8Bit().constData()); emit completedSocketDiscovery(); @@ -261,10 +277,12 @@ void DomainHandler::setIsConnected(bool isConnected) { _isConnected = isConnected; if (_isConnected) { - emit connectedToDomain(_hostname); + emit connectedToDomain(_domainURL); - // we've connected to new domain - time to ask it for global settings - requestDomainSettings(); + if (_domainURL.scheme() == URL_SCHEME_HIFI && !_domainURL.host().isEmpty()) { + // we've connected to new domain - time to ask it for global settings + requestDomainSettings(); + } } else { emit disconnectedFromDomain(); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 78f9798089..fbc60e2492 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -25,6 +25,7 @@ #include "NLPacketList.h" #include "Node.h" #include "ReceivedMessage.h" +#include "NetworkingConstants.h" const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102; const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103; @@ -37,14 +38,14 @@ class DomainHandler : public QObject { Q_OBJECT public: DomainHandler(QObject* parent = 0); - + void disconnect(); void clearSettings(); const QUuid& getUUID() const { return _uuid; } void setUUID(const QUuid& uuid); - const QString& getHostname() const { return _hostname; } + QString getHostname() const { return _domainURL.host(); } const QHostAddress& getIP() const { return _sockAddr.getAddress(); } void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); } @@ -57,7 +58,7 @@ public: const QUuid& getConnectionToken() const { return _connectionToken; } void setConnectionToken(const QUuid& connectionToken) { _connectionToken = connectionToken; } - + const QUuid& getAssignmentUUID() const { return _assignmentUUID; } void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } @@ -73,11 +74,12 @@ public: bool isConnected() const { return _isConnected; } void setIsConnected(bool isConnected); + bool isServerless() const { return _domainURL.scheme() != URL_SCHEME_HIFI; } bool hasSettings() const { return !_settingsObject.isEmpty(); } void requestDomainSettings(); const QJsonObject& getSettingsObject() const { return _settingsObject; } - + void setPendingPath(const QString& pendingPath) { _pendingPath = pendingPath; } const QString& getPendingPath() { return _pendingPath; } void clearPendingPath() { _pendingPath.clear(); } @@ -139,7 +141,7 @@ public: }; public slots: - void setSocketAndID(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT, const QUuid& id = QUuid()); + void setURLAndID(QUrl domainURL, QUuid id); void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id); void processSettingsPacketList(QSharedPointer packetList); @@ -153,14 +155,14 @@ private slots: void completedIceServerHostnameLookup(); signals: - void hostnameChanged(const QString& hostname); + void domainURLChanged(QUrl domainURL); // NOTE: the emission of completedSocketDiscovery does not mean a connection to DS is established // It means that, either from DNS lookup or ICE, we think we have a socket we can talk to DS on void completedSocketDiscovery(); void resetting(); - void connectedToDomain(const QString& hostname); + void connectedToDomain(QUrl domainURL); void disconnectedFromDomain(); void iceSocketAndIDReceived(); @@ -179,7 +181,7 @@ private: void hardReset(); QUuid _uuid; - QString _hostname; + QUrl _domainURL; HifiSockAddr _sockAddr; QUuid _assignmentUUID; QUuid _connectionToken; @@ -200,4 +202,7 @@ private: QTimer _apiRefreshTimer; }; +const QString DOMAIN_SPAWNING_POINT { "/0, -10, 0" }; + + #endif // hifi_DomainHandler_h diff --git a/libraries/networking/src/FileResourceRequest.cpp b/libraries/networking/src/FileResourceRequest.cpp index 7cbb401f7f..1a4096bc02 100644 --- a/libraries/networking/src/FileResourceRequest.cpp +++ b/libraries/networking/src/FileResourceRequest.cpp @@ -17,9 +17,11 @@ #include #include +#include #include "NetworkLogging.h" #include "ResourceManager.h" +#include "NetworkingConstants.h" void FileResourceRequest::doSend() { auto statTracker = DependencyManager::get(); @@ -29,7 +31,7 @@ void FileResourceRequest::doSend() { if (_url.scheme() == URL_SCHEME_QRC) { filename = ":/" + _url.path(); } else { - filename = _url.toLocalFile(); + filename = PathUtils::expandToLocalDataAbsolutePath(_url).toLocalFile(); // sometimes on windows, we see the toLocalFile() return null, // in this case we will attempt to simply use the url as a string if (filename.isEmpty()) { diff --git a/libraries/networking/src/NetworkingConstants.cpp b/libraries/networking/src/NetworkingConstants.cpp new file mode 100644 index 0000000000..622c307efa --- /dev/null +++ b/libraries/networking/src/NetworkingConstants.cpp @@ -0,0 +1,24 @@ +// +// NetworkingConstants.cpp +// libraries/networking/src +// +// Created by Seth Alves on 2018-2-28. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "NetworkingConstants.h" + +namespace NetworkingConstants { + // You can change the return of this function if you want to use a custom metaverse URL at compile time + // or you can pass a custom URL via the env variable + QUrl METAVERSE_SERVER_URL() { + const QString HIFI_METAVERSE_URL_ENV = "HIFI_METAVERSE_URL"; + const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains(HIFI_METAVERSE_URL_ENV) + ? QUrl(QProcessEnvironment::systemEnvironment().value(HIFI_METAVERSE_URL_ENV)) + : METAVERSE_SERVER_URL_STABLE; + return serverURL; + }; +} diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index a4726f9b1a..8eb1e71ed6 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -25,18 +25,17 @@ namespace NetworkingConstants { // if you manually generate a personal access token for the domains scope // at https://staging.highfidelity.com/user/tokens/new?for_domain_server=true - const QUrl METAVERSE_SERVER_URL_STABLE("https://metaverse.highfidelity.com"); - const QUrl METAVERSE_SERVER_URL_STAGING("https://staging.highfidelity.com"); - - // You can change the return of this function if you want to use a custom metaverse URL at compile time - // or you can pass a custom URL via the env variable - static const QUrl METAVERSE_SERVER_URL() { - static const QString HIFI_METAVERSE_URL_ENV = "HIFI_METAVERSE_URL"; - static const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains(HIFI_METAVERSE_URL_ENV) - ? QUrl(QProcessEnvironment::systemEnvironment().value(HIFI_METAVERSE_URL_ENV)) - : METAVERSE_SERVER_URL_STABLE; - return serverURL; - }; + const QUrl METAVERSE_SERVER_URL_STABLE { "https://metaverse.highfidelity.com" }; + const QUrl METAVERSE_SERVER_URL_STAGING { "https://staging.highfidelity.com" }; + QUrl METAVERSE_SERVER_URL(); } +const QString URL_SCHEME_HIFI = "hifi"; +const QString URL_SCHEME_QRC = "qrc"; +const QString URL_SCHEME_FILE = "file"; +const QString URL_SCHEME_HTTP = "http"; +const QString URL_SCHEME_HTTPS = "https"; +const QString URL_SCHEME_FTP = "ftp"; +const QString URL_SCHEME_ATP = "atp"; + #endif // hifi_NetworkingConstants_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 71d448ede9..cb0d2e4cd5 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -55,7 +55,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) // handle domain change signals from AddressManager connect(addressManager.data(), &AddressManager::possibleDomainChangeRequired, - &_domainHandler, &DomainHandler::setSocketAndID); + &_domainHandler, &DomainHandler::setURLAndID); connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID, &_domainHandler, &DomainHandler::setIceServerHostnameAndID); @@ -91,7 +91,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) connect(accountManager.data(), &AccountManager::newKeypair, this, &NodeList::sendDomainServerCheckIn); // clear out NodeList when login is finished - connect(accountManager.data(), SIGNAL(loginComplete()) , this, SLOT(reset())); + connect(accountManager.data(), SIGNAL(loginComplete(const QUrl&)) , this, SLOT(reset())); // clear our NodeList when logout is requested connect(accountManager.data(), SIGNAL(logoutComplete()) , this, SLOT(reset())); @@ -106,7 +106,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) // setup our timer to send keepalive pings (it's started and stopped on domain connect/disconnect) _keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); // 1s, Qt::CoarseTimer acceptable connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings); - connect(&_domainHandler, SIGNAL(connectedToDomain(QString)), &_keepAlivePingTimer, SLOT(start())); + connect(&_domainHandler, SIGNAL(connectedToDomain(QUrl)), &_keepAlivePingTimer, SLOT(start())); connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop); // set our sockAddrBelongsToDomainOrNode method as the connection creation filter for the udt::Socket diff --git a/libraries/networking/src/ResourceManager.h b/libraries/networking/src/ResourceManager.h index d5fa2aa0e2..9fc636f5fe 100644 --- a/libraries/networking/src/ResourceManager.h +++ b/libraries/networking/src/ResourceManager.h @@ -22,13 +22,6 @@ #include "ResourceRequest.h" -const QString URL_SCHEME_QRC = "qrc"; -const QString URL_SCHEME_FILE = "file"; -const QString URL_SCHEME_HTTP = "http"; -const QString URL_SCHEME_HTTPS = "https"; -const QString URL_SCHEME_FTP = "ftp"; -const QString URL_SCHEME_ATP = "atp"; - class ResourceManager: public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 7e0c82506b..dafdfd5bf4 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1685,6 +1685,15 @@ bool Octree::readFromURL(const QString& urlString) { } auto data = request->getData(); + + QByteArray uncompressedJsonData; + bool wasCompressed = gunzip(data, uncompressedJsonData); + + if (wasCompressed) { + QDataStream inputStream(uncompressedJsonData); + return readFromStream(uncompressedJsonData.size(), inputStream, marketplaceID); + } + QDataStream inputStream(data); return readFromStream(data.size(), inputStream, marketplaceID); } diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index e95084df52..ff6801109e 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -140,9 +140,6 @@ public: virtual void setContext(const gpu::ContextPointer& context) final { _gpuContext = context; } virtual void submitFrame(const gpu::FramePointer& newFrame) = 0; - // Does the rendering surface have current focus? - virtual bool hasFocus() const = 0; - // The size of the rendering target (may be larger than the device size due to distortion) virtual glm::uvec2 getRecommendedRenderSize() const = 0; diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 02ff234c01..90424b04b2 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -9,6 +9,7 @@ #include "AnimDebugDraw.h" +#include #include #include @@ -21,7 +22,6 @@ #include "animdebugdraw_vert.h" #include "animdebugdraw_frag.h" - class AnimDebugDrawData { public: diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index bf265fae8c..3fd18a0eaf 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -100,6 +100,8 @@ static const int VERTICES_PER_TRIANGLE = 3; static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; +static const gpu::Element TEXCOORD0_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV }; +static const gpu::Element TANGENT_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA }; static const gpu::Element TEXCOORD4_ELEMENT { gpu::VEC4, gpu::FLOAT, gpu::XYZW }; @@ -107,8 +109,10 @@ static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_STREAM_FORMAT; -static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normals -static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3); +static const uint SHAPE_VERTEX_STRIDE = sizeof(GeometryCache::ShapeVertex); // position, normal, texcoords, tangent +static const uint SHAPE_NORMALS_OFFSET = offsetof(GeometryCache::ShapeVertex, normal); +static const uint SHAPE_TEXCOORD0_OFFSET = offsetof(GeometryCache::ShapeVertex, uv); +static const uint SHAPE_TANGENT_OFFSET = offsetof(GeometryCache::ShapeVertex, tangent); void GeometryCache::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityExtents, QVector &outPointList) { @@ -167,16 +171,20 @@ std::vector polygon() { return result; } -void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const geometry::VertexVector& vertices) { +void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const std::vector& vertices) { gpu::Buffer::Size offset = vertexBuffer->getSize(); vertexBuffer->append(vertices); - gpu::Buffer::Size viewSize = vertices.size() * sizeof(glm::vec3); + gpu::Buffer::Size viewSize = vertices.size() * sizeof(ShapeVertex); _positionView = gpu::BufferView(vertexBuffer, offset, viewSize, SHAPE_VERTEX_STRIDE, POSITION_ELEMENT); _normalView = gpu::BufferView(vertexBuffer, offset + SHAPE_NORMALS_OFFSET, viewSize, SHAPE_VERTEX_STRIDE, NORMAL_ELEMENT); + _texCoordView = gpu::BufferView(vertexBuffer, offset + SHAPE_TEXCOORD0_OFFSET, + viewSize, SHAPE_VERTEX_STRIDE, TEXCOORD0_ELEMENT); + _tangentView = gpu::BufferView(vertexBuffer, offset + SHAPE_TANGENT_OFFSET, + viewSize, SHAPE_VERTEX_STRIDE, TANGENT_ELEMENT); } void GeometryCache::ShapeData::setupIndices(gpu::BufferPointer& indexBuffer, const geometry::IndexVector& indices, const geometry::IndexVector& wireIndices) { @@ -202,6 +210,8 @@ void GeometryCache::ShapeData::setupIndices(gpu::BufferPointer& indexBuffer, con void GeometryCache::ShapeData::setupBatch(gpu::Batch& batch) const { batch.setInputBuffer(gpu::Stream::POSITION, _positionView); batch.setInputBuffer(gpu::Stream::NORMAL, _normalView); + batch.setInputBuffer(gpu::Stream::TEXCOORD, _texCoordView); + batch.setInputBuffer(gpu::Stream::TANGENT, _tangentView); batch.setIndexBuffer(_indicesView); } @@ -268,14 +278,14 @@ static IndexPair indexToken(geometry::Index a, geometry::Index b) { template void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { using namespace geometry; - VertexVector vertices; + std::vector vertices; IndexVector solidIndices, wireIndices; IndexPairs wireSeenIndices; size_t faceCount = shape.faces.size(); size_t faceIndexCount = triangulatedFaceIndexCount(); - vertices.reserve(N * faceCount * 2); + vertices.reserve(N * faceCount); solidIndices.reserve(faceIndexCount * faceCount); Index baseVertex = 0; @@ -284,11 +294,35 @@ void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { using namespace geometry; - VertexVector vertices; - vertices.reserve(shape.vertices.size() * 2); + std::vector vertices; + vertices.reserve(shape.vertices.size()); for (const auto& vertex : shape.vertices) { - vertices.push_back(vertex); - vertices.push_back(vertex); + // We'll fill in the correct tangents later, once we correct the UVs + vertices.emplace_back(vertex, vertex, calculateSphereTexCoord(vertex), vec3(0.0f)); + } + + // We need to fix up the sphere's UVs because it's actually a tesselated icosahedron. See http://mft-dev.dk/uv-mapping-sphere/ + size_t faceCount = shape.faces.size(); + for (size_t f = 0; f < faceCount; f++) { + // Fix zipper + { + float& u1 = vertices[shape.faces[f][0]].uv.x; + float& u2 = vertices[shape.faces[f][1]].uv.x; + float& u3 = vertices[shape.faces[f][2]].uv.x; + + if (glm::isnan(u1)) { + u1 = (u2 + u3) / 2.0f; + } + if (glm::isnan(u2)) { + u2 = (u1 + u3) / 2.0f; + } + if (glm::isnan(u3)) { + u3 = (u1 + u2) / 2.0f; + } + + const float U_THRESHOLD = 0.25f; + float max = glm::max(u1, glm::max(u2, u3)); + float min = glm::min(u1, glm::min(u2, u3)); + + if (max - min > U_THRESHOLD) { + if (u1 < U_THRESHOLD) { + u1 += 1.0f; + } + if (u2 < U_THRESHOLD) { + u2 += 1.0f; + } + if (u3 < U_THRESHOLD) { + u3 += 1.0f; + } + } + } + + // Fix swirling at poles + for (Index i = 0; i < N; i++) { + Index originalIndex = shape.faces[f][i]; + if (shape.vertices[originalIndex].y == 1.0f || shape.vertices[originalIndex].y == -1.0f) { + float uSum = 0.0f; + for (Index i2 = 1; i2 <= N - 1; i2++) { + float u = vertices[shape.faces[f][(i + i2) % N]].uv.x; + uSum += u; + } + uSum /= (float)(N - 1); + vertices[originalIndex].uv.x = uSum; + break; + } + } + + // Fill in tangents + for (Index i = 0; i < N; i++) { + vec3 tangent = calculateSphereTangent(vertices[shape.faces[f][i]].uv.x); + vertices[shape.faces[f][i]].tangent = tangent; + } } IndexVector solidIndices, wireIndices; IndexPairs wireSeenIndices; - size_t faceCount = shape.faces.size(); size_t faceIndexCount = triangulatedFaceIndexCount(); solidIndices.reserve(faceIndexCount * faceCount); @@ -365,25 +473,22 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid template void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer, bool isConical = false) { using namespace geometry; - VertexVector vertices; + std::vector vertices; IndexVector solidIndices, wireIndices; // Top (if not conical) and bottom faces std::vector shape = polygon(); if (isConical) { for (uint32_t i = 0; i < N; i++) { - vertices.push_back(vec3(0.0f, 0.5f, 0.0f)); - vertices.push_back(vec3(0.0f, 1.0f, 0.0f)); + vertices.emplace_back(vec3(0.0f, 0.5f, 0.0f), vec3(0.0f, 1.0f, 0.0f), vec2((float)i / (float)N, 1.0f), vec3(0.0f)); } } else { for (const vec3& v : shape) { - vertices.push_back(vec3(v.x, 0.5f, v.z)); - vertices.push_back(vec3(0.0f, 1.0f, 0.0f)); + vertices.emplace_back(vec3(v.x, 0.5f, v.z), vec3(0.0f, 1.0f, 0.0f), vec2(v.x, v.z) + vec2(0.5f), vec3(1.0f, 0.0f, 0.0f)); } } for (const vec3& v : shape) { - vertices.push_back(vec3(v.x, -0.5f, v.z)); - vertices.push_back(vec3(0.0f, -1.0f, 0.0f)); + vertices.emplace_back(vec3(v.x, -0.5f, v.z), vec3(0.0f, -1.0f, 0.0f), vec2(-v.x, v.z) + vec2(0.5f), vec3(-1.0f, 0.0f, 0.0f)); } Index baseVertex = 0; for (uint32_t i = 2; i < N; i++) { @@ -412,15 +517,16 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver vec3 topRight = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(right.x, 0.5f, right.z)); vec3 bottomLeft = vec3(left.x, -0.5f, left.z); vec3 bottomRight = vec3(right.x, -0.5f, right.z); + vec3 tangent = glm::normalize(bottomLeft - bottomRight); - vertices.push_back(topLeft); - vertices.push_back(normal); - vertices.push_back(bottomLeft); - vertices.push_back(normal); - vertices.push_back(topRight); - vertices.push_back(normal); - vertices.push_back(bottomRight); - vertices.push_back(normal); + // Our tex coords go in the opposite direction as our vertices + float u = 1.0f - (float)i / (float)N; + float u2 = 1.0f - (float)(i + 1) / (float)N; + + vertices.emplace_back(topLeft, normal, vec2(u, 0.0f), tangent); + vertices.emplace_back(bottomLeft, normal, vec2(u, 1.0f), tangent); + vertices.emplace_back(topRight, normal, vec2(u2, 0.0f), tangent); + vertices.emplace_back(bottomRight, normal, vec2(u2, 1.0f), tangent); solidIndices.push_back(baseVertex + 0); solidIndices.push_back(baseVertex + 2); @@ -439,41 +545,6 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver shapeData.setupIndices(indexBuffer, solidIndices, wireIndices); } -void drawCircle(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { - // Draw a circle with radius 1/4th the size of the bounding box - using namespace geometry; - - VertexVector vertices; - IndexVector solidIndices, wireIndices; - const int NUM_CIRCLE_VERTICES = 64; - - std::vector shape = polygon(); - for (const vec3& v : shape) { - vertices.push_back(vec3(v.x, 0.0f, v.z)); - vertices.push_back(vec3(0.0f, 0.0f, 0.0f)); - } - - Index baseVertex = 0; - for (uint32_t i = 2; i < NUM_CIRCLE_VERTICES; i++) { - solidIndices.push_back(baseVertex + 0); - solidIndices.push_back(baseVertex + i); - solidIndices.push_back(baseVertex + i - 1); - solidIndices.push_back(baseVertex + NUM_CIRCLE_VERTICES); - solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES - 1); - solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES); - } - - for (uint32_t i = 1; i <= NUM_CIRCLE_VERTICES; i++) { - wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES)); - wireIndices.push_back(baseVertex + i - 1); - wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES) + NUM_CIRCLE_VERTICES); - wireIndices.push_back(baseVertex + (i - 1) + NUM_CIRCLE_VERTICES); - } - - shapeData.setupVertices(vertexBuffer, vertices); - shapeData.setupIndices(indexBuffer, solidIndices, wireIndices); -} - // FIXME solids need per-face vertices, but smooth shaded // components do not. Find a way to support using draw elements // or draw arrays as appropriate @@ -506,9 +577,9 @@ void GeometryCache::buildShapes() { // Line { ShapeData& shapeData = _shapes[Line]; - shapeData.setupVertices(_shapeVertices, VertexVector { - vec3(-0.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f), - vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f) + shapeData.setupVertices(_shapeVertices, std::vector { + ShapeVertex(vec3(-0.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f), vec2(0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f)), + ShapeVertex(vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f), vec2(0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f)) }); IndexVector wireIndices; // Only two indices @@ -572,6 +643,8 @@ gpu::Stream::FormatPointer& getSolidStreamFormat() { SOLID_STREAM_FORMAT = std::make_shared(); // 1 for everyone SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); + SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); + SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); } return SOLID_STREAM_FORMAT; } @@ -581,6 +654,8 @@ gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() { INSTANCED_SOLID_STREAM_FORMAT = std::make_shared(); // 1 for everyone INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); + INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); + INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return INSTANCED_SOLID_STREAM_FORMAT; @@ -591,6 +666,8 @@ gpu::Stream::FormatPointer& getInstancedSolidFadeStreamFormat() { INSTANCED_SOLID_FADE_STREAM_FORMAT = std::make_shared(); // 1 for everyone INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); + INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); + INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD2, gpu::Stream::TEXCOORD2, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD3, gpu::Stream::TEXCOORD3, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); @@ -893,6 +970,7 @@ void GeometryCache::updateVertices(int id, const QVector& points, con details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); + // TODO: circle3D overlays use this to define their vertices, so they need tex coords details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 3c6b85bed1..e0ba99b09e 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -354,13 +354,24 @@ public: /// Set a batch to the simple pipeline, returning the previous pipeline void useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend = false); + struct ShapeVertex { + ShapeVertex(const vec3& pos, const vec3& normal, const vec2& uv, const vec3& tangent) : pos(pos), normal(normal), uv(uv), tangent(tangent) {} + + vec3 pos; + vec3 normal; + vec2 uv; + vec3 tangent; + }; + struct ShapeData { gpu::BufferView _positionView; gpu::BufferView _normalView; + gpu::BufferView _texCoordView; + gpu::BufferView _tangentView; gpu::BufferView _indicesView; gpu::BufferView _wireIndicesView; - void setupVertices(gpu::BufferPointer& vertexBuffer, const geometry::VertexVector& vertices); + void setupVertices(gpu::BufferPointer& vertexBuffer, const std::vector& vertices); void setupIndices(gpu::BufferPointer& indexBuffer, const geometry::IndexVector& indices, const geometry::IndexVector& wireIndices); void setupBatch(gpu::Batch& batch) const; void draw(gpu::Batch& batch) const; diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index b3662385b0..26bb85a853 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -148,25 +148,25 @@ vec3 fetchLightmapMap(vec2 uv) { } <@endfunc@> -<@func tangentToViewSpace(fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> +<@func evalMaterialNormal(fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> { vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz); vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz); - vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); + vec3 normalizedBitangent = cross(normalizedNormal, normalizedTangent); vec3 localNormal = <$fetchedNormal$>; - <$normal$> = vec3(normalizedTangent * localNormal.x + normalizedNormal * localNormal.y + normalizedBitangent * localNormal.z); + <$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z); } <@endfunc@> -<@func tangentToViewSpaceLOD(fragPos, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> +<@func evalMaterialNormalLOD(fragPos, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> { vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz); vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz); - vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); + vec3 normalizedBitangent = 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 + // 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.0, 100.0, (-<$fragPos$>).z)); - <$normal$> = vec3(normalizedTangent * localNormal.x + normalizedNormal * localNormal.y + normalizedBitangent * localNormal.z); + <$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z); } <@endfunc@> diff --git a/libraries/render-utils/src/forward_model_normal_map.slf b/libraries/render-utils/src/forward_model_normal_map.slf index b32ed862d6..0ba464d3f0 100644 --- a/libraries/render-utils/src/forward_model_normal_map.slf +++ b/libraries/render-utils/src/forward_model_normal_map.slf @@ -58,7 +58,7 @@ void main(void) { vec3 fragPosition = _position.xyz; vec3 fragNormal; - <$tangentToViewSpace(normalTex, _normal, _tangent, fragNormal)$> + <$evalMaterialNormal(normalTex, _normal, _tangent, fragNormal)$> TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/model_lightmap_normal_map.slf b/libraries/render-utils/src/model_lightmap_normal_map.slf index 8734ea74b8..eecde59e54 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map.slf +++ b/libraries/render-utils/src/model_lightmap_normal_map.slf @@ -33,11 +33,11 @@ void main(void) { <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, normalTexel, metallicTex)$> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$> - vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTexel, _normal, _tangent, viewNormal)$> + vec3 fragNormal; + <$evalMaterialNormalLOD(_position, normalTexel, _normal, _tangent, fragNormal)$> packDeferredFragmentLightmap( - normalize(viewNormal.xyz), + normalize(fragNormal.xyz), evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), getMaterialAlbedo(mat) * albedo.rgb * _color, getMaterialRoughness(mat) * roughness, diff --git a/libraries/render-utils/src/model_lightmap_normal_map_fade.slf b/libraries/render-utils/src/model_lightmap_normal_map_fade.slf index e6cb35ec4f..af497f90c6 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map_fade.slf +++ b/libraries/render-utils/src/model_lightmap_normal_map_fade.slf @@ -43,11 +43,11 @@ void main(void) { <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, normalTexel, metallicTex)$> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$> - vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTexel, _normal, _tangent, viewNormal)$> + vec3 fragNormal; + <$evalMaterialNormalLOD(_position, normalTexel, _normal, _tangent, fragNormal)$> packDeferredFragmentLightmap( - normalize(viewNormal.xyz), + normalize(fragNormal.xyz), evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), getMaterialAlbedo(mat) * albedo.rgb * _color, getMaterialRoughness(mat) * roughness, diff --git a/libraries/render-utils/src/model_normal_map.slf b/libraries/render-utils/src/model_normal_map.slf index 82f667bf73..49613eca6a 100644 --- a/libraries/render-utils/src/model_normal_map.slf +++ b/libraries/render-utils/src/model_normal_map.slf @@ -46,8 +46,8 @@ void main(void) { vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; - vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + vec3 fragNormal; + <$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$> float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; @@ -56,7 +56,7 @@ void main(void) { <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; packDeferredFragment( - normalize(viewNormal.xyz), + normalize(fragNormal.xyz), opacity, albedo, roughness, diff --git a/libraries/render-utils/src/model_normal_map_fade.slf b/libraries/render-utils/src/model_normal_map_fade.slf index 67bea98cf0..bf6222652c 100644 --- a/libraries/render-utils/src/model_normal_map_fade.slf +++ b/libraries/render-utils/src/model_normal_map_fade.slf @@ -56,8 +56,8 @@ void main(void) { vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; - vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + vec3 fragNormal; + <$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$> float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; @@ -65,7 +65,7 @@ void main(void) { float scattering = getMaterialScattering(mat); packDeferredFragment( - normalize(viewNormal.xyz), + normalize(fragNormal.xyz), opacity, albedo, roughness, diff --git a/libraries/render-utils/src/model_translucent_normal_map.slf b/libraries/render-utils/src/model_translucent_normal_map.slf index 759007d93e..52015660c6 100644 --- a/libraries/render-utils/src/model_translucent_normal_map.slf +++ b/libraries/render-utils/src/model_translucent_normal_map.slf @@ -61,7 +61,7 @@ void main(void) { vec3 fragPosition = _position.xyz; vec3 fragNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, fragNormal)$> + <$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$> TransformCamera cam = getTransformCamera(); vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); diff --git a/libraries/render-utils/src/model_translucent_normal_map_fade.slf b/libraries/render-utils/src/model_translucent_normal_map_fade.slf index 204b5ac56b..c6c0e16812 100644 --- a/libraries/render-utils/src/model_translucent_normal_map_fade.slf +++ b/libraries/render-utils/src/model_translucent_normal_map_fade.slf @@ -71,7 +71,7 @@ void main(void) { vec3 fragPosition = _position.xyz; // Lighting is done in world space vec3 fragNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, fragNormal)$> + <$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$> TransformCamera cam = getTransformCamera(); vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index ac402a549f..b5c76257ef 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -81,7 +81,7 @@ const QString& PathUtils::resourcesPath() { #else staticResourcePath = ":/"; #endif - + #if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) if (USE_SOURCE_TREE_RESOURCES()) { // For dev builds, optionally load content from the Git source tree @@ -120,6 +120,31 @@ QUrl PathUtils::resourcesUrl(const QString& relativeUrl) { return QUrl(resourcesUrl() + relativeUrl); } +QUrl PathUtils::expandToLocalDataAbsolutePath(const QUrl& fileUrl) { + QString path = fileUrl.path(); + + if (path.startsWith("/~/")) { + // this results in a qrc:// url... + // return resourcesUrl(path.mid(3)); + +#ifdef Q_OS_MAC + static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; +#elif defined (ANDROID) + static const QString staticResourcePath = + QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources/"; +#else + static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/"; +#endif + path.replace(0, 3, staticResourcePath); + QUrl expandedURL = QUrl::fromLocalFile(path); + return expandedURL; + } + + QUrl::fromLocalFile(resourcesPath()).toString(); + + return fileUrl; +} + const QString& PathUtils::qmlBaseUrl() { static const QString staticResourcePath = resourcesUrl() + "qml/"; return staticResourcePath; diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index ec71ccee6a..d879ac968d 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -37,6 +37,7 @@ public: static QUrl resourcesUrl(const QString& relative); static const QString& resourcesPath(); static const QString& qmlBaseUrl(); + static QUrl expandToLocalDataAbsolutePath(const QUrl& fileUrl); static QUrl qmlUrl(const QString& relative); #ifdef DEV_BUILD static const QString& projectRootPath(); diff --git a/libraries/shared/src/shared/Shapes.cpp b/libraries/shared/src/shared/Shapes.cpp index dabdf21202..afcf956165 100644 --- a/libraries/shared/src/shared/Shapes.cpp +++ b/libraries/shared/src/shared/Shapes.cpp @@ -8,8 +8,11 @@ #include "Shapes.h" +#include "qmath.h" + namespace geometry { +using glm::vec2; using glm::vec3; // The golden ratio @@ -19,8 +22,8 @@ Solid<3> tesselate(const Solid<3>& solid_, int count) { Solid<3> solid = solid_; float length = glm::length(solid.vertices[0]); for (int i = 0; i < count; ++i) { - Solid<3> result { solid.vertices, {} }; - result.vertices.reserve(solid.vertices.size() + solid.faces.size() * 3); + Solid<3> result { solid.vertices, {}, {} }; + result.vertices.reserve(solid.vertices.size() + solid.faces.size() * 6); for (size_t f = 0; f < solid.faces.size(); ++f) { Index baseVertex = (Index)result.vertices.size(); const Face<3>& oldFace = solid.faces[f]; @@ -30,13 +33,26 @@ Solid<3> tesselate(const Solid<3>& solid_, int count) { vec3 ab = glm::normalize(a + b) * length; vec3 bc = glm::normalize(b + c) * length; vec3 ca = glm::normalize(c + a) * length; + + result.vertices.push_back(a); + result.vertices.push_back(ab); + result.vertices.push_back(ca); + result.faces.push_back(Face<3>{ { baseVertex, baseVertex + 1, baseVertex + 2 } }); + + result.vertices.push_back(ab); + result.vertices.push_back(b); + result.vertices.push_back(bc); + result.faces.push_back(Face<3>{ { baseVertex + 3, baseVertex + 4, baseVertex + 5 } }); + + result.vertices.push_back(bc); + result.vertices.push_back(c); + result.vertices.push_back(ca); + result.faces.push_back(Face<3>{ { baseVertex + 6, baseVertex + 7, baseVertex + 8 } }); + result.vertices.push_back(ab); result.vertices.push_back(bc); result.vertices.push_back(ca); - result.faces.push_back(Face<3>{ { oldFace[0], baseVertex, baseVertex + 2 } }); - result.faces.push_back(Face<3>{ { baseVertex, oldFace[1], baseVertex + 1 } }); - result.faces.push_back(Face<3>{ { baseVertex + 1, oldFace[2], baseVertex + 2 } }); - result.faces.push_back(Face<3>{ { baseVertex, baseVertex + 1, baseVertex + 2 } }); + result.faces.push_back(Face<3>{ { baseVertex + 9, baseVertex + 10, baseVertex + 11 } }); } solid = result; } @@ -50,6 +66,10 @@ const Solid<3>& tetrahedron() { static const auto D = vec3(-1, -1, 1); static const Solid<3> TETRAHEDRON = Solid<3>{ { A, B, C, D }, + { vec2(0.75f, 0.5f), vec2(0.5f, 0.0f), vec2(0.25f, 0.5f), + vec2(0.5f, 1.0f), vec2(1.0f, 1.0f), vec2(0.75f, 0.5f), + vec2(0.25f, 0.5f), vec2(0.5f, 1.0f), vec2(0.75f, 0.5f), + vec2(0.25f, 0.5f), vec2(0.0f, 1.0f), vec2(0.5f, 1.0f) }, FaceVector<3>{ Face<3> { { 0, 1, 2 } }, Face<3> { { 3, 1, 0 } }, @@ -65,8 +85,15 @@ const Solid<4>& cube() { static const auto B = vec3(-1, 1, 1); static const auto C = vec3(-1, 1, -1); static const auto D = vec3(1, 1, -1); + static const float THIRD = 1.0f / 3.0f; static const Solid<4> CUBE = Solid<4>{ { A, B, C, D, -A, -B, -C, -D }, + { vec2(0.5f, 0.0f), vec2(0.25f, 0.0f), vec2(0.25f, THIRD), vec2(0.5f, THIRD), + vec2(0.5f, THIRD), vec2(0.25f, THIRD), vec2(0.25f, 2.0f * THIRD), vec2(0.5f, 2.0f * THIRD), + vec2(0.25f, THIRD), vec2(0.0f, THIRD), vec2(0.0f, 2.0f * THIRD), vec2(0.25f, 2.0f * THIRD), + vec2(1.0f, THIRD), vec2(0.75f, THIRD), vec2(0.75f, 2.0f * THIRD), vec2(1.0f, 2.0f * THIRD), + vec2(0.75f, THIRD), vec2(0.5f, THIRD), vec2(0.5f, 2.0f * THIRD), vec2(0.75f, 2.0f * THIRD), + vec2(0.25f, 1.0f), vec2(0.5f, 1.0f), vec2(0.5f, 2.0f * THIRD), vec2(0.25f, 2.0f * THIRD) }, FaceVector<4>{ Face<4> { { 3, 2, 1, 0 } }, Face<4> { { 0, 1, 7, 6 } }, @@ -86,8 +113,18 @@ const Solid<3>& octahedron() { static const auto D = vec3(0, 0, -1); static const auto E = vec3(1, 0, 0); static const auto F = vec3(-1, 0, 0); + static const float THIRD = 1.0f / 3.0f; + static const float SEVENTH = 1.0f / 7.0f; static const Solid<3> OCTAHEDRON = Solid<3>{ { A, B, C, D, E, F}, + { vec2(2.0f * SEVENTH, THIRD), vec2(SEVENTH, 2.0f * THIRD), vec2(3.0f * SEVENTH, 2.0f * THIRD), + vec2(2.0f * SEVENTH, THIRD), vec2(3.0f * SEVENTH, 2.0f * THIRD), vec2(4.0f * SEVENTH, THIRD), + vec2(5.0f * SEVENTH, 0.0f), vec2(4.0f * SEVENTH, THIRD), vec2(6.0f * SEVENTH, THIRD), + vec2(2.0f * SEVENTH, THIRD), vec2(0.0f, THIRD), vec2(1.0f * SEVENTH, 2.0f * THIRD), + vec2(2.0f * SEVENTH, 1.0f), vec2(3.0f * SEVENTH, 2.0f * THIRD), vec2(1.0f * SEVENTH, 2.0f * THIRD), + vec2(5.0f * SEVENTH, 2.0f * THIRD), vec2(4.0f * SEVENTH, THIRD), vec2(3.0f * SEVENTH, 2.0f * THIRD), + vec2(5.0f * SEVENTH, 2.0f * THIRD), vec2(6.0f * SEVENTH, THIRD), vec2(4.0f * SEVENTH, THIRD), + vec2(5.0f * SEVENTH, 2.0f * THIRD), vec2(1.0f, 2.0f * THIRD), vec2(6.0f * SEVENTH, THIRD) }, FaceVector<3> { Face<3> { { 0, 2, 4, } }, Face<3> { { 0, 4, 3, } }, @@ -116,11 +153,52 @@ const Solid<5>& dodecahedron() { static const vec3 I = vec3(0, -IP, P); static const vec3 J = vec3(P, 0, IP); + + /* _ + / \ | + / \ y2 + / \ | + / \ _ + \ / | + \ / y1 + \ / | + ___________ _ + |x3|- - x1 - -||x3| + |- - - - x2- - - -| + */ + + // x1, x2, and x3 are the solutions to the following system of equations: + // 1 = 3 * x1 + 3 * x2 + x3 + // x1 + 2 * x3 = (golden ratio) * x1 + // x2 = x1 + 2 * x3 + static const float x1 = 4.0f / (17.0f + 7.0f * sqrtf(5.0f)); + static const float x2 = (1.0f / 11.0f) * (5.0f * sqrtf(5.0f) - 9.0f); + static const float x2_2 = x2 / 2.0f; + static const float x3 = (1.0f / 11.0f) * (6.0f * sqrtf(5.0f) - 13.0f); + // y1 and y2 are the solutions to the following system of equations (x is the sidelength, but is different than x1 because the scale in the y direction is different): + // 1 = 3 * y1 + 2 * y2 + // y1 = sin(108 deg) * x + // y1 + y2 = x * sqrtf(5 + 2 * sqrtf(5)) / 2 + static const float y1 = sqrtf(2.0f * (5.0f + sqrtf(5.0f))) / (sqrtf(2.0f * (5.0f + sqrtf(5.0f))) + 4.0f * sqrtf(5.0f + 2.0f * sqrtf(5.0f))); + static const float y2 = -(sqrtf(2.0f * (5.0f + sqrtf(5.0f))) - 2.0f * sqrtf(5.0f + 2.0f * sqrtf(5.0f))) / (sqrtf(2.0f * (5.0f + sqrtf(5.0f))) + 4.0f * sqrtf(5.0f + 2.0f * sqrtf(5.0f))); + static const Solid<5> DODECAHEDRON = Solid<5>{ { A, B, C, D, E, F, G, H, I, J, -A, -B, -C, -D, -E, -F, -G, -H, -I, -J, }, + { vec2(x1 + x2_2, 0.0f), vec2(x2_2, 0.0f), vec2(x2_2 - x3, y1), vec2(x3 + x1, y1 + y2), vec2(x3 + x1 + x2_2, y1), + vec2(1.0f - (x2 - x3 + x2_2), 0.0f), vec2(1.0f - (x2 + x1 + x3), y2), vec2(1.0f - (x2 + x1), y1 + y2), vec2(1.0f - x2, y1 + y2), vec2(1.0f - (x2 - x3), y2), + vec2(1.0f - x2_2, y1), vec2(1.0f - x2, y1 + y2), vec2(1.0f - (x3 + x1), y1 + y2 + y1), vec2(1.0f - x3, y1 + y2 + y1), vec2(1.0f, y1 + y2), + vec2(x3, y1 + y2), vec2(0.0f, y1 + y2 + y1), vec2(x2_2, y1 + y2 + y1 + y2), vec2(x2, y1 + y2 + y1), vec2(x1 + x3, y1 + y2), + vec2(x3 + x1, y1 + y2), vec2(x2, y1 + y2 + y1), vec2(x2 + x1, y1 + y2 + y1), vec2(x3 + x1 + x2, y1 + y2), vec2(x3 + x1 + x2_2, y1), + vec2(x3 + x1 + x2_2, y1), vec2(x3 + x1 + x2, y1 + y2), vec2(x3 + x1 + x2 + x2_2, y1), vec2(x1 + x2 + x2_2, 0.0f), vec2(x3 + x1 + x2_2 + x3, 0.0f), + vec2(1.0f - (x3 + x1 + x2_2), 1.0f - y1), vec2(1.0f - (x3 + x1 + x2), 1.0f - (y1 + y2)), vec2(1.0f - (x3 + x1 + x2 + x2_2), 1.0f - y1), vec2(1.0f - (x1 + x2 + x2_2), 1.0f), vec2(1.0f - (x3 + x1 + x2_2 + x3), 1.0f), + vec2(x2 + x1 + x3, 1.0f - y2), vec2(x2 + x1, 1.0f - (y1 + y2)), vec2(x2, 1.0f - (y1 + y2)), vec2(x2 - x3, 1.0f - y2), vec2(x2 - x3 + x2_2, 1.0f), + vec2(x2 + x1 + x2, y1 + y2 + y1), vec2(x3 + x1 + x2 + x1, y1 + y2), vec2(x3 + x1 + x2, y1 + y2), vec2(x2 + x1, y1 + y2 + y1), vec2(x2 + x1 + x2_2, y1 + y2 + y1 + y2), + vec2(1.0f - (x3 + x1 + x2), y1 + y2 + y1), vec2(1.0f - (x2 + x1), y1 + y2), vec2(1.0f - (x2 + x1 + x2_2), y1), vec2(1.0f - (x2 + x1 + x2), y1 + y2), vec2(1.0f - (x3 + x1 + x2 + x1), y1 + y2 + y1), + vec2(1.0f - (x3 + x1 + x2_2), y1 + y2 + y1 + y2), vec2(1.0f - (x3 + x1), y1 + y2 + y1), vec2(1.0f - x2, y1 + y2), vec2(1.0f - (x2 + x1), y2 + y1), vec2(1.0f - (x3 + x1 + x2), y1 + y2 + y1), + vec2(1.0f - (x1 + x2_2), 1.0f), vec2(1.0f - x2_2, 1.0f), vec2(1.0f - (x2_2 - x3), 1.0f - y1), vec2(1.0f - (x1 + x3), 1.0f - (y1 + y2)), vec2(1.0f - (x3 + x1 + x2_2), 1.0f - y1) }, FaceVector<5> { Face<5> { { 0, 1, 2, 3, 4 } }, Face<5> { { 0, 5, 18, 6, 1 } }, @@ -148,12 +226,33 @@ const Solid<3>& icosahedron() { static const auto D = vec3(P, 0, N); static const auto E = vec3(P, 0, -N); static const auto F = vec3(0, N, -P); - + static const float THIRD = 1.0f / 3.0f; + static const float ELEVENTH = 1.0f / 11.0f; static const Solid<3> ICOSAHEDRON = Solid<3> { { A, B, C, D, E, F, -A, -B, -C, -D, -E, -F, }, + { vec2(3.0f * ELEVENTH, 0.0f), vec2(2.0f * ELEVENTH, THIRD), vec2(4.0f * ELEVENTH, THIRD), + vec2(2.0f * ELEVENTH, THIRD), vec2(3.0f * ELEVENTH, 2.0f * THIRD), vec2(4.0f * ELEVENTH, THIRD), + vec2(3.0f * ELEVENTH, 2.0f * THIRD), vec2(5.0f * ELEVENTH, 2.0f * THIRD), vec2(4.0f * ELEVENTH, THIRD), + vec2(5.0f * ELEVENTH, 2.0f * THIRD), vec2(6.0f * ELEVENTH, THIRD), vec2(4.0f * ELEVENTH, THIRD), + vec2(6.0f * ELEVENTH, THIRD), vec2(5.0f * ELEVENTH, 0.0f), vec2(4.0f * ELEVENTH, THIRD), + vec2(1.0f * ELEVENTH, 0.0f), vec2(0.0f, THIRD), vec2(2.0f * ELEVENTH, THIRD), + vec2(1.0f * ELEVENTH, 2.0f * THIRD), vec2(2.0f * ELEVENTH, THIRD), vec2(0.0f, THIRD), + vec2(2.0f * ELEVENTH, THIRD), vec2(1.0f * ELEVENTH, 2.0f * THIRD), vec2(3.0f * ELEVENTH, 2.0f * THIRD), + vec2(2.0f * ELEVENTH, 1.0f), vec2(3.0f * ELEVENTH, 2.0f * THIRD), vec2(1.0f * ELEVENTH, 2.0f * THIRD), + vec2(3.0f * ELEVENTH, 2.0f * THIRD), vec2(4.0f * ELEVENTH, 1.0f), vec2(5.0f * ELEVENTH, 2.0f * THIRD), + vec2(7.0f * ELEVENTH, 2.0f * THIRD), vec2(5.0f * ELEVENTH, 2.0f * THIRD), vec2(6.0f * ELEVENTH, 1.0f), + vec2(5.0f * ELEVENTH, 2.0f * THIRD), vec2(7.0f * ELEVENTH, 2.0f * THIRD), vec2(6.0f * ELEVENTH, THIRD), + vec2(8.0f * ELEVENTH, THIRD), vec2(6.0f * ELEVENTH, THIRD), vec2(7.0f * ELEVENTH, 2.0f * THIRD), + vec2(6.0f * ELEVENTH, THIRD), vec2(8.0f * ELEVENTH, THIRD), vec2(7.0f * ELEVENTH, 0.0f), + vec2(10.0f * ELEVENTH, THIRD), vec2(9.0f * ELEVENTH, 0.0f), vec2(8.0f * ELEVENTH, THIRD), + vec2(7.0f * ELEVENTH, 2.0f * THIRD), vec2(8.0f * ELEVENTH, 1.0f), vec2(9.0f * ELEVENTH, 2.0f * THIRD), + vec2(8.0f * ELEVENTH, THIRD), vec2(7.0f * ELEVENTH, 2.0f * THIRD), vec2(9.0f * ELEVENTH, 2.0f * THIRD), + vec2(10.0f * ELEVENTH, THIRD), vec2(8.0f * ELEVENTH, THIRD), vec2(9.0f * ELEVENTH, 2.0f * THIRD), + vec2(1.0f, 2.0f * THIRD), vec2(10.0f * ELEVENTH, THIRD), vec2(9.0f * ELEVENTH, 2.0f * THIRD), + vec2(10.0f * ELEVENTH, 1.0f), vec2(1.0f, 2.0f * THIRD), vec2(9.0f * ELEVENTH, 2.0f * THIRD) }, FaceVector<3> { Face<3> { { 1, 2, 0 } }, Face<3> { { 2, 3, 0 } }, diff --git a/libraries/shared/src/shared/Shapes.h b/libraries/shared/src/shared/Shapes.h index 3486a0a663..6bd2eab199 100644 --- a/libraries/shared/src/shared/Shapes.h +++ b/libraries/shared/src/shared/Shapes.h @@ -22,6 +22,7 @@ namespace geometry { using Index = uint32_t; using Vec = glm::vec3; using VertexVector = std::vector; + using TexCoordVector = std::vector; using IndexVector = std::vector; template @@ -33,6 +34,7 @@ namespace geometry { template struct Solid { VertexVector vertices; + TexCoordVector texCoords; FaceVector faces; Solid& fitDimension(float newMaxDimension) { diff --git a/plugins/hifiCodec/CMakeLists.txt b/plugins/hifiCodec/CMakeLists.txt index 28c1dc3807..9ecaf7b511 100644 --- a/plugins/hifiCodec/CMakeLists.txt +++ b/plugins/hifiCodec/CMakeLists.txt @@ -12,5 +12,7 @@ link_hifi_libraries(audio plugins) add_dependency_external_projects(hifiAudioCodec) target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${HIFIAUDIOCODEC_LIBRARIES}) -install_beside_console() +if (BUILD_SERVER) + install_beside_console() +endif () diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 638cadf574..893b7f48b1 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -19,7 +19,7 @@ if (WIN32 AND (NOT USE_GLES)) set(TARGET_NAME oculus) setup_hifi_plugin(Multimedia) link_hifi_libraries( - shared task gl gpu gpu-gl controllers ui qml + shared task gl gpu ${PLATFORM_GL_BACKEND} controllers ui qml plugins ui-plugins display-plugins input-plugins audio-client networking render-utils ${PLATFORM_GL_BACKEND} diff --git a/plugins/pcmCodec/CMakeLists.txt b/plugins/pcmCodec/CMakeLists.txt index 900a642a88..cce33ecd1a 100644 --- a/plugins/pcmCodec/CMakeLists.txt +++ b/plugins/pcmCodec/CMakeLists.txt @@ -9,5 +9,7 @@ set(TARGET_NAME pcmCodec) setup_hifi_client_server_plugin() link_hifi_libraries(shared plugins) -install_beside_console() +if (BUILD_SERVER) + install_beside_console() +endif () diff --git a/scripts/+android/defaultScripts.js b/scripts/+android/defaultScripts.js index 9094952e62..11aee6a9d2 100644 --- a/scripts/+android/defaultScripts.js +++ b/scripts/+android/defaultScripts.js @@ -15,8 +15,9 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/progress.js", "system/+android/touchscreenvirtualpad.js", "system/+android/bottombar.js", - "system/+android/audio.js", - "system/+android/modes.js"/*, + "system/+android/audio.js" , + "system/+android/modes.js", + "system/+android/stats.js"/*, "system/away.js", "system/controllers/controllerDisplayManager.js", "system/controllers/handControllerGrabAndroid.js", diff --git a/scripts/system/+android/stats.js b/scripts/system/+android/stats.js new file mode 100644 index 0000000000..a93bcb5794 --- /dev/null +++ b/scripts/system/+android/stats.js @@ -0,0 +1,39 @@ +"use strict"; +// +// stats.js +// scripts/system/ +// +// Created by Sam Gondelman on 3/14/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + +var statsbar; +var statsButton; + +function init() { + statsbar = new QmlFragment({ + qml: "hifi/StatsBar.qml" + }); + + statsButton = statsbar.addButton({ + icon: "icons/stats.svg", + activeIcon: "icons/stats.svg", + textSize: 45, + bgOpacity: 0.0, + activeBgOpacity: 0.0, + bgColor: "#FFFFFF", + text: "STATS" + }); + statsButton.clicked.connect(function() { + Menu.triggerOption("Stats"); + }); +} + +init(); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 26ffb08796..d2e7d3ffc8 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -698,6 +698,9 @@ Window.location = "hifi://BankOfHighFidelity"; } break; + case 'wallet_availableUpdatesReceived': + // NOP + break; default: print('Unrecognized message from QML:', JSON.stringify(message)); } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 5e9aefcb07..cfaf517487 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -777,9 +777,12 @@ function findClickedEntity(event) { } var pickRay = Camera.computePickRay(event.x, event.y); - var overlayResult = Overlays.findRayIntersection(pickRay, true, getMainTabletIDs()); - if (overlayResult.intersects) { - return null; + var tabletIDs = getMainTabletIDs(); + if (tabletIDs.length > 0) { + var overlayResult = Overlays.findRayIntersection(pickRay, true, tabletIDs); + if (overlayResult.intersects) { + return null; + } } var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking @@ -968,8 +971,13 @@ function mouseReleaseEvent(event) { function wasTabletClicked(event) { var rayPick = Camera.computePickRay(event.x, event.y); - var result = Overlays.findRayIntersection(rayPick, true, getMainTabletIDs()); - return result.intersects; + var tabletIDs = getMainTabletIDs(); + if (tabletIDs.length === 0) { + return false; + } else { + var result = Overlays.findRayIntersection(rayPick, true, getMainTabletIDs()); + return result.intersects; + } } function mouseClickEvent(event) { diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index fb49de1050..864c7d92b4 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -30,6 +30,7 @@ var userIsLoggedIn = false; var walletNeedsSetup = false; var marketplaceBaseURL = "https://highfidelity.com"; + var messagesWaiting = false; function injectCommonCode(isDirectoryPage) { @@ -205,16 +206,22 @@ purchasesElement.id = "purchasesButton"; purchasesElement.setAttribute('href', "#"); - purchasesElement.innerHTML = "My Purchases"; + purchasesElement.innerHTML = ""; + if (messagesWaiting) { + purchasesElement.innerHTML += " "; + } + purchasesElement.innerHTML += "My Purchases"; // FRONTEND WEBDEV RANT: The username dropdown should REALLY not be programmed to be on the same // line as the search bar, overlaid on top of the search bar, floated right, and then relatively bumped up using "top:-50px". + $('.navbar-brand').css('margin-right', '10px'); purchasesElement.style = "height:100%;margin-top:18px;font-weight:bold;float:right;margin-right:" + (dropDownElement.offsetWidth + 30) + "px;position:relative;z-index:999;"; navbarBrandElement.parentNode.insertAdjacentElement('beforeend', purchasesElement); $('#purchasesButton').on('click', function () { EventBridge.emitWebEvent(JSON.stringify({ type: "PURCHASES", - referrerURL: window.location.href + referrerURL: window.location.href, + hasUpdates: messagesWaiting })); }); } @@ -243,7 +250,7 @@ }); } - function buyButtonClicked(id, name, author, price, href, referrer) { + function buyButtonClicked(id, name, author, price, href, referrer, edition) { EventBridge.emitWebEvent(JSON.stringify({ type: "CHECKOUT", itemId: id, @@ -251,7 +258,8 @@ itemPrice: price ? parseInt(price, 10) : 0, itemHref: href, referrer: referrer, - itemAuthor: author + itemAuthor: author, + itemEdition: edition })); } @@ -319,7 +327,8 @@ $(this).closest('.grid-item').find('.creator').find('.value').text(), $(this).closest('.grid-item').find('.item-cost').text(), $(this).attr('data-href'), - "mainPage"); + "mainPage", + -1); }); } @@ -410,7 +419,11 @@ } var cost = $('.item-cost').text(); - if (availability !== 'available') { + var isUpdating = window.location.href.indexOf('edition=') > -1; + var urlParams = new URLSearchParams(window.location.search); + if (isUpdating) { + purchaseButton.html('UPDATE FOR FREE'); + } else if (availability !== 'available') { purchaseButton.html('UNAVAILABLE (' + availability + ')'); } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { purchaseButton.html('PURCHASE startThread(); const DomainHandler& domainHandler = nodeList->getDomainHandler(); - connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); + connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainChanged(QUrl))); connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ACClientApp::domainConnectionRefused); connect(nodeList.data(), &NodeList::nodeAdded, this, &ACClientApp::nodeAdded); @@ -169,7 +169,7 @@ void ACClientApp::domainConnectionRefused(const QString& reasonMessage, int reas qDebug() << "domainConnectionRefused"; } -void ACClientApp::domainChanged(const QString& domainHostname) { +void ACClientApp::domainChanged(QUrl domainURL) { if (_verbose) { qDebug() << "domainChanged"; } diff --git a/tools/ac-client/src/ACClientApp.h b/tools/ac-client/src/ACClientApp.h index d43e78eaeb..7a31b5ef5e 100644 --- a/tools/ac-client/src/ACClientApp.h +++ b/tools/ac-client/src/ACClientApp.h @@ -29,7 +29,7 @@ public: private slots: void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo); - void domainChanged(const QString& domainHostname); + void domainChanged(QUrl domainURL); void nodeAdded(SharedNodePointer node); void nodeActivated(SharedNodePointer node); void nodeKilled(SharedNodePointer node); diff --git a/tools/atp-client/src/ATPClientApp.cpp b/tools/atp-client/src/ATPClientApp.cpp index dbc2ad53f6..526065b2f7 100644 --- a/tools/atp-client/src/ATPClientApp.cpp +++ b/tools/atp-client/src/ATPClientApp.cpp @@ -158,7 +158,7 @@ ATPClientApp::ATPClientApp(int argc, char* argv[]) : nodeList->startThread(); const DomainHandler& domainHandler = nodeList->getDomainHandler(); - connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); + connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainChanged(QUrl))); connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ATPClientApp::domainConnectionRefused); connect(nodeList.data(), &NodeList::nodeAdded, this, &ATPClientApp::nodeAdded); @@ -227,7 +227,7 @@ void ATPClientApp::domainConnectionRefused(const QString& reasonMessage, int rea } } -void ATPClientApp::domainChanged(const QString& domainHostname) { +void ATPClientApp::domainChanged(QUrl domainURL) { if (_verbose) { qDebug() << "domainChanged"; } diff --git a/tools/atp-client/src/ATPClientApp.h b/tools/atp-client/src/ATPClientApp.h index 7ab4ec4a7a..63ee218e4b 100644 --- a/tools/atp-client/src/ATPClientApp.h +++ b/tools/atp-client/src/ATPClientApp.h @@ -31,7 +31,7 @@ public: private slots: void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo); - void domainChanged(const QString& domainHostname); + void domainChanged(QUrl domainURL); void nodeAdded(SharedNodePointer node); void nodeActivated(SharedNodePointer node); void nodeKilled(SharedNodePointer node); diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 549414cbab..71bb997303 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -18,4 +18,6 @@ elseif (APPLE) set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") endif() -install_beside_console() +if (BUILD_SERVER) + install_beside_console() +endif () diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp index f5af5455fb..35550cdca8 100644 --- a/tools/oven/src/BakerCLI.cpp +++ b/tools/oven/src/BakerCLI.cpp @@ -18,6 +18,7 @@ #include "ModelBakingLoggingCategory.h" #include "BakerCLI.h" #include "FBXBaker.h" +#include "JSBaker.h" #include "TextureBaker.h" BakerCLI::BakerCLI(OvenCLIApplication* parent) : QObject(parent) { @@ -34,6 +35,7 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& qDebug() << "Baking file type: " << type; static const QString MODEL_EXTENSION { "fbx" }; + static const QString SCRIPT_EXTENSION { "js" }; QString extension = type; @@ -44,6 +46,7 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& // check what kind of baker we should be creating bool isFBX = extension == MODEL_EXTENSION; + bool isScript = extension == SCRIPT_EXTENSION; bool isSupportedImage = QImageReader::supportedImageFormats().contains(extension.toLatin1()); @@ -57,12 +60,16 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& outputPath) }; _baker->moveToThread(Oven::instance().getNextWorkerThread()); + } else if (isScript) { + _baker = std::unique_ptr { new JSBaker(inputUrl, outputPath) }; + _baker->moveToThread(Oven::instance().getNextWorkerThread()); } else if (isSupportedImage) { _baker = std::unique_ptr { new TextureBaker(inputUrl, image::TextureUsage::CUBE_TEXTURE, outputPath) }; _baker->moveToThread(Oven::instance().getNextWorkerThread()); } else { qCDebug(model_baking) << "Failed to determine baker type for file" << inputUrl; QCoreApplication::exit(OVEN_STATUS_CODE_FAIL); + return; } // invoke the bake method on the baker thread diff --git a/tools/oven/src/BakerCLI.h b/tools/oven/src/BakerCLI.h index 4f5b6607b0..bf33b625dd 100644 --- a/tools/oven/src/BakerCLI.h +++ b/tools/oven/src/BakerCLI.h @@ -14,6 +14,7 @@ #include #include +#include #include @@ -31,6 +32,8 @@ class BakerCLI : public QObject { public: BakerCLI(OvenCLIApplication* parent); + +public slots: void bakeFile(QUrl inputUrl, const QString& outputPath, const QString& type = QString::null); private slots: diff --git a/tools/oven/src/Oven.cpp b/tools/oven/src/Oven.cpp index a9aa6907f1..c3fec2d15e 100644 --- a/tools/oven/src/Oven.cpp +++ b/tools/oven/src/Oven.cpp @@ -40,6 +40,8 @@ Oven::Oven() { } Oven::~Oven() { + DependencyManager::get()->cleanup(); + // quit all worker threads and wait on them for (auto& thread : _workerThreads) { thread->quit(); diff --git a/tools/oven/src/OvenCLIApplication.cpp b/tools/oven/src/OvenCLIApplication.cpp index 38d9963eeb..2fb8ea03f2 100644 --- a/tools/oven/src/OvenCLIApplication.cpp +++ b/tools/oven/src/OvenCLIApplication.cpp @@ -40,7 +40,8 @@ OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) : QUrl inputUrl(QDir::fromNativeSeparators(parser.value(CLI_INPUT_PARAMETER))); QUrl outputUrl(QDir::fromNativeSeparators(parser.value(CLI_OUTPUT_PARAMETER))); QString type = parser.isSet(CLI_TYPE_PARAMETER) ? parser.value(CLI_TYPE_PARAMETER) : QString::null; - cli->bakeFile(inputUrl, outputUrl.toString(), type); + QMetaObject::invokeMethod(cli, "bakeFile", Qt::QueuedConnection, Q_ARG(QUrl, inputUrl), + Q_ARG(QString, outputUrl.toString()), Q_ARG(QString, type)); } else { parser.showHelp(); QCoreApplication::quit();