mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 01:03:38 +02:00
Merge branch 'loginInitiative2' of https://github.com/wayne-chen/hifi into loginInitiative2
This commit is contained in:
commit
8e4837f8c8
37 changed files with 1014 additions and 620 deletions
|
@ -66,7 +66,7 @@ if (ANDROID)
|
|||
set(GLES_OPTION ON)
|
||||
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
|
||||
else ()
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets)
|
||||
set(PLATFORM_QT_COMPONENTS WebEngine)
|
||||
endif ()
|
||||
|
||||
if (USE_GLES AND (NOT ANDROID))
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
#include <QTemporaryDir>
|
||||
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
#include <gl/GLWindow.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
|
||||
#include <shared/FileUtils.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
|
@ -973,9 +975,11 @@ OffscreenGLCanvas* _qmlShareContext { nullptr };
|
|||
// and manually set THAT to be the shared context for the Chromium helper
|
||||
#if !defined(DISABLE_QML)
|
||||
OffscreenGLCanvas* _chromiumShareContext { nullptr };
|
||||
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
|
||||
#endif
|
||||
|
||||
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
|
||||
Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context();
|
||||
|
||||
Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 };
|
||||
|
||||
const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 60.0f;
|
||||
|
@ -1021,6 +1025,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
auto steamClient = PluginManager::getInstance()->getSteamClientPlugin();
|
||||
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
|
||||
setProperty(hifi::properties::CRASHED, _previousSessionCrashed);
|
||||
|
||||
{
|
||||
const QStringList args = arguments();
|
||||
|
||||
|
@ -1219,7 +1224,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &Application::domainConnectionRefused);
|
||||
|
||||
nodeList->getDomainHandler().setErrorDomainURL(QUrl(REDIRECT_HIFI_ADDRESS));
|
||||
nodeList->getDomainHandler().setLoginScreenDomainURL(QUrl(LOGIN_SCREEN_HIFI_ADDRESS));
|
||||
|
||||
// We could clear ATP assets only when changing domains, but it's possible that the domain you are connected
|
||||
// to has gone down and switched to a new content set, so when you reconnect the cached ATP assets will no longer be valid.
|
||||
|
@ -1374,7 +1378,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_glWidget->setMouseTracking(true);
|
||||
// Make sure the window is set to the correct size by processing the pending events
|
||||
QCoreApplication::processEvents();
|
||||
_glWidget->createContext();
|
||||
|
||||
// Create the main thread context, the GPU backend
|
||||
initializeGL();
|
||||
|
@ -1742,27 +1745,30 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
userInputMapper->registerDevice(_touchscreenVirtualPadDevice->getInputDevice());
|
||||
}
|
||||
|
||||
{
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||
// this will force the model the look at the correct directory (weird order of operations issue)
|
||||
scriptEngines->reloadLocalFiles();
|
||||
QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH);
|
||||
_defaultScriptsLocation = getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str());
|
||||
|
||||
// do this as late as possible so that all required subsystems are initialized
|
||||
// If we've overridden the default scripts location, just load default scripts
|
||||
// otherwise, load 'em all
|
||||
//{
|
||||
// auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||
// // this will force the model the look at the correct directory (weird order of operations issue)
|
||||
// scriptEngines->reloadLocalFiles();
|
||||
|
||||
// we just want to see if --scripts was set, we've already parsed it and done
|
||||
// the change in PathUtils. Rather than pass that in the constructor, lets just
|
||||
// look (this could be debated)
|
||||
QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH);
|
||||
QDir defaultScriptsLocation(getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str()));
|
||||
if (!defaultScriptsLocation.exists()) {
|
||||
scriptEngines->loadDefaultScripts();
|
||||
scriptEngines->defaultScriptsLocationOverridden(true);
|
||||
} else {
|
||||
scriptEngines->loadScripts();
|
||||
}
|
||||
}
|
||||
// // do this as late as possible so that all required subsystems are initialized
|
||||
// // If we've overridden the default scripts location, just load default scripts
|
||||
// // otherwise, load 'em all
|
||||
|
||||
// // we just want to see if --scripts was set, we've already parsed it and done
|
||||
// // the change in PathUtils. Rather than pass that in the constructor, lets just
|
||||
// // look (this could be debated)
|
||||
// QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH);
|
||||
// QDir defaultScriptsLocation(getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str()));
|
||||
// if (!defaultScriptsLocation.exists()) {
|
||||
// scriptEngines->loadDefaultScripts();
|
||||
// scriptEngines->defaultScriptsLocationOverridden(true);
|
||||
// } else {
|
||||
// scriptEngines->loadScripts();
|
||||
// }
|
||||
//}
|
||||
|
||||
// Make sure we don't time out during slow operations at startup
|
||||
updateHeartbeat();
|
||||
|
@ -2244,28 +2250,28 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
_snapshotSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/snapshot/snap.wav"));
|
||||
|
||||
QVariant testProperty = property(hifi::properties::TEST);
|
||||
qDebug() << testProperty;
|
||||
if (testProperty.isValid()) {
|
||||
const auto testScript = property(hifi::properties::TEST).toUrl();
|
||||
//QVariant testProperty = property(hifi::properties::TEST);
|
||||
//qDebug() << testProperty;
|
||||
//if (testProperty.isValid()) {
|
||||
// const auto testScript = property(hifi::properties::TEST).toUrl();
|
||||
|
||||
// Set last parameter to exit interface when the test script finishes, if so requested
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(testScript, false, false, false, false, quitWhenFinished);
|
||||
// // Set last parameter to exit interface when the test script finishes, if so requested
|
||||
// DependencyManager::get<ScriptEngines>()->loadScript(testScript, false, false, false, false, quitWhenFinished);
|
||||
|
||||
// This is done so we don't get a "connection time-out" message when we haven't passed in a URL.
|
||||
if (arguments().contains("--url")) {
|
||||
auto reply = SandboxUtils::getStatus();
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
||||
handleSandboxStatus(reply);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
PROFILE_RANGE(render, "GetSandboxStatus");
|
||||
auto reply = SandboxUtils::getStatus();
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
||||
handleSandboxStatus(reply);
|
||||
});
|
||||
}
|
||||
// // This is done so we don't get a "connection time-out" message when we haven't passed in a URL.
|
||||
// if (arguments().contains("--url")) {
|
||||
// auto reply = SandboxUtils::getStatus();
|
||||
// connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
||||
// handleSandboxStatus(reply);
|
||||
// });
|
||||
// }
|
||||
//} else {
|
||||
// PROFILE_RANGE(render, "GetSandboxStatus");
|
||||
// auto reply = SandboxUtils::getStatus();
|
||||
// connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
||||
// handleSandboxStatus(reply);
|
||||
// });
|
||||
//}
|
||||
|
||||
// Monitor model assets (e.g., from Clara.io) added to the world that may need resizing.
|
||||
static const int ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS = 1000;
|
||||
|
@ -2285,7 +2291,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(this, &QCoreApplication::aboutToQuit, this, &Application::addAssetToWorldMessageClose);
|
||||
connect(&domainHandler, &DomainHandler::domainURLChanged, this, &Application::addAssetToWorldMessageClose);
|
||||
connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &Application::addAssetToWorldMessageClose);
|
||||
connect(&domainHandler, &DomainHandler::redirectToLoginScreenDomainURL, this, &Application::addAssetToWorldMessageClose);
|
||||
|
||||
updateSystemTabletMode();
|
||||
|
||||
|
@ -2341,6 +2346,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground);
|
||||
AndroidHelper::instance().notifyLoadComplete();
|
||||
#endif
|
||||
pauseUntilLoginDetermined();
|
||||
}
|
||||
|
||||
void Application::updateVerboseLogging() {
|
||||
|
@ -2718,46 +2724,66 @@ void Application::initializeGL() {
|
|||
_isGLInitialized = true;
|
||||
}
|
||||
|
||||
if (!_glWidget->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make window context current");
|
||||
}
|
||||
_glWidget->windowHandle()->setFormat(getDefaultOpenGLSurfaceFormat());
|
||||
|
||||
// When loading QtWebEngineWidgets, it creates a global share context on startup.
|
||||
// We have to account for this possibility by checking here for an existing
|
||||
// global share context
|
||||
auto globalShareContext = qt_gl_global_share_context();
|
||||
|
||||
#if !defined(DISABLE_QML)
|
||||
// Build a shared canvas / context for the Chromium processes
|
||||
{
|
||||
// Disable signed distance field font rendering on ATI/AMD GPUs, due to
|
||||
// https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app
|
||||
std::string vendor{ (const char*)glGetString(GL_VENDOR) };
|
||||
if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) {
|
||||
qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text"));
|
||||
}
|
||||
|
||||
if (!globalShareContext) {
|
||||
// Chromium rendering uses some GL functions that prevent nSight from capturing
|
||||
// frames, so we only create the shared context if nsight is NOT active.
|
||||
if (!nsightActive()) {
|
||||
_chromiumShareContext = new OffscreenGLCanvas();
|
||||
_chromiumShareContext->setObjectName("ChromiumShareContext");
|
||||
_chromiumShareContext->create(_glWidget->qglContext());
|
||||
auto format =QSurfaceFormat::defaultFormat();
|
||||
#ifdef Q_OS_MAC
|
||||
// On mac, the primary shared OpenGL context must be a 3.2 core context,
|
||||
// or chromium flips out and spews error spam (but renders fine)
|
||||
format.setMajorVersion(3);
|
||||
format.setMinorVersion(2);
|
||||
#endif
|
||||
_chromiumShareContext->setFormat(format);
|
||||
_chromiumShareContext->create();
|
||||
if (!_chromiumShareContext->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make chromium shared context current");
|
||||
}
|
||||
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
|
||||
globalShareContext = _chromiumShareContext->getContext();
|
||||
qt_gl_set_global_share_context(globalShareContext);
|
||||
_chromiumShareContext->doneCurrent();
|
||||
// Restore the GL widget context
|
||||
if (!_glWidget->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make window context current");
|
||||
}
|
||||
} else {
|
||||
qCWarning(interfaceapp) << "nSight detected, disabling chrome rendering";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
_glWidget->createContext(globalShareContext);
|
||||
|
||||
if (!_glWidget->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make window context current");
|
||||
}
|
||||
|
||||
#if !defined(DISABLE_QML)
|
||||
// Disable signed distance field font rendering on ATI/AMD GPUs, due to
|
||||
// https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app
|
||||
std::string vendor{ (const char*)glGetString(GL_VENDOR) };
|
||||
if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) {
|
||||
qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text"));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!globalShareContext) {
|
||||
globalShareContext = _glWidget->qglContext();
|
||||
qt_gl_set_global_share_context(globalShareContext);
|
||||
}
|
||||
|
||||
// Build a shared canvas / context for the QML rendering
|
||||
{
|
||||
_qmlShareContext = new OffscreenGLCanvas();
|
||||
_qmlShareContext->setObjectName("QmlShareContext");
|
||||
_qmlShareContext->create(_glWidget->qglContext());
|
||||
_qmlShareContext->create(globalShareContext);
|
||||
if (!_qmlShareContext->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make QML shared context current");
|
||||
}
|
||||
|
@ -2891,18 +2917,14 @@ void Application::showLoginScreen() {
|
|||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
if (!accountManager->isLoggedIn()) {
|
||||
// dialogsManager->showLoginScreenDialog();
|
||||
_loginDialogPoppedUp = true;
|
||||
dialogsManager->showLoginDialog();
|
||||
QJsonObject loginData = {};
|
||||
loginData["action"] = "login dialog shown";
|
||||
UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData);
|
||||
if (qApp->isHMDMode()) {
|
||||
// create web overlay.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto loginScreenDomainURL = nodeList->getDomainHandler().getLoginScreenDomainURL();
|
||||
goToLoginScreenDomainURL(loginScreenDomainURL);
|
||||
}
|
||||
_window->setWindowTitle("High Fidelity Interface");
|
||||
} else {
|
||||
resumeAfterLoginDialogActionTaken();
|
||||
}
|
||||
_loginDialogPoppedUp = !accountManager->isLoggedIn();
|
||||
loginDialogPoppedUp.set(_loginDialogPoppedUp);
|
||||
|
@ -3043,14 +3065,12 @@ void Application::initializeUi() {
|
|||
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
|
||||
|
||||
flushMenuUpdates();
|
||||
|
||||
// Now that the menu is instantiated, ensure the display plugin menu is properly updated
|
||||
{
|
||||
auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
|
||||
// first sort the plugins into groupings: standard, advanced, developer
|
||||
std::stable_sort(displayPlugins.begin(), displayPlugins.end(),
|
||||
[](const DisplayPluginPointer& a, const DisplayPluginPointer& b)->bool { return a->getGrouping() < b->getGrouping(); });
|
||||
|
||||
[](const DisplayPluginPointer& a, const DisplayPluginPointer& b) -> bool { return a->getGrouping() < b->getGrouping(); });
|
||||
int dpIndex = 1;
|
||||
// concatenate the groupings into a single list in the order: standard, advanced, developer
|
||||
for(const auto& displayPlugin : displayPlugins) {
|
||||
|
@ -3063,6 +3083,7 @@ void Application::initializeUi() {
|
|||
parent->addSeparator();
|
||||
}
|
||||
|
||||
|
||||
// The display plugins are created before the menu now, so we need to do this here to hide the menu bar
|
||||
// now that it exists
|
||||
if (_window && _window->isFullScreen()) {
|
||||
|
@ -3638,24 +3659,6 @@ void Application::loadErrorDomain(QUrl domainURL) {
|
|||
_fullSceneReceivedCounter++;
|
||||
}
|
||||
|
||||
void Application::loadLoginScreenDomain(QUrl domainURL) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "loadLoginScreenDomain", Q_ARG(QUrl, domainURL));
|
||||
return;
|
||||
}
|
||||
|
||||
if (domainURL.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto namedPaths = prepareServerlessDomainContents(domainURL);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->getDomainHandler().loadedLoginScreenDomain(namedPaths);
|
||||
|
||||
_fullSceneReceivedCounter++;
|
||||
}
|
||||
|
||||
bool Application::importImage(const QString& urlString) {
|
||||
qCDebug(interfaceapp) << "An image file has been dropped in";
|
||||
QString filepath(urlString);
|
||||
|
@ -3890,7 +3893,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (hasFocus()) {
|
||||
if (hasFocus() && !_loginDialogPoppedUp) {
|
||||
if (_keyboardMouseDevice->isActive()) {
|
||||
_keyboardMouseDevice->keyPressEvent(event);
|
||||
}
|
||||
|
@ -5181,6 +5184,86 @@ void Application::init() {
|
|||
_entitySimulation->setWorkloadSpace(getEntities()->getWorkloadSpace());
|
||||
}
|
||||
|
||||
void Application::pauseUntilLoginDetermined() {
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
QMetaObject::invokeMethod(this, "pauseUntilLoginDetermined");
|
||||
return;
|
||||
}
|
||||
|
||||
auto myAvatar = qApp->getMyAvatar();
|
||||
myAvatar->setEnableMeshVisible(false);
|
||||
|
||||
const auto& nodeList = DependencyManager::get<NodeList>();
|
||||
// disconnect domain handler.
|
||||
nodeList->getDomainHandler().disconnect();
|
||||
Menu::getInstance()->setVisible(false);
|
||||
|
||||
{
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||
scriptEngines->reloadLocalFiles();
|
||||
scriptEngines->loadControllerScripts();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::resumeAfterLoginDialogActionTaken() {
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
QMetaObject::invokeMethod(this, "resumeAfterLoginDialogActionTaken");
|
||||
return;
|
||||
}
|
||||
|
||||
auto myAvatar = qApp->getMyAvatar();
|
||||
myAvatar->setEnableMeshVisible(true);
|
||||
|
||||
{
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||
// this will force the model the look at the correct directory (weird order of operations issue)
|
||||
scriptEngines->reloadLocalFiles();
|
||||
|
||||
// do this as late as possible so that all required subsystems are initialized
|
||||
// If we've overridden the default scripts location, just load default scripts
|
||||
// otherwise, load 'em all
|
||||
|
||||
// we just want to see if --scripts was set, we've already parsed it and done
|
||||
// the change in PathUtils. Rather than pass that in the constructor, lets just
|
||||
// look (this could be debated)
|
||||
if (!_defaultScriptsLocation.exists()) {
|
||||
scriptEngines->loadDefaultScripts();
|
||||
scriptEngines->defaultScriptsLocationOverridden(true);
|
||||
} else {
|
||||
scriptEngines->loadScripts();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant testProperty = property(hifi::properties::TEST);
|
||||
qDebug() << testProperty;
|
||||
if (testProperty.isValid()) {
|
||||
const auto testScript = property(hifi::properties::TEST).toUrl();
|
||||
|
||||
// Set last parameter to exit interface when the test script finishes, if so requested
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(testScript, false, false, false, false, quitWhenFinished);
|
||||
|
||||
// This is done so we don't get a "connection time-out" message when we haven't passed in a URL.
|
||||
if (arguments().contains("--url")) {
|
||||
auto reply = SandboxUtils::getStatus();
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
||||
handleSandboxStatus(reply);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
PROFILE_RANGE(render, "GetSandboxStatus");
|
||||
auto reply = SandboxUtils::getStatus();
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
||||
handleSandboxStatus(reply);
|
||||
});
|
||||
}
|
||||
|
||||
const auto& nodeList = DependencyManager::get<NodeList>();
|
||||
// disconnect domain handler.
|
||||
nodeList->getDomainHandler().resetting();
|
||||
|
||||
Menu::getInstance()->setVisible(true);
|
||||
}
|
||||
|
||||
void Application::loadAvatarScripts(const QVector<QString>& urls) {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
auto runningScripts = scriptEngines->getRunningScripts();
|
||||
|
@ -6516,7 +6599,6 @@ void Application::updateWindowTitle() const {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
auto isInErrorState = nodeList->getDomainHandler().isInErrorState();
|
||||
auto isInLoginScreenState = nodeList->getDomainHandler().isInLoginScreenState();
|
||||
|
||||
QString buildVersion = " - "
|
||||
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
|
||||
|
@ -6526,8 +6608,6 @@ void Application::updateWindowTitle() const {
|
|||
|
||||
QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" :
|
||||
nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
|
||||
// check for login state - login state needs empty connection status
|
||||
connectionStatus = isInLoginScreenState ? "" : connectionStatus;
|
||||
QString username = accountManager->getAccountInfo().getUsername();
|
||||
|
||||
setCrashAnnotation("username", username.toStdString());
|
||||
|
@ -6536,8 +6616,6 @@ void Application::updateWindowTitle() const {
|
|||
if (isServerlessMode()) {
|
||||
if (isInErrorState) {
|
||||
currentPlaceName = "serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString();
|
||||
} else if (isInLoginScreenState) {
|
||||
currentPlaceName = "High Fidelity Interface";
|
||||
} else {
|
||||
currentPlaceName = "serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
|
||||
}
|
||||
|
@ -6616,23 +6694,6 @@ void Application::goToErrorDomainURL(QUrl errorDomainURL) {
|
|||
updateWindowTitle();
|
||||
}
|
||||
|
||||
void Application::goToLoginScreenDomainURL(QUrl loginScreenDomainURL) {
|
||||
// disable physics until we have enough information about our new location to not cause craziness.
|
||||
resetPhysicsReadyInformation();
|
||||
setIsServerlessMode(loginScreenDomainURL.scheme() != URL_SCHEME_HIFI);
|
||||
|
||||
// show avatar as a mesh and show hand controllers.
|
||||
qApp->getMyAvatar()->setEnableMeshVisible(false);
|
||||
DependencyManager::get<HMDScriptingInterface>()->requestShowHandControllers();
|
||||
// set into login screen state.
|
||||
emit loginScreenStateChanged(true);
|
||||
|
||||
if (isServerlessMode()) {
|
||||
loadLoginScreenDomain(loginScreenDomainURL);
|
||||
}
|
||||
updateWindowTitle();
|
||||
}
|
||||
|
||||
void Application::resettingDomain() {
|
||||
_notifiedPacketVersionMismatchThisDomain = false;
|
||||
|
||||
|
@ -8430,10 +8491,9 @@ void Application::setShowBulletConstraintLimits(bool value) {
|
|||
}
|
||||
|
||||
void Application::onDismissedLoginDialog() {
|
||||
// TODO something with login dialog.
|
||||
qDebug() << "dismissed login dialog";
|
||||
_loginDialogPoppedUp = false;
|
||||
loginDialogPoppedUp.set(false);
|
||||
resumeAfterLoginDialogActionTaken();
|
||||
}
|
||||
|
||||
void Application::setShowTrackedObjects(bool value) {
|
||||
|
|
|
@ -320,8 +320,6 @@ public:
|
|||
void setOtherAvatarsReplicaCount(int count) { DependencyManager::get<AvatarHashMap>()->setReplicaCount(count); }
|
||||
|
||||
bool getLoginDialogPoppedUp() const { return _loginDialogPoppedUp; }
|
||||
void pauseUntilLoginDetermined();
|
||||
void resumeAfterLoginDialogActionTaken();
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
void beforeEnterBackground();
|
||||
|
@ -341,8 +339,6 @@ signals:
|
|||
|
||||
void interstitialModeChanged(bool isInInterstitialMode);
|
||||
|
||||
void loginScreenStateChanged(bool isInLoginScreenState);
|
||||
|
||||
public slots:
|
||||
QVector<EntityItemID> pasteEntities(float x, float y, float z);
|
||||
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset = nullptr);
|
||||
|
@ -351,7 +347,6 @@ public slots:
|
|||
void updateThreadPoolCount() const;
|
||||
void updateSystemTabletMode();
|
||||
void goToErrorDomainURL(QUrl errorDomainURL);
|
||||
void goToLoginScreenDomainURL(QUrl loginScreenDomainURL);
|
||||
|
||||
Q_INVOKABLE void loadDialog();
|
||||
Q_INVOKABLE void loadScriptURLDialog() const;
|
||||
|
@ -446,7 +441,6 @@ public slots:
|
|||
|
||||
void loadServerlessDomain(QUrl domainURL);
|
||||
void loadErrorDomain(QUrl domainURL);
|
||||
void loadLoginScreenDomain(QUrl domainURL);
|
||||
void setIsInterstitialMode(bool interstitialMode);
|
||||
|
||||
void updateVerboseLogging();
|
||||
|
@ -516,6 +510,8 @@ private slots:
|
|||
|
||||
private:
|
||||
void init();
|
||||
void pauseUntilLoginDetermined();
|
||||
void resumeAfterLoginDialogActionTaken();
|
||||
bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event);
|
||||
bool handleFileOpenEvent(QFileOpenEvent* event);
|
||||
void cleanupBeforeQuit();
|
||||
|
@ -664,6 +660,8 @@ private:
|
|||
QPointer<LogDialog> _logDialog;
|
||||
QPointer<EntityScriptServerLogDialog> _entityScriptServerLogDialog;
|
||||
|
||||
QDir _defaultScriptsLocation{""};
|
||||
|
||||
FileLogger* _logger;
|
||||
|
||||
TouchEvent _lastTouchEvent;
|
||||
|
|
|
@ -33,7 +33,6 @@ void ConnectionMonitor::init() {
|
|||
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &ConnectionMonitor::stopTimer);
|
||||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ConnectionMonitor::stopTimer);
|
||||
connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &ConnectionMonitor::stopTimer);
|
||||
connect(&domainHandler, &DomainHandler::redirectToLoginScreenDomainURL, this, &ConnectionMonitor::stopTimer);
|
||||
connect(this, &ConnectionMonitor::setRedirectErrorState, &domainHandler, &DomainHandler::setRedirectErrorState);
|
||||
|
||||
_timer.setSingleShot(true);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <SandboxUtils.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
|
||||
#include "AddressManager.h"
|
||||
#include "Application.h"
|
||||
|
@ -40,6 +41,18 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
#ifdef Q_OS_MAC
|
||||
auto format = getDefaultOpenGLSurfaceFormat();
|
||||
// Deal with some weirdness in the chromium context sharing on Mac.
|
||||
// The primary share context needs to be 3.2, so that the Chromium will
|
||||
// succeed in it's creation of it's command stub contexts.
|
||||
format.setVersion(3, 2);
|
||||
// This appears to resolve the issues with corrupted fonts on OSX. No
|
||||
// idea why.
|
||||
qputenv("QT_ENABLE_GLYPH_CACHE_WORKAROUND", "true");
|
||||
// https://i.kym-cdn.com/entries/icons/original/000/008/342/ihave.jpg
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
#endif
|
||||
setupHifiApplication(BuildInfo::INTERFACE_NAME);
|
||||
|
||||
QStringList arguments;
|
||||
|
|
|
@ -115,6 +115,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
|||
batch.resetViewTransform();
|
||||
batch.setResourceTexture(0, _uiTexture);
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1), _qmlGeometryId);
|
||||
batch.setResourceTexture(0, nullptr);
|
||||
}
|
||||
|
||||
void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
|
||||
|
|
|
@ -88,6 +88,7 @@ public:
|
|||
// Move the OpenGL context to the present thread
|
||||
// Extra code because of the widget 'wrapper' context
|
||||
_context = context;
|
||||
_context->doneCurrent();
|
||||
_context->moveToThread(this);
|
||||
}
|
||||
|
||||
|
@ -179,7 +180,9 @@ public:
|
|||
_context->makeCurrent();
|
||||
{
|
||||
PROFILE_RANGE(render, "PluginPresent")
|
||||
gl::globalLock();
|
||||
currentPlugin->present();
|
||||
gl::globalRelease(false);
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
_context->doneCurrent();
|
||||
|
|
|
@ -261,6 +261,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
|
|||
DependencyManager::get<GeometryCache>()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId);
|
||||
batch.popProjectionJitter();
|
||||
batch.setResourceTexture(0, nullptr);
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::hasWebSurface() {
|
||||
|
|
|
@ -25,25 +25,24 @@
|
|||
#include "GLLogging.h"
|
||||
#include "Config.h"
|
||||
#include "GLHelpers.h"
|
||||
#include "QOpenGLContextWrapper.h"
|
||||
|
||||
using namespace gl;
|
||||
|
||||
|
||||
bool Context::enableDebugLogger() {
|
||||
#if defined(Q_OS_MAC)
|
||||
// OSX does not support GL_KHR_debug or GL_ARB_debug_output
|
||||
return false;
|
||||
#else
|
||||
#if defined(DEBUG) || defined(USE_GLES)
|
||||
static bool enableDebugLogger = true;
|
||||
#else
|
||||
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL");
|
||||
static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
|
||||
#endif
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
// If the previous run crashed, force GL debug logging on
|
||||
if (qApp->property(hifi::properties::CRASHED).toBool()) {
|
||||
enableDebugLogger = true;
|
||||
}
|
||||
});
|
||||
return enableDebugLogger;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,8 +67,6 @@ void Context::updateSwapchainMemoryUsage(size_t prevSize, size_t newSize) {
|
|||
}
|
||||
|
||||
|
||||
Context* Context::PRIMARY = nullptr;
|
||||
|
||||
Context::Context() {}
|
||||
|
||||
Context::Context(QWindow* window) {
|
||||
|
@ -97,9 +94,6 @@ void Context::release() {
|
|||
_context = nullptr;
|
||||
#endif
|
||||
_window = nullptr;
|
||||
if (PRIMARY == this) {
|
||||
PRIMARY = nullptr;
|
||||
}
|
||||
updateSwapchainMemoryCounter();
|
||||
}
|
||||
|
||||
|
@ -235,16 +229,9 @@ typedef HGLRC(APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShare
|
|||
GLAPI PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
|
||||
GLAPI PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
|
||||
|
||||
Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context();
|
||||
|
||||
void Context::create() {
|
||||
if (!PRIMARY) {
|
||||
PRIMARY = static_cast<Context*>(qApp->property(hifi::properties::gl::PRIMARY_CONTEXT).value<void*>());
|
||||
}
|
||||
|
||||
if (PRIMARY) {
|
||||
_version = PRIMARY->_version;
|
||||
}
|
||||
|
||||
void Context::create(QOpenGLContext* shareContext) {
|
||||
assert(0 != _hwnd);
|
||||
assert(0 == _hdc);
|
||||
auto hwnd = _hwnd;
|
||||
|
@ -338,7 +325,10 @@ void Context::create() {
|
|||
contextAttribs.push_back(0);
|
||||
}
|
||||
contextAttribs.push_back(0);
|
||||
auto shareHglrc = PRIMARY ? PRIMARY->_hglrc : 0;
|
||||
if (!shareContext) {
|
||||
shareContext = qt_gl_global_share_context();
|
||||
}
|
||||
HGLRC shareHglrc = (HGLRC)QOpenGLContextWrapper::nativeContext(shareContext);
|
||||
_hglrc = wglCreateContextAttribsARB(_hdc, shareHglrc, &contextAttribs[0]);
|
||||
}
|
||||
|
||||
|
@ -346,11 +336,6 @@ void Context::create() {
|
|||
throw std::runtime_error("Could not create GL context");
|
||||
}
|
||||
|
||||
if (!PRIMARY) {
|
||||
PRIMARY = this;
|
||||
qApp->setProperty(hifi::properties::gl::PRIMARY_CONTEXT, QVariant::fromValue((void*)PRIMARY));
|
||||
}
|
||||
|
||||
if (!makeCurrent()) {
|
||||
throw std::runtime_error("Could not make context current");
|
||||
}
|
||||
|
@ -363,12 +348,11 @@ void Context::create() {
|
|||
|
||||
#endif
|
||||
|
||||
|
||||
OffscreenContext::~OffscreenContext() {
|
||||
_window->deleteLater();
|
||||
}
|
||||
|
||||
void OffscreenContext::create() {
|
||||
void OffscreenContext::create(QOpenGLContext* shareContext) {
|
||||
if (!_window) {
|
||||
_window = new QWindow();
|
||||
_window->setFlags(Qt::MSWindowsOwnDC);
|
||||
|
@ -379,5 +363,5 @@ void OffscreenContext::create() {
|
|||
qCDebug(glLogging) << "New Offscreen GLContext, window size = " << windowSize.width() << " , " << windowSize.height();
|
||||
QGuiApplication::processEvents();
|
||||
}
|
||||
Parent::create();
|
||||
Parent::create(shareContext);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ class QSurface;
|
|||
class QWindow;
|
||||
class QOpenGLContext;
|
||||
class QThread;
|
||||
class QOpenGLDebugMessage;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#define GL_CUSTOM_CONTEXT
|
||||
|
@ -30,7 +31,6 @@ namespace gl {
|
|||
class Context {
|
||||
protected:
|
||||
QWindow* _window { nullptr };
|
||||
static Context* PRIMARY;
|
||||
static void destroyContext(QOpenGLContext* context);
|
||||
#if defined(GL_CUSTOM_CONTEXT)
|
||||
uint32_t _version { 0x0401 };
|
||||
|
@ -48,6 +48,9 @@ namespace gl {
|
|||
|
||||
public:
|
||||
static bool enableDebugLogger();
|
||||
static void debugMessageHandler(const QOpenGLDebugMessage &debugMessage);
|
||||
static void setupDebugLogging(QOpenGLContext* context);
|
||||
|
||||
Context();
|
||||
Context(QWindow* window);
|
||||
void release();
|
||||
|
@ -59,14 +62,14 @@ namespace gl {
|
|||
static void makeCurrent(QOpenGLContext* context, QSurface* surface);
|
||||
void swapBuffers();
|
||||
void doneCurrent();
|
||||
virtual void create();
|
||||
virtual void create(QOpenGLContext* shareContext = nullptr);
|
||||
QOpenGLContext* qglContext();
|
||||
void moveToThread(QThread* thread);
|
||||
|
||||
static size_t evalSurfaceMemoryUsage(uint32_t width, uint32_t height, uint32_t pixelSize);
|
||||
static size_t getSwapchainMemoryUsage();
|
||||
static void updateSwapchainMemoryUsage(size_t prevSize, size_t newSize);
|
||||
|
||||
|
||||
private:
|
||||
static std::atomic<size_t> _totalSwapchainMemoryUsage;
|
||||
|
||||
|
@ -81,7 +84,7 @@ namespace gl {
|
|||
QWindow* _window { nullptr };
|
||||
public:
|
||||
virtual ~OffscreenContext();
|
||||
void create() override;
|
||||
void create(QOpenGLContext* shareContext = nullptr) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <QtPlatformHeaders/QWGLNativeContext>
|
||||
#endif
|
||||
|
||||
#include <QtGui/QOpenGLDebugMessage>
|
||||
|
||||
#include "GLHelpers.h"
|
||||
|
||||
using namespace gl;
|
||||
|
@ -47,6 +49,32 @@ void Context::moveToThread(QThread* thread) {
|
|||
qglContext()->moveToThread(thread);
|
||||
}
|
||||
|
||||
void Context::debugMessageHandler(const QOpenGLDebugMessage& debugMessage) {
|
||||
auto severity = debugMessage.severity();
|
||||
switch (severity) {
|
||||
case QOpenGLDebugMessage::NotificationSeverity:
|
||||
case QOpenGLDebugMessage::LowSeverity:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qDebug(glLogging) << debugMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
void Context::setupDebugLogging(QOpenGLContext *context) {
|
||||
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(context);
|
||||
QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, nullptr, [](const QOpenGLDebugMessage& message){
|
||||
Context::debugMessageHandler(message);
|
||||
});
|
||||
if (logger->initialize()) {
|
||||
logger->enableMessages();
|
||||
logger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
|
||||
} else {
|
||||
qCWarning(glLogging) << "OpenGL context does not support debugging";
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef GL_CUSTOM_CONTEXT
|
||||
bool Context::makeCurrent() {
|
||||
updateSwapchainMemoryCounter();
|
||||
|
@ -65,21 +93,29 @@ void Context::doneCurrent() {
|
|||
}
|
||||
}
|
||||
|
||||
Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context();
|
||||
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat();
|
||||
|
||||
|
||||
void Context::create() {
|
||||
void Context::create(QOpenGLContext* shareContext) {
|
||||
_context = new QOpenGLContext();
|
||||
if (PRIMARY) {
|
||||
_context->setShareContext(PRIMARY->qglContext());
|
||||
} else {
|
||||
PRIMARY = this;
|
||||
_context->setFormat(_window->format());
|
||||
if (!shareContext) {
|
||||
shareContext = qt_gl_global_share_context();
|
||||
}
|
||||
_context->setFormat(getDefaultOpenGLSurfaceFormat());
|
||||
_context->create();
|
||||
|
||||
_context->setShareContext(shareContext);
|
||||
_context->create();
|
||||
_swapchainPixelSize = evalGLFormatSwapchainPixelSize(_context->format());
|
||||
updateSwapchainMemoryCounter();
|
||||
|
||||
if (!makeCurrent()) {
|
||||
throw std::runtime_error("Could not make context current");
|
||||
}
|
||||
if (enableDebugLogger()) {
|
||||
setupDebugLogging(_context);
|
||||
}
|
||||
doneCurrent();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtGui/QOpenGLDebugLogger>
|
||||
|
||||
#include "Context.h"
|
||||
|
||||
size_t evalGLFormatSwapchainPixelSize(const QSurfaceFormat& format) {
|
||||
size_t pixelSize = format.redBufferSize() + format.greenBufferSize() + format.blueBufferSize() + format.alphaBufferSize();
|
||||
// We don't apply the length of the swap chain into this pixelSize since it is not vsible for the Process (on windows).
|
||||
|
@ -34,14 +36,54 @@ bool gl::disableGl45() {
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#define SERIALIZE_GL_RENDERING
|
||||
#endif
|
||||
|
||||
#ifdef SERIALIZE_GL_RENDERING
|
||||
|
||||
// This terrible terrible hack brought to you by the complete lack of reasonable
|
||||
// OpenGL debugging tools on OSX. Without this serialization code, the UI textures
|
||||
// frequently become 'glitchy' and get composited onto the main scene in what looks
|
||||
// like a partially rendered state.
|
||||
// This looks very much like either state bleeding across the contexts, or bad
|
||||
// synchronization for the shared OpenGL textures. However, previous attempts to resolve
|
||||
// it, even with gratuitous use of glFinish hasn't improved the situation
|
||||
|
||||
static std::mutex _globalOpenGLLock;
|
||||
|
||||
void gl::globalLock() {
|
||||
_globalOpenGLLock.lock();
|
||||
}
|
||||
|
||||
void gl::globalRelease(bool finish) {
|
||||
if (finish) {
|
||||
glFinish();
|
||||
}
|
||||
_globalOpenGLLock.unlock();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void gl::globalLock() {}
|
||||
void gl::globalRelease(bool finish) {}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void gl::getTargetVersion(int& major, int& minor) {
|
||||
#if defined(USE_GLES)
|
||||
major = 3;
|
||||
minor = 2;
|
||||
#else
|
||||
#if defined(Q_OS_MAC)
|
||||
major = 4;
|
||||
minor = 1;
|
||||
#else
|
||||
major = 4;
|
||||
minor = disableGl45() ? 1 : 5;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
|
||||
|
@ -57,6 +99,9 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
|
|||
#else
|
||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
||||
#endif
|
||||
if (gl::Context::enableDebugLogger()) {
|
||||
format.setOption(QSurfaceFormat::DebugContext);
|
||||
}
|
||||
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
||||
format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS);
|
||||
format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS);
|
||||
|
@ -64,7 +109,6 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
|
|||
::gl::getTargetVersion(major, minor);
|
||||
format.setMajorVersion(major);
|
||||
format.setMinorVersion(minor);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
});
|
||||
return format;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ int glVersionToInteger(QString glVersion);
|
|||
bool isRenderThread();
|
||||
|
||||
namespace gl {
|
||||
void globalLock();
|
||||
void globalRelease(bool finish = true);
|
||||
|
||||
void withSavedContext(const std::function<void()>& f);
|
||||
|
||||
bool checkGLError(const char* name);
|
||||
|
|
|
@ -63,10 +63,10 @@ int GLWidget::getDeviceHeight() const {
|
|||
return height() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f);
|
||||
}
|
||||
|
||||
void GLWidget::createContext() {
|
||||
void GLWidget::createContext(QOpenGLContext* shareContext) {
|
||||
_context = new gl::Context();
|
||||
_context->setWindow(windowHandle());
|
||||
_context->create();
|
||||
_context->create(shareContext);
|
||||
_context->makeCurrent();
|
||||
_context->clear();
|
||||
_context->doneCurrent();
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
int getDeviceHeight() const;
|
||||
QSize getDeviceSize() const { return QSize(getDeviceWidth(), getDeviceHeight()); }
|
||||
QPaintEngine* paintEngine() const override;
|
||||
void createContext();
|
||||
void createContext(QOpenGLContext* shareContext = nullptr);
|
||||
bool makeCurrent();
|
||||
void doneCurrent();
|
||||
void swapBuffers();
|
||||
|
|
|
@ -22,7 +22,7 @@ void GLWindow::createContext(QOpenGLContext* shareContext) {
|
|||
void GLWindow::createContext(const QSurfaceFormat& format, QOpenGLContext* shareContext) {
|
||||
_context = new gl::Context();
|
||||
_context->setWindow(this);
|
||||
_context->create();
|
||||
_context->create(shareContext);
|
||||
_context->makeCurrent();
|
||||
_context->clear();
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ OffscreenGLCanvas::OffscreenGLCanvas() :
|
|||
_context(new QOpenGLContext),
|
||||
_offscreenSurface(new QOffscreenSurface)
|
||||
{
|
||||
setFormat(getDefaultOpenGLSurfaceFormat());
|
||||
}
|
||||
|
||||
OffscreenGLCanvas::~OffscreenGLCanvas() {
|
||||
|
@ -49,12 +50,15 @@ OffscreenGLCanvas::~OffscreenGLCanvas() {
|
|||
|
||||
}
|
||||
|
||||
void OffscreenGLCanvas::setFormat(const QSurfaceFormat& format) {
|
||||
_context->setFormat(format);
|
||||
}
|
||||
|
||||
bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) {
|
||||
if (nullptr != sharedContext) {
|
||||
sharedContext->doneCurrent();
|
||||
_context->setShareContext(sharedContext);
|
||||
}
|
||||
_context->setFormat(getDefaultOpenGLSurfaceFormat());
|
||||
if (!_context->create()) {
|
||||
qFatal("Failed to create OffscreenGLCanvas context");
|
||||
}
|
||||
|
@ -69,38 +73,16 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) {
|
|||
if (!_context->makeCurrent(_offscreenSurface)) {
|
||||
qFatal("Unable to make offscreen surface current");
|
||||
}
|
||||
_context->doneCurrent();
|
||||
#else
|
||||
if (!_offscreenSurface->isValid()) {
|
||||
qFatal("Offscreen surface is invalid");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gl::Context::enableDebugLogger()) {
|
||||
_context->makeCurrent(_offscreenSurface);
|
||||
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
|
||||
connect(logger, &QOpenGLDebugLogger::messageLogged, this, &OffscreenGLCanvas::onMessageLogged);
|
||||
logger->initialize();
|
||||
logger->enableMessages();
|
||||
logger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
|
||||
_context->doneCurrent();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OffscreenGLCanvas::onMessageLogged(const QOpenGLDebugMessage& debugMessage) {
|
||||
auto severity = debugMessage.severity();
|
||||
switch (severity) {
|
||||
case QOpenGLDebugMessage::NotificationSeverity:
|
||||
case QOpenGLDebugMessage::LowSeverity:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qDebug(glLogging) << debugMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
bool OffscreenGLCanvas::makeCurrent() {
|
||||
bool result = _context->makeCurrent(_offscreenSurface);
|
||||
if (glGetString) {
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
class QOpenGLContext;
|
||||
class QOffscreenSurface;
|
||||
class QOpenGLDebugMessage;
|
||||
class QSurfaceFormat;
|
||||
|
||||
class OffscreenGLCanvas : public QObject {
|
||||
public:
|
||||
OffscreenGLCanvas();
|
||||
~OffscreenGLCanvas();
|
||||
void setFormat(const QSurfaceFormat& format);
|
||||
bool create(QOpenGLContext* sharedContext = nullptr);
|
||||
bool makeCurrent();
|
||||
void doneCurrent();
|
||||
|
@ -35,9 +37,6 @@ public:
|
|||
void setThreadContext();
|
||||
static bool restoreThreadContext();
|
||||
|
||||
private slots:
|
||||
void onMessageLogged(const QOpenGLDebugMessage &debugMessage);
|
||||
|
||||
protected:
|
||||
void clearThreadContext();
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
|
||||
#include <QOpenGLContext>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QtPlatformHeaders/QWGLNativeContext>
|
||||
#endif
|
||||
|
||||
uint32_t QOpenGLContextWrapper::currentContextVersion() {
|
||||
QOpenGLContext* context = QOpenGLContext::currentContext();
|
||||
if (!context) {
|
||||
|
@ -45,6 +49,19 @@ void QOpenGLContextWrapper::setFormat(const QSurfaceFormat& format) {
|
|||
_context->setFormat(format);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
void* QOpenGLContextWrapper::nativeContext(QOpenGLContext* context) {
|
||||
HGLRC result = 0;
|
||||
if (context != nullptr) {
|
||||
auto nativeHandle = context->nativeHandle();
|
||||
if (nativeHandle.canConvert<QWGLNativeContext>()) {
|
||||
result = nativeHandle.value<QWGLNativeContext>().context();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool QOpenGLContextWrapper::create() {
|
||||
return _context->create();
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_QOpenGLContextWrapper_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <QtGlobal>
|
||||
|
||||
class QOpenGLContext;
|
||||
class QSurface;
|
||||
|
@ -21,6 +22,10 @@ class QThread;
|
|||
|
||||
class QOpenGLContextWrapper {
|
||||
public:
|
||||
#ifdef Q_OS_WIN
|
||||
static void* nativeContext(QOpenGLContext* context);
|
||||
#endif
|
||||
|
||||
QOpenGLContextWrapper();
|
||||
QOpenGLContextWrapper(QOpenGLContext* context);
|
||||
virtual ~QOpenGLContextWrapper();
|
||||
|
|
|
@ -708,37 +708,37 @@ void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) {
|
|||
|
||||
void GLBackend::releaseBuffer(GLuint id, Size size) const {
|
||||
Lock lock(_trashMutex);
|
||||
_buffersTrash.push_back({ id, size });
|
||||
_currentFrameTrash.buffersTrash.push_back({ id, size });
|
||||
}
|
||||
|
||||
void GLBackend::releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const {
|
||||
Lock lock(_trashMutex);
|
||||
_externalTexturesTrash.push_back({ id, recycler });
|
||||
_currentFrameTrash.externalTexturesTrash.push_back({ id, recycler });
|
||||
}
|
||||
|
||||
void GLBackend::releaseTexture(GLuint id, Size size) const {
|
||||
Lock lock(_trashMutex);
|
||||
_texturesTrash.push_back({ id, size });
|
||||
_currentFrameTrash.texturesTrash.push_back({ id, size });
|
||||
}
|
||||
|
||||
void GLBackend::releaseFramebuffer(GLuint id) const {
|
||||
Lock lock(_trashMutex);
|
||||
_framebuffersTrash.push_back(id);
|
||||
_currentFrameTrash.framebuffersTrash.push_back(id);
|
||||
}
|
||||
|
||||
void GLBackend::releaseShader(GLuint id) const {
|
||||
Lock lock(_trashMutex);
|
||||
_shadersTrash.push_back(id);
|
||||
_currentFrameTrash.shadersTrash.push_back(id);
|
||||
}
|
||||
|
||||
void GLBackend::releaseProgram(GLuint id) const {
|
||||
Lock lock(_trashMutex);
|
||||
_programsTrash.push_back(id);
|
||||
_currentFrameTrash.programsTrash.push_back(id);
|
||||
}
|
||||
|
||||
void GLBackend::releaseQuery(GLuint id) const {
|
||||
Lock lock(_trashMutex);
|
||||
_queriesTrash.push_back(id);
|
||||
_currentFrameTrash.queriesTrash.push_back(id);
|
||||
}
|
||||
|
||||
void GLBackend::queueLambda(const std::function<void()> lambda) const {
|
||||
|
@ -746,6 +746,81 @@ void GLBackend::queueLambda(const std::function<void()> lambda) const {
|
|||
_lambdaQueue.push_back(lambda);
|
||||
}
|
||||
|
||||
void GLBackend::FrameTrash::cleanup() {
|
||||
glWaitSync(fence, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync(fence);
|
||||
|
||||
{
|
||||
std::vector<GLuint> ids;
|
||||
ids.reserve(buffersTrash.size());
|
||||
for (auto pair : buffersTrash) {
|
||||
ids.push_back(pair.first);
|
||||
}
|
||||
if (!ids.empty()) {
|
||||
glDeleteBuffers((GLsizei)ids.size(), ids.data());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<GLuint> ids;
|
||||
ids.reserve(framebuffersTrash.size());
|
||||
for (auto id : framebuffersTrash) {
|
||||
ids.push_back(id);
|
||||
}
|
||||
if (!ids.empty()) {
|
||||
glDeleteFramebuffers((GLsizei)ids.size(), ids.data());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<GLuint> ids;
|
||||
ids.reserve(texturesTrash.size());
|
||||
for (auto pair : texturesTrash) {
|
||||
ids.push_back(pair.first);
|
||||
}
|
||||
if (!ids.empty()) {
|
||||
glDeleteTextures((GLsizei)ids.size(), ids.data());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (!externalTexturesTrash.empty()) {
|
||||
std::vector<GLsync> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto id : programsTrash) {
|
||||
glDeleteProgram(id);
|
||||
}
|
||||
|
||||
for (auto id : shadersTrash) {
|
||||
glDeleteShader(id);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<GLuint> ids;
|
||||
ids.reserve(queriesTrash.size());
|
||||
for (auto id : queriesTrash) {
|
||||
ids.push_back(id);
|
||||
}
|
||||
if (!ids.empty()) {
|
||||
glDeleteQueries((GLsizei)ids.size(), ids.data());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GLBackend::recycle() const {
|
||||
PROFILE_RANGE(render_gpu_gl, __FUNCTION__)
|
||||
{
|
||||
|
@ -759,112 +834,16 @@ void GLBackend::recycle() const {
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<GLuint> ids;
|
||||
std::list<std::pair<GLuint, Size>> 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());
|
||||
}
|
||||
while (!_previousFrameTrashes.empty()) {
|
||||
_previousFrameTrashes.front().cleanup();
|
||||
_previousFrameTrashes.pop_front();
|
||||
}
|
||||
|
||||
_previousFrameTrashes.emplace_back();
|
||||
{
|
||||
std::vector<GLuint> ids;
|
||||
std::list<GLuint> 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<GLuint> ids;
|
||||
std::list<std::pair<GLuint, Size>> 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<std::pair<GLuint, Texture::ExternalRecycler>> externalTexturesTrash;
|
||||
{
|
||||
Lock lock(_trashMutex);
|
||||
std::swap(_externalTexturesTrash, externalTexturesTrash);
|
||||
}
|
||||
if (!externalTexturesTrash.empty()) {
|
||||
std::vector<GLsync> 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<GLuint> programsTrash;
|
||||
{
|
||||
Lock lock(_trashMutex);
|
||||
std::swap(_programsTrash, programsTrash);
|
||||
}
|
||||
for (auto id : programsTrash) {
|
||||
glDeleteProgram(id);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::list<GLuint> shadersTrash;
|
||||
{
|
||||
Lock lock(_trashMutex);
|
||||
std::swap(_shadersTrash, shadersTrash);
|
||||
}
|
||||
for (auto id : shadersTrash) {
|
||||
glDeleteShader(id);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<GLuint> ids;
|
||||
std::list<GLuint> 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());
|
||||
}
|
||||
Lock lock(_trashMutex);
|
||||
_previousFrameTrashes.back().swap(_currentFrameTrash);
|
||||
_previousFrameTrashes.back().fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
}
|
||||
|
||||
_textureManagement._transferEngine->manageMemory();
|
||||
|
|
|
@ -419,16 +419,34 @@ protected:
|
|||
static const size_t INVALID_OFFSET = (size_t)-1;
|
||||
bool _inRenderTransferPass{ false };
|
||||
int _currentDraw{ -1 };
|
||||
|
||||
std::list<std::string> profileRanges;
|
||||
|
||||
struct FrameTrash {
|
||||
GLsync fence = nullptr;
|
||||
std::list<std::pair<GLuint, Size>> buffersTrash;
|
||||
std::list<std::pair<GLuint, Size>> texturesTrash;
|
||||
std::list<std::pair<GLuint, Texture::ExternalRecycler>> externalTexturesTrash;
|
||||
std::list<GLuint> framebuffersTrash;
|
||||
std::list<GLuint> shadersTrash;
|
||||
std::list<GLuint> programsTrash;
|
||||
std::list<GLuint> queriesTrash;
|
||||
|
||||
void swap(FrameTrash& other) {
|
||||
buffersTrash.swap(other.buffersTrash);
|
||||
texturesTrash.swap(other.texturesTrash);
|
||||
externalTexturesTrash.swap(other.externalTexturesTrash);
|
||||
framebuffersTrash.swap(other.framebuffersTrash);
|
||||
shadersTrash.swap(other.shadersTrash);
|
||||
programsTrash.swap(other.programsTrash);
|
||||
queriesTrash.swap(other.queriesTrash);
|
||||
}
|
||||
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
mutable Mutex _trashMutex;
|
||||
mutable std::list<std::pair<GLuint, Size>> _buffersTrash;
|
||||
mutable std::list<std::pair<GLuint, Size>> _texturesTrash;
|
||||
mutable std::list<std::pair<GLuint, Texture::ExternalRecycler>> _externalTexturesTrash;
|
||||
mutable std::list<GLuint> _framebuffersTrash;
|
||||
mutable std::list<GLuint> _shadersTrash;
|
||||
mutable std::list<GLuint> _programsTrash;
|
||||
mutable std::list<GLuint> _queriesTrash;
|
||||
mutable FrameTrash _currentFrameTrash;
|
||||
mutable std::list<FrameTrash> _previousFrameTrashes;
|
||||
std::list<std::string> profileRanges;
|
||||
mutable std::list<std::function<void()>> _lambdaQueue;
|
||||
|
||||
void renderPassTransfer(const Batch& batch);
|
||||
|
|
|
@ -62,16 +62,6 @@ DomainHandler::DomainHandler(QObject* parent) :
|
|||
|
||||
// stop the refresh timer if redirected to the error domain
|
||||
connect(this, &DomainHandler::redirectToErrorDomainURL, &_apiRefreshTimer, &QTimer::stop);
|
||||
|
||||
// stop the refresh timer if redirected to the login screen domain
|
||||
connect(this, &DomainHandler::redirectToLoginScreenDomainURL, &_apiRefreshTimer, &QTimer::stop);
|
||||
|
||||
|
||||
// stop the refresh timer if redirected to the login screen domain
|
||||
connect(this, &DomainHandler::redirectToLoginScreenDomainURL, [this]() {
|
||||
_isInLoginScreenState = true;
|
||||
qCDebug(networking) << "Redirecting user to " << _loginScreenDomainURL;
|
||||
});
|
||||
}
|
||||
|
||||
void DomainHandler::disconnect() {
|
||||
|
@ -123,7 +113,7 @@ void DomainHandler::softReset() {
|
|||
QMetaObject::invokeMethod(&_settingsTimer, "stop");
|
||||
|
||||
// restart the API refresh timer in case we fail to connect and need to refresh information
|
||||
if (!_isInErrorState || !_isInLoginScreenState) {
|
||||
if (!_isInErrorState) {
|
||||
QMetaObject::invokeMethod(&_apiRefreshTimer, "start");
|
||||
}
|
||||
}
|
||||
|
@ -135,9 +125,6 @@ void DomainHandler::hardReset() {
|
|||
_isInErrorState = false;
|
||||
emit redirectErrorStateChanged(_isInErrorState);
|
||||
|
||||
_isInLoginScreenState = false;
|
||||
emit loginScreenStateChanged(_isInLoginScreenState);
|
||||
|
||||
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
|
||||
_pendingDomainID = QUuid();
|
||||
_iceServerSockAddr = HifiSockAddr();
|
||||
|
@ -176,11 +163,6 @@ void DomainHandler::setErrorDomainURL(const QUrl& url) {
|
|||
return;
|
||||
}
|
||||
|
||||
void DomainHandler::setLoginScreenDomainURL(const QUrl& url) {
|
||||
_loginScreenDomainURL = url;
|
||||
return;
|
||||
}
|
||||
|
||||
void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname) {
|
||||
if (_sockAddr != sockAddr) {
|
||||
// we should reset on a sockAddr change
|
||||
|
@ -383,17 +365,6 @@ void DomainHandler::loadedErrorDomain(std::map<QString, QString> namedPaths) {
|
|||
DependencyManager::get<AddressManager>()->goToViewpointForPath(viewpoint, QString());
|
||||
}
|
||||
|
||||
void DomainHandler::loadedLoginScreenDomain(std::map<QString, QString> namedPaths) {
|
||||
auto lookup = namedPaths.find("/");
|
||||
QString viewpoint;
|
||||
if (lookup != namedPaths.end()) {
|
||||
viewpoint = lookup->second;
|
||||
} else {
|
||||
viewpoint = DOMAIN_SPAWNING_POINT;
|
||||
}
|
||||
DependencyManager::get<AddressManager>()->goToViewpointForPath(viewpoint, QString());
|
||||
}
|
||||
|
||||
void DomainHandler::setRedirectErrorState(QUrl errorUrl, QString reasonMessage, int reasonCode, const QString& extraInfo) {
|
||||
_lastDomainConnectionError = reasonCode;
|
||||
if (getInterstitialModeEnabled() && isHardRefusal(reasonCode)) {
|
||||
|
|
|
@ -58,9 +58,6 @@ public:
|
|||
|
||||
int getLastDomainConnectionError() { return _lastDomainConnectionError; }
|
||||
|
||||
QUrl getLoginScreenDomainURL(){ return _loginScreenDomainURL; }
|
||||
void setLoginScreenDomainURL(const QUrl& url);
|
||||
|
||||
const QHostAddress& getIP() const { return _sockAddr.getAddress(); }
|
||||
void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); }
|
||||
|
||||
|
@ -96,8 +93,6 @@ public:
|
|||
|
||||
void loadedErrorDomain(std::map<QString, QString> namedPaths);
|
||||
|
||||
void loadedLoginScreenDomain(std::map<QString, QString> namedPaths);
|
||||
|
||||
QString getViewPointFromNamedPath(QString namedPath);
|
||||
|
||||
bool hasSettings() const { return !_settingsObject.isEmpty(); }
|
||||
|
@ -211,9 +206,6 @@ signals:
|
|||
void redirectToErrorDomainURL(QUrl errorDomainURL);
|
||||
void redirectErrorStateChanged(bool isInErrorState);
|
||||
|
||||
void redirectToLoginScreenDomainURL();
|
||||
void loginScreenStateChanged(bool isInLoginScreenState);
|
||||
|
||||
void limitOfSilentDomainCheckInsReached();
|
||||
|
||||
private:
|
||||
|
@ -227,7 +219,6 @@ private:
|
|||
Node::LocalID _localID;
|
||||
QUrl _domainURL;
|
||||
QUrl _errorDomainURL;
|
||||
QUrl _loginScreenDomainURL;
|
||||
HifiSockAddr _sockAddr;
|
||||
QUuid _assignmentUUID;
|
||||
QUuid _connectionToken;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <gl/Config.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
|
@ -114,6 +115,7 @@ void RenderEventHandler::onRender() {
|
|||
|
||||
PROFILE_RANGE(render_qml_gl, __FUNCTION__);
|
||||
|
||||
gl::globalLock();
|
||||
if (!_shared->preRender()) {
|
||||
return;
|
||||
}
|
||||
|
@ -139,11 +141,12 @@ void RenderEventHandler::onRender() {
|
|||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
// Fence will be used in another thread / context, so a flush is required
|
||||
glFlush();
|
||||
_shared->updateTextureAndFence({ texture, fence });
|
||||
// Fence will be used in another thread / context, so a flush is required
|
||||
_shared->_quickWindow->resetOpenGLState();
|
||||
}
|
||||
gl::globalRelease();
|
||||
}
|
||||
|
||||
void RenderEventHandler::onQuit() {
|
||||
|
@ -167,4 +170,5 @@ void RenderEventHandler::onQuit() {
|
|||
moveToThread(qApp->thread());
|
||||
QThread::currentThread()->quit();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -51,8 +51,10 @@ uint32_t TextureCache::acquireTexture(const QSize& size) {
|
|||
if (!textureSet.returnedTextures.empty()) {
|
||||
auto textureAndFence = textureSet.returnedTextures.front();
|
||||
textureSet.returnedTextures.pop_front();
|
||||
glWaitSync((GLsync)textureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync((GLsync)textureAndFence.second);
|
||||
if (textureAndFence.second) {
|
||||
glWaitSync((GLsync)textureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync((GLsync)textureAndFence.second);
|
||||
}
|
||||
return textureAndFence.first;
|
||||
}
|
||||
return createTexture(size);
|
||||
|
@ -101,9 +103,11 @@ void TextureCache::destroyTexture(uint32_t texture) {
|
|||
|
||||
void TextureCache::destroy(const Value& textureAndFence) {
|
||||
const auto& fence = textureAndFence.second;
|
||||
// FIXME prevents crash on shutdown, but we should migrate to a global functions object owned by the shared context.
|
||||
glWaitSync((GLsync)fence, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync((GLsync)fence);
|
||||
if (fence) {
|
||||
// FIXME prevents crash on shutdown, but we should migrate to a global functions object owned by the shared context.
|
||||
glWaitSync((GLsync)fence, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync((GLsync)fence);
|
||||
}
|
||||
destroyTexture(textureAndFence.first);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStanda
|
|||
static const bool HIFI_SCRIPT_DEBUGGABLES { true };
|
||||
static const QString SETTINGS_KEY { "RunningScripts" };
|
||||
static const QUrl DEFAULT_SCRIPTS_LOCATION { "file:///~//defaultScripts.js" };
|
||||
static const QUrl CONTROLLER_SCRIPTS_LOCATION { "file:///~//system//controllers//controllerScripts.js" };
|
||||
// Using a QVariantList so this is human-readable in the settings file
|
||||
static Setting::Handle<QVariantList> runningScriptsHandle(SETTINGS_KEY, { QVariant(DEFAULT_SCRIPTS_LOCATION) });
|
||||
|
||||
|
@ -287,6 +288,10 @@ void ScriptEngines::loadDefaultScripts() {
|
|||
loadScript(DEFAULT_SCRIPTS_LOCATION);
|
||||
}
|
||||
|
||||
void ScriptEngines::loadControllerScripts() {
|
||||
loadScript(CONTROLLER_SCRIPTS_LOCATION);
|
||||
}
|
||||
|
||||
void ScriptEngines::loadOneScript(const QString& scriptFilename) {
|
||||
loadScript(scriptFilename);
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ public:
|
|||
void setDebugScriptUrl(const QString& url) { _debugScriptUrl = url; };
|
||||
|
||||
void loadDefaultScripts();
|
||||
void loadControllerScripts();
|
||||
void reloadLocalFiles();
|
||||
|
||||
QStringList getRunningScripts();
|
||||
|
|
|
@ -710,15 +710,6 @@ const GROUPS = [
|
|||
decimals: 2,
|
||||
propertyID: "particleRadius",
|
||||
},
|
||||
{
|
||||
label: "Size Spread",
|
||||
type: "slider",
|
||||
min: 0,
|
||||
max: 4,
|
||||
step: 0.01,
|
||||
decimals: 2,
|
||||
propertyID: "radiusSpread",
|
||||
},
|
||||
{
|
||||
label: "Size Start",
|
||||
type: "slider",
|
||||
|
@ -739,6 +730,15 @@ const GROUPS = [
|
|||
propertyID: "radiusFinish",
|
||||
fallbackProperty: "particleRadius",
|
||||
},
|
||||
{
|
||||
label: "Size Spread",
|
||||
type: "slider",
|
||||
min: 0,
|
||||
max: 4,
|
||||
step: 0.01,
|
||||
decimals: 2,
|
||||
propertyID: "radiusSpread",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -783,15 +783,6 @@ const GROUPS = [
|
|||
decimals: 2,
|
||||
propertyID: "alpha",
|
||||
},
|
||||
{
|
||||
label: "Alpha Spread",
|
||||
type: "slider",
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
decimals: 2,
|
||||
propertyID: "alphaSpread",
|
||||
},
|
||||
{
|
||||
label: "Alpha Start",
|
||||
type: "slider",
|
||||
|
@ -812,6 +803,15 @@ const GROUPS = [
|
|||
propertyID: "alphaFinish",
|
||||
fallbackProperty: "alpha",
|
||||
},
|
||||
{
|
||||
label: "Alpha Spread",
|
||||
type: "slider",
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
decimals: 2,
|
||||
propertyID: "alphaSpread",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -853,17 +853,6 @@ const GROUPS = [
|
|||
unit: "deg",
|
||||
propertyID: "particleSpin",
|
||||
},
|
||||
{
|
||||
label: "Spin Spread",
|
||||
type: "slider",
|
||||
min: 0,
|
||||
max: 360,
|
||||
step: 1,
|
||||
decimals: 0,
|
||||
multiplier: DEGREES_TO_RADIANS,
|
||||
unit: "deg",
|
||||
propertyID: "spinSpread",
|
||||
},
|
||||
{
|
||||
label: "Spin Start",
|
||||
type: "slider",
|
||||
|
@ -888,6 +877,17 @@ const GROUPS = [
|
|||
propertyID: "spinFinish",
|
||||
fallbackProperty: "particleSpin",
|
||||
},
|
||||
{
|
||||
label: "Spin Spread",
|
||||
type: "slider",
|
||||
min: 0,
|
||||
max: 360,
|
||||
step: 1,
|
||||
decimals: 0,
|
||||
multiplier: DEGREES_TO_RADIANS,
|
||||
unit: "deg",
|
||||
propertyID: "spinSpread",
|
||||
},
|
||||
{
|
||||
label: "Rotate with Entity",
|
||||
type: "bool",
|
||||
|
@ -1356,15 +1356,15 @@ function getPropertyInputElement(propertyID) {
|
|||
}
|
||||
|
||||
function enableChildren(el, selector) {
|
||||
var elSelectors = el.querySelectorAll(selector);
|
||||
for (var selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) {
|
||||
let elSelectors = el.querySelectorAll(selector);
|
||||
for (let selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) {
|
||||
elSelectors[selectorIndex].removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function disableChildren(el, selector) {
|
||||
var elSelectors = el.querySelectorAll(selector);
|
||||
for (var selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) {
|
||||
let elSelectors = el.querySelectorAll(selector);
|
||||
for (let selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) {
|
||||
elSelectors[selectorIndex].setAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
@ -1372,7 +1372,7 @@ function disableChildren(el, selector) {
|
|||
function enableProperties() {
|
||||
enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker");
|
||||
enableChildren(document, ".colpick");
|
||||
var elLocked = getPropertyInputElement("locked");
|
||||
let elLocked = getPropertyInputElement("locked");
|
||||
|
||||
if (elLocked.checked === false) {
|
||||
removeStaticUserData();
|
||||
|
@ -1383,10 +1383,10 @@ function enableProperties() {
|
|||
function disableProperties() {
|
||||
disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker");
|
||||
disableChildren(document, ".colpick");
|
||||
for (var pickKey in colorPickers) {
|
||||
for (let pickKey in colorPickers) {
|
||||
colorPickers[pickKey].colpickHide();
|
||||
}
|
||||
var elLocked = getPropertyInputElement("locked");
|
||||
let elLocked = getPropertyInputElement("locked");
|
||||
|
||||
if (elLocked.checked === true) {
|
||||
if ($('#property-userData-editor').css('display') === "block") {
|
||||
|
@ -1527,7 +1527,7 @@ function getPropertyValue(originalPropertyName) {
|
|||
* PROPERTY UPDATE FUNCTIONS
|
||||
*/
|
||||
|
||||
function updateProperty(originalPropertyName, propertyValue) {
|
||||
function updateProperty(originalPropertyName, propertyValue, isParticleProperty) {
|
||||
let propertyUpdate = {};
|
||||
// if this is a compound property name (i.e. animation.running) then split it by . up to 3 times
|
||||
let splitPropertyName = originalPropertyName.split('.');
|
||||
|
@ -1546,8 +1546,8 @@ function updateProperty(originalPropertyName, propertyValue) {
|
|||
propertyUpdate[originalPropertyName] = propertyValue;
|
||||
}
|
||||
// queue up particle property changes with the debounced sync to avoid
|
||||
// causing particle emitting to reset each frame when updating values
|
||||
if (properties[originalPropertyName].isParticleProperty) {
|
||||
// causing particle emitting to reset excessively with each value change
|
||||
if (isParticleProperty) {
|
||||
Object.keys(propertyUpdate).forEach(function (propertyUpdateKey) {
|
||||
particlePropertyUpdates[propertyUpdateKey] = propertyUpdate[propertyUpdateKey];
|
||||
});
|
||||
|
@ -1570,30 +1570,29 @@ function updateProperties(propertiesToUpdate) {
|
|||
}));
|
||||
}
|
||||
|
||||
|
||||
function createEmitTextPropertyUpdateFunction(propertyName) {
|
||||
function createEmitTextPropertyUpdateFunction(propertyName, isParticleProperty) {
|
||||
return function() {
|
||||
updateProperty(propertyName, this.value);
|
||||
updateProperty(propertyName, this.value, isParticleProperty);
|
||||
};
|
||||
}
|
||||
|
||||
function createEmitCheckedPropertyUpdateFunction(propertyName, inverse) {
|
||||
function createEmitCheckedPropertyUpdateFunction(propertyName, inverse, isParticleProperty) {
|
||||
return function() {
|
||||
updateProperty(propertyName, inverse ? !this.checked : this.checked);
|
||||
updateProperty(propertyName, inverse ? !this.checked : this.checked, isParticleProperty);
|
||||
};
|
||||
}
|
||||
|
||||
function createEmitNumberPropertyUpdateFunction(propertyName, multiplier) {
|
||||
function createEmitNumberPropertyUpdateFunction(propertyName, multiplier, isParticleProperty) {
|
||||
return function() {
|
||||
if (multiplier === undefined) {
|
||||
multiplier = 1;
|
||||
}
|
||||
let value = parseFloat(this.value) * multiplier;
|
||||
updateProperty(propertyName, value);
|
||||
updateProperty(propertyName, value, isParticleProperty);
|
||||
};
|
||||
}
|
||||
|
||||
function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier) {
|
||||
function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier, isParticleProperty) {
|
||||
return function () {
|
||||
if (multiplier === undefined) {
|
||||
multiplier = 1;
|
||||
|
@ -1602,11 +1601,11 @@ function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier
|
|||
x: elX.value * multiplier,
|
||||
y: elY.value * multiplier
|
||||
};
|
||||
updateProperty(propertyName, newValue);
|
||||
updateProperty(propertyName, newValue, isParticleProperty);
|
||||
};
|
||||
}
|
||||
|
||||
function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multiplier) {
|
||||
function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multiplier, isParticleProperty) {
|
||||
return function() {
|
||||
if (multiplier === undefined) {
|
||||
multiplier = 1;
|
||||
|
@ -1616,26 +1615,26 @@ function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multi
|
|||
y: elY.value * multiplier,
|
||||
z: elZ.value * multiplier
|
||||
};
|
||||
updateProperty(propertyName, newValue);
|
||||
updateProperty(propertyName, newValue, isParticleProperty);
|
||||
};
|
||||
}
|
||||
|
||||
function createEmitColorPropertyUpdateFunction(propertyName, elRed, elGreen, elBlue) {
|
||||
function createEmitColorPropertyUpdateFunction(propertyName, elRed, elGreen, elBlue, isParticleProperty) {
|
||||
return function() {
|
||||
emitColorPropertyUpdate(propertyName, elRed.value, elGreen.value, elBlue.value);
|
||||
emitColorPropertyUpdate(propertyName, elRed.value, elGreen.value, elBlue.value, isParticleProperty);
|
||||
};
|
||||
}
|
||||
|
||||
function emitColorPropertyUpdate(propertyName, red, green, blue) {
|
||||
function emitColorPropertyUpdate(propertyName, red, green, blue, isParticleProperty) {
|
||||
let newValue = {
|
||||
red: red,
|
||||
green: green,
|
||||
blue: blue
|
||||
};
|
||||
updateProperty(propertyName, newValue);
|
||||
updateProperty(propertyName, newValue, isParticleProperty);
|
||||
}
|
||||
|
||||
function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) {
|
||||
function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString, isParticleProperty) {
|
||||
if (subPropertyElement.checked) {
|
||||
if (propertyValue.indexOf(subPropertyString)) {
|
||||
propertyValue += subPropertyString + ',';
|
||||
|
@ -1644,13 +1643,13 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen
|
|||
// We've unchecked, so remove
|
||||
propertyValue = propertyValue.replace(subPropertyString + ",", "");
|
||||
}
|
||||
updateProperty(propertyName, propertyValue);
|
||||
updateProperty(propertyName, propertyValue, isParticleProperty);
|
||||
}
|
||||
|
||||
function createImageURLUpdateFunction(propertyName) {
|
||||
function createImageURLUpdateFunction(propertyName, isParticleProperty) {
|
||||
return function () {
|
||||
var newTextures = JSON.stringify({ "tex.picture": this.value });
|
||||
updateProperty(propertyName, newTextures);
|
||||
let newTextures = JSON.stringify({ "tex.picture": this.value });
|
||||
updateProperty(propertyName, newTextures, isParticleProperty);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1673,7 +1672,7 @@ function createStringProperty(property, elProperty, elLabel) {
|
|||
elInput.readOnly = true;
|
||||
}
|
||||
|
||||
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName));
|
||||
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty));
|
||||
|
||||
elProperty.appendChild(elLabel);
|
||||
elProperty.appendChild(elInput);
|
||||
|
@ -1709,10 +1708,10 @@ function createBoolProperty(property, elProperty, elLabel) {
|
|||
let subPropertyOf = propertyData.subPropertyOf;
|
||||
if (subPropertyOf !== undefined) {
|
||||
elInput.addEventListener('change', function() {
|
||||
updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], elInput, propertyName);
|
||||
updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], elInput, propertyName, property.isParticleProperty);
|
||||
});
|
||||
} else {
|
||||
elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse));
|
||||
elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse, property.isParticleProperty));
|
||||
}
|
||||
|
||||
return elInput;
|
||||
|
@ -1745,7 +1744,7 @@ function createNumberProperty(property, elProperty, elLabel) {
|
|||
elInput.value = defaultValue;
|
||||
}
|
||||
|
||||
elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals));
|
||||
elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty));
|
||||
|
||||
elProperty.appendChild(elLabel);
|
||||
elProperty.appendChild(elInput);
|
||||
|
@ -1791,7 +1790,7 @@ function createSliderProperty(property, elProperty, elLabel) {
|
|||
if (propertyData.multiplier !== undefined) {
|
||||
inputValue *= propertyData.multiplier;
|
||||
}
|
||||
updateProperty(property.name, inputValue);
|
||||
updateProperty(property.name, inputValue, property.isParticleProperty);
|
||||
};
|
||||
elSlider.oninput = function (event) {
|
||||
let sliderValue = event.target.value;
|
||||
|
@ -1807,7 +1806,7 @@ function createSliderProperty(property, elProperty, elLabel) {
|
|||
if (propertyData.multiplier !== undefined) {
|
||||
sliderValue *= propertyData.multiplier;
|
||||
}
|
||||
updateProperty(property.name, sliderValue);
|
||||
updateProperty(property.name, sliderValue, property.isParticleProperty);
|
||||
};
|
||||
|
||||
elDiv.appendChild(elLabel);
|
||||
|
@ -1843,8 +1842,8 @@ function createVec3Property(property, elProperty, elLabel) {
|
|||
let elInputZ = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT],
|
||||
propertyData.min, propertyData.max, propertyData.step);
|
||||
|
||||
let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY,
|
||||
elInputZ, propertyData.multiplier);
|
||||
let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, elInputZ,
|
||||
propertyData.multiplier, property.isParticleProperty);
|
||||
elInputX.addEventListener('change', inputChangeFunction);
|
||||
elInputY.addEventListener('change', inputChangeFunction);
|
||||
elInputZ.addEventListener('change', inputChangeFunction);
|
||||
|
@ -1876,8 +1875,8 @@ function createVec2Property(property, elProperty, elLabel) {
|
|||
let elInputY = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT],
|
||||
propertyData.min, propertyData.max, propertyData.step);
|
||||
|
||||
let inputChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elInputX,
|
||||
elInputY, propertyData.multiplier);
|
||||
let inputChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elInputX, elInputY,
|
||||
propertyData.multiplier, property.isParticleProperty);
|
||||
elInputX.addEventListener('change', inputChangeFunction);
|
||||
elInputY.addEventListener('change', inputChangeFunction);
|
||||
|
||||
|
@ -1908,7 +1907,8 @@ function createColorProperty(property, elProperty, elLabel) {
|
|||
let elInputG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP);
|
||||
let elInputB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP);
|
||||
|
||||
let inputChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elInputR, elInputG, elInputB);
|
||||
let inputChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elInputR, elInputG, elInputB,
|
||||
property.isParticleProperty);
|
||||
elInputR.addEventListener('change', inputChangeFunction);
|
||||
elInputG.addEventListener('change', inputChangeFunction);
|
||||
elInputB.addEventListener('change', inputChangeFunction);
|
||||
|
@ -1964,7 +1964,7 @@ function createDropdownProperty(property, propertyID, elProperty, elLabel) {
|
|||
elInput.add(option);
|
||||
}
|
||||
|
||||
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName));
|
||||
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty));
|
||||
|
||||
elProperty.appendChild(elLabel);
|
||||
elProperty.appendChild(elInput);
|
||||
|
@ -1991,7 +1991,7 @@ function createTextareaProperty(property, elProperty, elLabel) {
|
|||
elInput.readOnly = true;
|
||||
}
|
||||
|
||||
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName));
|
||||
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty));
|
||||
|
||||
elProperty.appendChild(elInput);
|
||||
|
||||
|
@ -2057,9 +2057,9 @@ function createTextureProperty(property, elProperty, elLabel) {
|
|||
elInput.imageLoad = imageLoad;
|
||||
elInput.oninput = function (event) {
|
||||
// Add throttle
|
||||
var url = event.target.value;
|
||||
let url = event.target.value;
|
||||
imageLoad(url);
|
||||
updateProperty(property.name, url)
|
||||
updateProperty(property.name, url, property.isParticleProperty)
|
||||
};
|
||||
elInput.onchange = elInput.oninput;
|
||||
|
||||
|
@ -2200,7 +2200,7 @@ function reloadServerScripts() {
|
|||
function copySkyboxURLToAmbientURL() {
|
||||
let skyboxURL = getPropertyInputElement("skybox.url").value;
|
||||
getPropertyInputElement("ambientLight.ambientURL").value = skyboxURL;
|
||||
updateProperty("ambientLight.ambientURL", skyboxURL);
|
||||
updateProperty("ambientLight.ambientURL", skyboxURL, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2215,13 +2215,13 @@ function clearUserData() {
|
|||
showUserDataTextArea();
|
||||
showNewJSONEditorButton();
|
||||
hideSaveUserDataButton();
|
||||
updateProperty('userData', elUserData.value);
|
||||
updateProperty('userData', elUserData.value, false);
|
||||
}
|
||||
|
||||
function newJSONEditor() {
|
||||
deleteJSONEditor();
|
||||
createJSONEditor();
|
||||
var data = {};
|
||||
let data = {};
|
||||
setEditorJSON(data);
|
||||
hideUserDataTextArea();
|
||||
hideNewJSONEditorButton();
|
||||
|
@ -2233,7 +2233,7 @@ function saveUserData() {
|
|||
}
|
||||
|
||||
function setUserDataFromEditor(noUpdate) {
|
||||
var json = null;
|
||||
let json = null;
|
||||
try {
|
||||
json = editor.get();
|
||||
} catch (e) {
|
||||
|
@ -2242,7 +2242,7 @@ function setUserDataFromEditor(noUpdate) {
|
|||
if (json === null) {
|
||||
return;
|
||||
} else {
|
||||
var text = editor.getText();
|
||||
let text = editor.getText();
|
||||
if (noUpdate === true) {
|
||||
EventBridge.emitWebEvent(
|
||||
JSON.stringify({
|
||||
|
@ -2255,15 +2255,15 @@ function setUserDataFromEditor(noUpdate) {
|
|||
);
|
||||
return;
|
||||
} else {
|
||||
updateProperty('userData', text);
|
||||
updateProperty('userData', text, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, removeKeys) {
|
||||
var propertyUpdate = {};
|
||||
var parsedData = {};
|
||||
var keysToBeRemoved = removeKeys ? removeKeys : [];
|
||||
let propertyUpdate = {};
|
||||
let parsedData = {};
|
||||
let keysToBeRemoved = removeKeys ? removeKeys : [];
|
||||
try {
|
||||
if ($('#property-userData-editor').css('height') !== "0px") {
|
||||
// if there is an expanded, we want to use its json.
|
||||
|
@ -2278,14 +2278,14 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r
|
|||
if (!(groupName in parsedData)) {
|
||||
parsedData[groupName] = {};
|
||||
}
|
||||
var keys = Object.keys(updateKeyPair);
|
||||
let keys = Object.keys(updateKeyPair);
|
||||
keys.forEach(function (key) {
|
||||
if (updateKeyPair[key] !== null && updateKeyPair[key] !== "null") {
|
||||
if (updateKeyPair[key] instanceof Element) {
|
||||
if (updateKeyPair[key].type === "checkbox") {
|
||||
parsedData[groupName][key] = updateKeyPair[key].checked;
|
||||
} else {
|
||||
var val = isNaN(updateKeyPair[key].value) ? updateKeyPair[key].value : parseInt(updateKeyPair[key].value);
|
||||
let val = isNaN(updateKeyPair[key].value) ? updateKeyPair[key].value : parseInt(updateKeyPair[key].value);
|
||||
parsedData[groupName][key] = val;
|
||||
}
|
||||
} else {
|
||||
|
@ -2318,8 +2318,8 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r
|
|||
var editor = null;
|
||||
|
||||
function createJSONEditor() {
|
||||
var container = document.getElementById("property-userData-editor");
|
||||
var options = {
|
||||
let container = document.getElementById("property-userData-editor");
|
||||
let options = {
|
||||
search: false,
|
||||
mode: 'tree',
|
||||
modes: ['code', 'tree'],
|
||||
|
@ -2331,7 +2331,7 @@ function createJSONEditor() {
|
|||
alert('JSON editor:' + e);
|
||||
},
|
||||
onChange: function() {
|
||||
var currentJSONString = editor.getText();
|
||||
let currentJSONString = editor.getText();
|
||||
|
||||
if (currentJSONString === '{"":""}') {
|
||||
return;
|
||||
|
@ -2432,13 +2432,13 @@ function clearMaterialData() {
|
|||
showMaterialDataTextArea();
|
||||
showNewJSONMaterialEditorButton();
|
||||
hideSaveMaterialDataButton();
|
||||
updateProperty('materialData', elMaterialData.value);
|
||||
updateProperty('materialData', elMaterialData.value, false);
|
||||
}
|
||||
|
||||
function newJSONMaterialEditor() {
|
||||
deleteJSONMaterialEditor();
|
||||
createJSONMaterialEditor();
|
||||
var data = {};
|
||||
let data = {};
|
||||
setMaterialEditorJSON(data);
|
||||
hideMaterialDataTextArea();
|
||||
hideNewJSONMaterialEditorButton();
|
||||
|
@ -2450,7 +2450,7 @@ function saveMaterialData() {
|
|||
}
|
||||
|
||||
function setMaterialDataFromEditor(noUpdate) {
|
||||
var json = null;
|
||||
let json = null;
|
||||
try {
|
||||
json = materialEditor.get();
|
||||
} catch (e) {
|
||||
|
@ -2459,7 +2459,7 @@ function setMaterialDataFromEditor(noUpdate) {
|
|||
if (json === null) {
|
||||
return;
|
||||
} else {
|
||||
var text = materialEditor.getText();
|
||||
let text = materialEditor.getText();
|
||||
if (noUpdate === true) {
|
||||
EventBridge.emitWebEvent(
|
||||
JSON.stringify({
|
||||
|
@ -2472,7 +2472,7 @@ function setMaterialDataFromEditor(noUpdate) {
|
|||
);
|
||||
return;
|
||||
} else {
|
||||
updateProperty('materialData', text);
|
||||
updateProperty('materialData', text, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2480,8 +2480,8 @@ function setMaterialDataFromEditor(noUpdate) {
|
|||
var materialEditor = null;
|
||||
|
||||
function createJSONMaterialEditor() {
|
||||
var container = document.getElementById("property-materialData-editor");
|
||||
var options = {
|
||||
let container = document.getElementById("property-materialData-editor");
|
||||
let options = {
|
||||
search: false,
|
||||
mode: 'tree',
|
||||
modes: ['code', 'tree'],
|
||||
|
@ -2493,7 +2493,7 @@ function createJSONMaterialEditor() {
|
|||
alert('JSON editor:' + e);
|
||||
},
|
||||
onChange: function() {
|
||||
var currentJSONString = materialEditor.getText();
|
||||
let currentJSONString = materialEditor.getText();
|
||||
|
||||
if (currentJSONString === '{"":""}') {
|
||||
return;
|
||||
|
@ -2583,11 +2583,11 @@ function saveJSONMaterialData(noUpdate) {
|
|||
}
|
||||
|
||||
function bindAllNonJSONEditorElements() {
|
||||
var inputs = $('input');
|
||||
var i;
|
||||
let inputs = $('input');
|
||||
let i;
|
||||
for (i = 0; i < inputs.length; ++i) {
|
||||
var input = inputs[i];
|
||||
var field = $(input);
|
||||
let input = inputs[i];
|
||||
let field = $(input);
|
||||
// TODO FIXME: (JSHint) Functions declared within loops referencing
|
||||
// an outer scoped variable may lead to confusing semantics.
|
||||
field.on('focus', function(e) {
|
||||
|
@ -2650,7 +2650,7 @@ function setDropdownValue(event) {
|
|||
*/
|
||||
|
||||
function setTextareaScrolling(element) {
|
||||
var isScrolling = element.scrollHeight > element.offsetHeight;
|
||||
let isScrolling = element.scrollHeight > element.offsetHeight;
|
||||
element.setAttribute("scrolling", isScrolling ? "true" : "false");
|
||||
}
|
||||
|
||||
|
@ -3236,22 +3236,22 @@ function loaded() {
|
|||
let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace");
|
||||
let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh");
|
||||
elParentMaterialNameString.addEventListener('change', function () {
|
||||
updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + this.value);
|
||||
updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + this.value, false);
|
||||
});
|
||||
elParentMaterialNameNumber.addEventListener('change', function () {
|
||||
updateProperty("parentMaterialName", this.value);
|
||||
updateProperty("parentMaterialName", this.value, false);
|
||||
});
|
||||
elParentMaterialNameCheckbox.addEventListener('change', function () {
|
||||
if (this.checked) {
|
||||
updateProperty("parentMaterialName", elParentMaterialNameNumber.value);
|
||||
updateProperty("parentMaterialName", elParentMaterialNameNumber.value, false);
|
||||
showParentMaterialNameBox(true, elParentMaterialNameNumber, elParentMaterialNameString);
|
||||
} else {
|
||||
updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + elParentMaterialNameString.value);
|
||||
updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + elParentMaterialNameString.value, false);
|
||||
showParentMaterialNameBox(false, elParentMaterialNameNumber, elParentMaterialNameString);
|
||||
}
|
||||
});
|
||||
|
||||
getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures'));
|
||||
getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures', false));
|
||||
|
||||
// Collapsible sections
|
||||
let elCollapsible = document.getElementsByClassName("section-header");
|
||||
|
@ -3346,12 +3346,12 @@ function loaded() {
|
|||
let propertyID = elDropdown.getAttribute("propertyID");
|
||||
let property = properties[propertyID];
|
||||
property.elInput = dt;
|
||||
dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property.name));
|
||||
dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property.name, property.isParticleProperty));
|
||||
}
|
||||
|
||||
elDropdowns = document.getElementsByTagName("select");
|
||||
while (elDropdowns.length > 0) {
|
||||
var el = elDropdowns[0];
|
||||
let el = elDropdowns[0];
|
||||
el.parentNode.removeChild(el);
|
||||
elDropdowns = document.getElementsByTagName("select");
|
||||
}
|
||||
|
|
77
tests-manual/qml/qml/MacQml.qml
Normal file
77
tests-manual/qml/qml/MacQml.qml
Normal file
|
@ -0,0 +1,77 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Dialogs 1.1
|
||||
import QtQuick.Controls 1.2
|
||||
import QtWebEngine 1.5
|
||||
|
||||
Item {
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
Rectangle {
|
||||
width: 5
|
||||
height: 5
|
||||
color: "red"
|
||||
ColorAnimation on color { loops: Animation.Infinite; from: "red"; to: "yellow"; duration: 1000 }
|
||||
}
|
||||
|
||||
|
||||
WebEngineView {
|
||||
id: root
|
||||
url: "https://google.com/"
|
||||
x: 6; y: 6;
|
||||
width: parent.width * 0.8
|
||||
height: parent.height * 0.8
|
||||
|
||||
}
|
||||
}
|
60
tests-manual/qml/src/MacQml.cpp
Normal file
60
tests-manual/qml/src/MacQml.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include "MacQml.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QtQuick/QQuickItem>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence;
|
||||
|
||||
void MacQml::update() {
|
||||
auto rootItem =_surface->getRootItem();
|
||||
float now = sinf(secTimestampNow());
|
||||
rootItem->setProperty("level", fabs(now));
|
||||
rootItem->setProperty("muted", now > 0.0f);
|
||||
rootItem->setProperty("statsValue", rand());
|
||||
|
||||
// Fetch any new textures
|
||||
TextureAndFence newTextureAndFence;
|
||||
if (_surface->fetchTexture(newTextureAndFence)) {
|
||||
if (_texture != 0) {
|
||||
auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
_discardLamdba(_texture, readFence);
|
||||
}
|
||||
_texture = newTextureAndFence.first;
|
||||
_glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
}
|
||||
|
||||
void MacQml::init() {
|
||||
Parent::init();
|
||||
_glf.glGenFramebuffers(1, &_fbo);
|
||||
_surface.reset(new hifi::qml::OffscreenSurface());
|
||||
//QUrl url =getTestResource("qml/main.qml");
|
||||
QUrl url = getTestResource("qml/MacQml.qml");
|
||||
hifi::qml::QmlContextObjectCallback callback =[](QQmlContext* context, QQuickItem* item) {
|
||||
};
|
||||
_surface->load(url, callback);
|
||||
_surface->resize(_window->size());
|
||||
_surface->resume();
|
||||
|
||||
}
|
||||
|
||||
void MacQml::draw() {
|
||||
auto size = _window->geometry().size();
|
||||
if (_texture) {
|
||||
_glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
|
||||
_glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
|
||||
_glf.glBlitFramebuffer(
|
||||
// src coordinates
|
||||
0, 0, size.width(), size.height(),
|
||||
// dst coordinates
|
||||
0, 0, size.width(), size.height(),
|
||||
// blit mask and filter
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
_glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
_glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
}
|
16
tests-manual/qml/src/MacQml.h
Normal file
16
tests-manual/qml/src/MacQml.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "TestCase.h"
|
||||
|
||||
#include <qml/OffscreenSurface.h>
|
||||
|
||||
class MacQml : public TestCase {
|
||||
using Parent = TestCase;
|
||||
public:
|
||||
GLuint _texture{ 0 };
|
||||
QmlPtr _surface;
|
||||
GLuint _fbo{ 0 };
|
||||
|
||||
MacQml(const QWindow* window) : Parent(window) {}
|
||||
void update() override;
|
||||
void init() override;
|
||||
void draw() override;
|
||||
};
|
131
tests-manual/qml/src/StressWeb.cpp
Normal file
131
tests-manual/qml/src/StressWeb.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
#include "StressWeb.h"
|
||||
|
||||
#include <QtQuick/QQuickItem>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence;
|
||||
|
||||
static const int DEFAULT_MAX_FPS = 10;
|
||||
static const QString CONTROL_URL{ "/qml/controls/WebEntityView.qml" };
|
||||
static const char* URL_PROPERTY{ "url" };
|
||||
|
||||
QString StressWeb::getSourceUrl(bool video) {
|
||||
static const std::vector<QString> SOURCE_URLS{
|
||||
"https://www.reddit.com/wiki/random",
|
||||
"https://en.wikipedia.org/wiki/Wikipedia:Random",
|
||||
"https://slashdot.org/",
|
||||
};
|
||||
|
||||
static const std::vector<QString> VIDEO_SOURCE_URLS{
|
||||
"https://www.youtube.com/watch?v=gDXwhHm4GhM",
|
||||
"https://www.youtube.com/watch?v=Ch_hoYPPeGc",
|
||||
};
|
||||
|
||||
const auto& sourceUrls = video ? VIDEO_SOURCE_URLS : SOURCE_URLS;
|
||||
auto index = rand() % sourceUrls.size();
|
||||
return sourceUrls[index];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void StressWeb::buildSurface(QmlInfo& qmlInfo, bool video) {
|
||||
++_surfaceCount;
|
||||
auto lifetimeSecs = (uint32_t)(5.0f + (randFloat() * 10.0f));
|
||||
auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs);
|
||||
qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow();
|
||||
qmlInfo.texture = 0;
|
||||
qmlInfo.surface.reset(new hifi::qml::OffscreenSurface());
|
||||
qmlInfo.surface->load(getTestResource(CONTROL_URL), [video](QQmlContext* context, QQuickItem* item) {
|
||||
item->setProperty(URL_PROPERTY, getSourceUrl(video));
|
||||
});
|
||||
qmlInfo.surface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
qmlInfo.surface->resize(_qmlSize);
|
||||
qmlInfo.surface->resume();
|
||||
}
|
||||
|
||||
void StressWeb::destroySurface(QmlInfo& qmlInfo) {
|
||||
auto& surface = qmlInfo.surface;
|
||||
auto& currentTexture = qmlInfo.texture;
|
||||
if (currentTexture) {
|
||||
auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
_discardLamdba(currentTexture, readFence);
|
||||
}
|
||||
auto webView = surface->getRootItem();
|
||||
if (webView) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(webView, "stop");
|
||||
webView->setProperty(URL_PROPERTY, "about:blank");
|
||||
}
|
||||
surface->pause();
|
||||
surface.reset();
|
||||
}
|
||||
|
||||
void StressWeb::update() {
|
||||
auto now = usecTimestampNow();
|
||||
// Fetch any new textures
|
||||
for (size_t x = 0; x < DIVISIONS_X; ++x) {
|
||||
for (size_t y = 0; y < DIVISIONS_Y; ++y) {
|
||||
auto& qmlInfo = _surfaces[x][y];
|
||||
if (!qmlInfo.surface) {
|
||||
if (now < _createStopTime && randFloat() > 0.99f) {
|
||||
buildSurface(qmlInfo, x == 0 && y == 0);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (now > qmlInfo.lifetime) {
|
||||
destroySurface(qmlInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& surface = qmlInfo.surface;
|
||||
auto& currentTexture = qmlInfo.texture;
|
||||
|
||||
TextureAndFence newTextureAndFence;
|
||||
if (surface->fetchTexture(newTextureAndFence)) {
|
||||
if (currentTexture != 0) {
|
||||
auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
_discardLamdba(currentTexture, readFence);
|
||||
}
|
||||
currentTexture = newTextureAndFence.first;
|
||||
_glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StressWeb::init() {
|
||||
Parent::init();
|
||||
_createStopTime = usecTimestampNow() + (3000u * USECS_PER_SECOND);
|
||||
_glf.glGenFramebuffers(1, &_fbo);
|
||||
}
|
||||
|
||||
void StressWeb::draw() {
|
||||
auto size = _window->geometry().size();
|
||||
auto incrementX = size.width() / DIVISIONS_X;
|
||||
auto incrementY = size.height() / DIVISIONS_Y;
|
||||
|
||||
for (uint32_t x = 0; x < DIVISIONS_X; ++x) {
|
||||
for (uint32_t y = 0; y < DIVISIONS_Y; ++y) {
|
||||
auto& qmlInfo = _surfaces[x][y];
|
||||
if (!qmlInfo.surface || !qmlInfo.texture) {
|
||||
continue;
|
||||
}
|
||||
_glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
|
||||
_glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, qmlInfo.texture, 0);
|
||||
_glf.glBlitFramebuffer(
|
||||
// src coordinates
|
||||
0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1,
|
||||
// dst coordinates
|
||||
incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1),
|
||||
// blit mask and filter
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
}
|
||||
}
|
||||
_glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
_glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
34
tests-manual/qml/src/StressWeb.h
Normal file
34
tests-manual/qml/src/StressWeb.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include "TestCase.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <qml/OffscreenSurface.h>
|
||||
|
||||
#define DIVISIONS_X 5
|
||||
#define DIVISIONS_Y 5
|
||||
|
||||
class StressWeb : public TestCase {
|
||||
using Parent = TestCase;
|
||||
public:
|
||||
using QmlPtr = QSharedPointer<hifi::qml::OffscreenSurface>;
|
||||
|
||||
struct QmlInfo {
|
||||
QmlPtr surface;
|
||||
GLuint texture{ 0 };
|
||||
uint64_t lifetime{ 0 };
|
||||
};
|
||||
|
||||
size_t _surfaceCount{ 0 };
|
||||
uint64_t _createStopTime{ 0 };
|
||||
const QSize _qmlSize{ 640, 480 };
|
||||
std::array<std::array<QmlInfo, DIVISIONS_Y>, DIVISIONS_X> _surfaces;
|
||||
GLuint _fbo{ 0 };
|
||||
|
||||
StressWeb(const QWindow* window) : Parent(window) {}
|
||||
static QString getSourceUrl(bool video);
|
||||
void buildSurface(QmlInfo& qmlInfo, bool video);
|
||||
void destroySurface(QmlInfo& qmlInfo);
|
||||
void update() override;
|
||||
void init() override;
|
||||
void draw() override;
|
||||
};
|
25
tests-manual/qml/src/TestCase.cpp
Normal file
25
tests-manual/qml/src/TestCase.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "TestCase.h"
|
||||
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QDir>
|
||||
|
||||
void TestCase::destroy() {
|
||||
}
|
||||
void TestCase::update() {
|
||||
}
|
||||
|
||||
void TestCase::init() {
|
||||
_glf.initializeOpenGLFunctions();
|
||||
_discardLamdba = hifi::qml::OffscreenSurface::getDiscardLambda();
|
||||
}
|
||||
|
||||
QUrl TestCase::getTestResource(const QString& relativePath) {
|
||||
static QString dir;
|
||||
if (dir.isEmpty()) {
|
||||
QDir path(__FILE__);
|
||||
path.cdUp();
|
||||
dir = path.cleanPath(path.absoluteFilePath("../")) + "/";
|
||||
qDebug() << "Resources Path: " << dir;
|
||||
}
|
||||
return QUrl::fromLocalFile(dir + relativePath);
|
||||
}
|
23
tests-manual/qml/src/TestCase.h
Normal file
23
tests-manual/qml/src/TestCase.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QOpenGLFunctions_4_1_Core>
|
||||
#include <qml/OffscreenSurface.h>
|
||||
|
||||
class TestCase {
|
||||
public:
|
||||
using QmlPtr = QSharedPointer<hifi::qml::OffscreenSurface>;
|
||||
using Builder = std::function<TestCase*(const QWindow*)>;
|
||||
TestCase(const QWindow* window) : _window(window) {}
|
||||
virtual void init();
|
||||
virtual void destroy();
|
||||
virtual void update();
|
||||
virtual void draw() = 0;
|
||||
static QUrl getTestResource(const QString& relativePath);
|
||||
|
||||
protected:
|
||||
QOpenGLFunctions_4_1_Core _glf;
|
||||
const QWindow* _window;
|
||||
std::function<void(uint32_t, void*)> _discardLamdba;
|
||||
};
|
|
@ -43,6 +43,11 @@
|
|||
#include <qml/OffscreenSurface.h>
|
||||
#include <unordered_set>
|
||||
#include <array>
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <gl/Context.h>
|
||||
|
||||
#include "TestCase.h"
|
||||
#include "MacQml.h"
|
||||
|
||||
namespace gl {
|
||||
extern void initModuleGl();
|
||||
|
@ -67,53 +72,37 @@ QUrl getTestResource(const QString& relativePath) {
|
|||
return QUrl::fromLocalFile(dir + relativePath);
|
||||
}
|
||||
|
||||
#define DIVISIONS_X 5
|
||||
#define DIVISIONS_Y 5
|
||||
|
||||
using QmlPtr = QSharedPointer<hifi::qml::OffscreenSurface>;
|
||||
using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence;
|
||||
|
||||
struct QmlInfo {
|
||||
QmlPtr surface;
|
||||
GLuint texture{ 0 };
|
||||
uint64_t lifetime{ 0 };
|
||||
};
|
||||
|
||||
class TestWindow : public QWindow {
|
||||
public:
|
||||
TestWindow();
|
||||
TestWindow(const TestCase::Builder& caseBuilder);
|
||||
|
||||
private:
|
||||
QOpenGLContext _glContext;
|
||||
OffscreenGLCanvas _sharedContext;
|
||||
std::array<std::array<QmlInfo, DIVISIONS_Y>, DIVISIONS_X> _surfaces;
|
||||
|
||||
TestCase* _testCase{ nullptr };
|
||||
QOpenGLFunctions_4_1_Core _glf;
|
||||
std::function<void(uint32_t, void*)> _discardLamdba;
|
||||
QSize _size;
|
||||
size_t _surfaceCount{ 0 };
|
||||
GLuint _fbo{ 0 };
|
||||
const QSize _qmlSize{ 640, 480 };
|
||||
bool _aboutToQuit{ false };
|
||||
uint64_t _createStopTime;
|
||||
void initGl();
|
||||
void updateSurfaces();
|
||||
void buildSurface(QmlInfo& qmlInfo, bool allowVideo);
|
||||
void destroySurface(QmlInfo& qmlInfo);
|
||||
void resizeWindow(const QSize& size);
|
||||
void draw();
|
||||
void resizeEvent(QResizeEvent* ev) override;
|
||||
};
|
||||
|
||||
TestWindow::TestWindow() {
|
||||
TestWindow::TestWindow(const TestCase::Builder& builder) {
|
||||
Setting::init();
|
||||
|
||||
_testCase = builder(this);
|
||||
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
|
||||
qmlRegisterType<QTestItem>("Hifi", 1, 0, "TestItem");
|
||||
|
||||
show();
|
||||
_createStopTime = usecTimestampNow() + (3000u * USECS_PER_SECOND);
|
||||
|
||||
resize(QSize(800, 600));
|
||||
|
||||
|
@ -129,162 +118,84 @@ TestWindow::TestWindow() {
|
|||
});
|
||||
}
|
||||
|
||||
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
|
||||
Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context();
|
||||
OffscreenGLCanvas* _chromiumShareContext{ nullptr};
|
||||
void TestWindow::initGl() {
|
||||
_glContext.setFormat(format());
|
||||
|
||||
auto globalShareContext = qt_gl_global_share_context();
|
||||
if (globalShareContext) {
|
||||
_glContext.setShareContext(globalShareContext);
|
||||
globalShareContext->makeCurrent(this);
|
||||
gl::Context::setupDebugLogging(globalShareContext);
|
||||
globalShareContext->doneCurrent();
|
||||
}
|
||||
|
||||
if (!_glContext.create() || !_glContext.makeCurrent(this)) {
|
||||
qFatal("Unable to intialize Window GL context");
|
||||
}
|
||||
gl::Context::setupDebugLogging(&_glContext);
|
||||
gl::initModuleGl();
|
||||
|
||||
_glf.initializeOpenGLFunctions();
|
||||
_glf.glGenFramebuffers(1, &_fbo);
|
||||
|
||||
if (!_sharedContext.create(&_glContext) || !_sharedContext.makeCurrent()) {
|
||||
qFatal("Unable to intialize Shared GL context");
|
||||
}
|
||||
hifi::qml::OffscreenSurface::setSharedContext(_sharedContext.getContext());
|
||||
_discardLamdba = hifi::qml::OffscreenSurface::getDiscardLambda();
|
||||
|
||||
if (!globalShareContext) {
|
||||
_chromiumShareContext = new OffscreenGLCanvas();
|
||||
_chromiumShareContext->setObjectName("ChromiumShareContext");
|
||||
_chromiumShareContext->create(&_glContext);
|
||||
if (!_chromiumShareContext->makeCurrent()) {
|
||||
qFatal("Unable to make chromium shared context current");
|
||||
}
|
||||
|
||||
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
|
||||
_chromiumShareContext->doneCurrent();
|
||||
}
|
||||
|
||||
// Restore the GL widget context
|
||||
if (!_glContext.makeCurrent(this)) {
|
||||
qFatal("Unable to make window context current");
|
||||
}
|
||||
|
||||
_testCase->init();
|
||||
}
|
||||
|
||||
void TestWindow::resizeWindow(const QSize& size) {
|
||||
_size = size;
|
||||
}
|
||||
|
||||
static const int DEFAULT_MAX_FPS = 10;
|
||||
static const QString CONTROL_URL{ "/qml/controls/WebEntityView.qml" };
|
||||
static const char* URL_PROPERTY{ "url" };
|
||||
|
||||
QString getSourceUrl(bool video) {
|
||||
static const std::vector<QString> SOURCE_URLS{
|
||||
"https://www.reddit.com/wiki/random",
|
||||
"https://en.wikipedia.org/wiki/Wikipedia:Random",
|
||||
"https://slashdot.org/",
|
||||
};
|
||||
|
||||
static const std::vector<QString> VIDEO_SOURCE_URLS{
|
||||
"https://www.youtube.com/watch?v=gDXwhHm4GhM",
|
||||
"https://www.youtube.com/watch?v=Ch_hoYPPeGc",
|
||||
};
|
||||
|
||||
const auto& sourceUrls = video ? VIDEO_SOURCE_URLS : SOURCE_URLS;
|
||||
auto index = rand() % sourceUrls.size();
|
||||
return sourceUrls[index];
|
||||
}
|
||||
|
||||
void TestWindow::buildSurface(QmlInfo& qmlInfo, bool video) {
|
||||
++_surfaceCount;
|
||||
auto lifetimeSecs = (uint32_t)(5.0f + (randFloat() * 10.0f));
|
||||
auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs);
|
||||
qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow();
|
||||
qmlInfo.texture = 0;
|
||||
qmlInfo.surface.reset(new hifi::qml::OffscreenSurface());
|
||||
qmlInfo.surface->load(getTestResource(CONTROL_URL), [video](QQmlContext* context, QQuickItem* item) {
|
||||
item->setProperty(URL_PROPERTY, getSourceUrl(video));
|
||||
});
|
||||
qmlInfo.surface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
qmlInfo.surface->resize(_qmlSize);
|
||||
qmlInfo.surface->resume();
|
||||
}
|
||||
|
||||
void TestWindow::destroySurface(QmlInfo& qmlInfo) {
|
||||
auto& surface = qmlInfo.surface;
|
||||
auto& currentTexture = qmlInfo.texture;
|
||||
if (currentTexture) {
|
||||
auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
_discardLamdba(currentTexture, readFence);
|
||||
}
|
||||
auto webView = surface->getRootItem();
|
||||
if (webView) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(webView, "stop");
|
||||
webView->setProperty(URL_PROPERTY, "about:blank");
|
||||
}
|
||||
surface->pause();
|
||||
surface.reset();
|
||||
}
|
||||
|
||||
void TestWindow::updateSurfaces() {
|
||||
auto now = usecTimestampNow();
|
||||
// Fetch any new textures
|
||||
for (size_t x = 0; x < DIVISIONS_X; ++x) {
|
||||
for (size_t y = 0; y < DIVISIONS_Y; ++y) {
|
||||
auto& qmlInfo = _surfaces[x][y];
|
||||
if (!qmlInfo.surface) {
|
||||
if (now < _createStopTime && randFloat() > 0.99f) {
|
||||
buildSurface(qmlInfo, x == 0 && y == 0);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (now > qmlInfo.lifetime) {
|
||||
destroySurface(qmlInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& surface = qmlInfo.surface;
|
||||
auto& currentTexture = qmlInfo.texture;
|
||||
|
||||
TextureAndFence newTextureAndFence;
|
||||
if (surface->fetchTexture(newTextureAndFence)) {
|
||||
if (currentTexture != 0) {
|
||||
auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
_discardLamdba(currentTexture, readFence);
|
||||
}
|
||||
currentTexture = newTextureAndFence.first;
|
||||
_glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestWindow::draw() {
|
||||
if (_aboutToQuit) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Attempting to draw before we're visible and have a valid size will
|
||||
// produce GL errors.
|
||||
if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] { initGl(); });
|
||||
|
||||
|
||||
if (!_glContext.makeCurrent(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateSurfaces();
|
||||
|
||||
auto size = this->geometry().size();
|
||||
auto incrementX = size.width() / DIVISIONS_X;
|
||||
auto incrementY = size.height() / DIVISIONS_Y;
|
||||
|
||||
_testCase->update();
|
||||
|
||||
auto size = geometry().size();
|
||||
_glf.glViewport(0, 0, size.width(), size.height());
|
||||
_glf.glClearColor(1, 0, 0, 1);
|
||||
_glf.glClear(GL_COLOR_BUFFER_BIT);
|
||||
for (uint32_t x = 0; x < DIVISIONS_X; ++x) {
|
||||
for (uint32_t y = 0; y < DIVISIONS_Y; ++y) {
|
||||
auto& qmlInfo = _surfaces[x][y];
|
||||
if (!qmlInfo.surface || !qmlInfo.texture) {
|
||||
continue;
|
||||
}
|
||||
_glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
|
||||
_glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, qmlInfo.texture, 0);
|
||||
_glf.glBlitFramebuffer(
|
||||
// src coordinates
|
||||
0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1,
|
||||
// dst coordinates
|
||||
incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1),
|
||||
// blit mask and filter
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
}
|
||||
}
|
||||
_glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
_glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
_testCase->draw();
|
||||
|
||||
_glContext.swapBuffers(this);
|
||||
}
|
||||
|
||||
|
@ -292,19 +203,15 @@ void TestWindow::resizeEvent(QResizeEvent* ev) {
|
|||
resizeWindow(ev->size());
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setDepthBufferSize(24);
|
||||
format.setStencilBufferSize(8);
|
||||
int main(int argc, char** argv) {
|
||||
auto format = getDefaultOpenGLSurfaceFormat();
|
||||
format.setVersion(4, 1);
|
||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
||||
format.setOption(QSurfaceFormat::DebugContext);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
// setFormat(format);
|
||||
|
||||
QGuiApplication app(argc, argv);
|
||||
TestWindow window;
|
||||
TestCase::Builder builder = [](const QWindow* window)->TestCase*{ return new MacQml(window); };
|
||||
TestWindow window(builder);
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue