diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml
index 0460b5b16a..13a435c4e5 100644
--- a/interface/resources/qml/controls/FlickableWebViewCore.qml
+++ b/interface/resources/qml/controls/FlickableWebViewCore.qml
@@ -19,6 +19,7 @@ Flickable {
signal newViewRequestedCallback(var request)
signal loadingChangedCallback(var loadRequest)
+ pressDelay: 300
boundsBehavior: Flickable.StopAtBounds
@@ -167,4 +168,11 @@ Flickable {
playing: visible
z: 10000
}
+
+ MouseArea {
+ anchors.fill: parent
+ onWheel: {
+ flick.flick(0, wheel.angleDelta.y*10)
+ }
+ }
}
diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
index 04f106d0f9..14a84311aa 100644
--- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
+++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
@@ -40,12 +40,20 @@ Rectangle {
Hifi.QmlCommerce {
id: commerce;
+ onAccountResult: {
+ if (result.status === "success") {
+ commerce.getKeyFilePathIfExists();
+ } else {
+ // unsure how to handle a failure here. We definitely cannot proceed.
+ }
+ }
+
onLoginStatusResult: {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
} else if (isLoggedIn) {
root.activeView = "initialize";
- commerce.getKeyFilePathIfExists();
+ commerce.account();
}
}
@@ -203,7 +211,7 @@ Rectangle {
commerce.getLoginStatus();
}
}
-
+
HifiWallet.NeedsLogIn {
id: needsLogIn;
visible: root.activeView === "needsLogIn";
@@ -239,7 +247,7 @@ Rectangle {
}
}
}
-
+
//
// "WALLET NOT SET UP" START
//
@@ -250,7 +258,7 @@ Rectangle {
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
-
+
RalewayRegular {
id: notSetUpText;
text: "Your wallet isn't set up.
Set up your Wallet (no credit card necessary) to claim your free HFC " +
@@ -281,7 +289,7 @@ Rectangle {
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 24;
-
+
// "Cancel" button
HifiControlsUit.Button {
id: cancelButton;
diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
index 383223a49c..697249f740 100644
--- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
@@ -35,12 +35,20 @@ Rectangle {
Hifi.QmlCommerce {
id: commerce;
+ onAccountResult: {
+ if (result.status === "success") {
+ commerce.getKeyFilePathIfExists();
+ } else {
+ // unsure how to handle a failure here. We definitely cannot proceed.
+ }
+ }
+
onLoginStatusResult: {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
} else if (isLoggedIn) {
root.activeView = "initialize";
- commerce.getKeyFilePathIfExists();
+ commerce.account();
}
}
@@ -174,7 +182,7 @@ Rectangle {
commerce.getLoginStatus();
}
}
-
+
HifiWallet.NeedsLogIn {
id: needsLogIn;
visible: root.activeView === "needsLogIn";
@@ -210,7 +218,7 @@ Rectangle {
}
}
}
-
+
//
// "WALLET NOT SET UP" START
//
@@ -221,7 +229,7 @@ Rectangle {
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
-
+
RalewayRegular {
id: notSetUpText;
text: "Your wallet isn't set up.
Set up your Wallet (no credit card necessary) to claim your free HFC " +
@@ -252,7 +260,7 @@ Rectangle {
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 24;
-
+
// "Cancel" button
HifiControlsUit.Button {
id: cancelButton;
@@ -309,7 +317,7 @@ Rectangle {
anchors.topMargin: 8;
anchors.bottom: actionButtonsContainer.top;
anchors.bottomMargin: 8;
-
+
//
// FILTER BAR START
//
@@ -383,7 +391,7 @@ Rectangle {
itemHref: root_file_url;
anchors.topMargin: 12;
anchors.bottomMargin: 12;
-
+
Connections {
onSendToPurchases: {
if (msg.method === 'purchases_itemInfoClicked') {
@@ -402,7 +410,7 @@ Rectangle {
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width;
-
+
// Explanitory text
RalewayRegular {
id: haventPurchasedYet;
diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp
index 2faa08064d..a0664f4f9b 100644
--- a/interface/src/commerce/Wallet.cpp
+++ b/interface/src/commerce/Wallet.cpp
@@ -31,6 +31,13 @@
#include
#include
+// I know, right? But per https://www.openssl.org/docs/faq.html
+// this avoids OPENSSL_Uplink(00007FF847238000,08): no OPENSSL_Applink
+// at runtime.
+#ifdef Q_OS_WIN
+#include
+#endif
+
static const char* KEY_FILE = "hifikey";
static const char* IMAGE_FILE = "hifi_image"; // eventually this will live in keyfile
diff --git a/interface/src/main.cpp b/interface/src/main.cpp
index 503daa177d..cb90160cfe 100644
--- a/interface/src/main.cpp
+++ b/interface/src/main.cpp
@@ -144,25 +144,33 @@ int main(int argc, const char* argv[]) {
#endif
}
+
+ // FIXME this method of checking the OpenGL version screws up the `QOpenGLContext::globalShareContext()` value, which in turn
+ // leads to crashes when creating the real OpenGL instance. Disabling for now until we come up with a better way of checking
+ // the GL version on the system without resorting to creating a full Qt application
+#if 0
// Check OpenGL version.
// This is done separately from the main Application so that start-up and shut-down logic within the main Application is
// not made more complicated than it already is.
- bool override = false;
+ bool overrideGLCheck = false;
+
QJsonObject glData;
{
OpenGLVersionChecker openGLVersionChecker(argc, const_cast(argv));
bool valid = true;
- glData = openGLVersionChecker.checkVersion(valid, override);
+ glData = openGLVersionChecker.checkVersion(valid, overrideGLCheck);
if (!valid) {
- if (override) {
+ if (overrideGLCheck) {
auto glVersion = glData["version"].toString();
- qCDebug(interfaceapp, "Running on insufficient OpenGL version: %s.", glVersion.toStdString().c_str());
+ qCWarning(interfaceapp, "Running on insufficient OpenGL version: %s.", glVersion.toStdString().c_str());
} else {
- qCDebug(interfaceapp, "Early exit due to OpenGL version.");
+ qCWarning(interfaceapp, "Early exit due to OpenGL version.");
return 0;
}
}
}
+#endif
+
// Debug option to demonstrate that the client's local time does not
// need to be in sync with any other network node. This forces clock
@@ -223,8 +231,9 @@ int main(int argc, const char* argv[]) {
Application app(argcExtended, const_cast(argvExtended.data()), startupTime, runningMarkerExisted);
+#if 0
// If we failed the OpenGLVersion check, log it.
- if (override) {
+ if (overrideGLcheck) {
auto accountManager = DependencyManager::get();
if (accountManager->isLoggedIn()) {
UserActivityLogger::getInstance().insufficientGLVersion(glData);
@@ -238,6 +247,8 @@ int main(int argc, const char* argv[]) {
});
}
}
+#endif
+
// Setup local server
QLocalServer server { &app };
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index 9f4a9a3c4a..d754d59721 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -91,7 +91,12 @@ void entitiesScriptEngineDeleter(ScriptEngine* engine) {
};
// Wait for the scripting thread from the thread pool to avoid hanging the main thread
- QThreadPool::globalInstance()->start(new WaitRunnable(engine));
+ auto threadPool = QThreadPool::globalInstance();
+ if (threadPool) {
+ threadPool->start(new WaitRunnable(engine));
+ } else {
+ delete engine;
+ }
}
void EntityTreeRenderer::resetEntitiesScriptEngine() {
diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
index 7349e9147e..3328076911 100644
--- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
@@ -91,20 +91,27 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
qCWarning(entitiesrenderer) << "Bad particle properties";
}
}
- if (_particleProperties != newParticleProperties) {
+
+ if (resultWithReadLock([&]{ return _particleProperties != newParticleProperties; })) {
_timeUntilNextEmit = 0;
- _particleProperties = newParticleProperties;
+ withWriteLock([&]{
+ _particleProperties = newParticleProperties;
+ });
}
_emitting = entity->getIsEmitting();
- if (_particleProperties.textures.isEmpty()) {
+ bool hasTexture = resultWithReadLock([&]{ return _particleProperties.textures.isEmpty(); });
+ if (hasTexture) {
if (_networkTexture) {
withWriteLock([&] {
_networkTexture.reset();
});
}
} else {
- if (!_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures)) {
+ bool textureNeedsUpdate = resultWithReadLock([&]{
+ return !_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures);
+ });
+ if (textureNeedsUpdate) {
withWriteLock([&] {
_networkTexture = DependencyManager::get()->getTexture(_particleProperties.textures);
});
@@ -115,15 +122,17 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
// Fill in Uniforms structure
ParticleUniforms particleUniforms;
- particleUniforms.radius.start = _particleProperties.radius.range.start;
- particleUniforms.radius.middle = _particleProperties.radius.gradient.target;
- particleUniforms.radius.finish = _particleProperties.radius.range.finish;
- particleUniforms.radius.spread = _particleProperties.radius.gradient.spread;
- particleUniforms.color.start = _particleProperties.getColorStart();
- particleUniforms.color.middle = _particleProperties.getColorMiddle();
- particleUniforms.color.finish = _particleProperties.getColorFinish();
- particleUniforms.color.spread = _particleProperties.getColorSpread();
- particleUniforms.lifespan = _particleProperties.lifespan;
+ withReadLock([&]{
+ particleUniforms.radius.start = _particleProperties.radius.range.start;
+ particleUniforms.radius.middle = _particleProperties.radius.gradient.target;
+ particleUniforms.radius.finish = _particleProperties.radius.range.finish;
+ particleUniforms.radius.spread = _particleProperties.radius.gradient.spread;
+ particleUniforms.color.start = _particleProperties.getColorStart();
+ particleUniforms.color.middle = _particleProperties.getColorMiddle();
+ particleUniforms.color.finish = _particleProperties.getColorFinish();
+ particleUniforms.color.spread = _particleProperties.getColorSpread();
+ particleUniforms.lifespan = _particleProperties.lifespan;
+ });
// Update particle uniforms
memcpy(&_uniformBuffer.edit(), &particleUniforms, sizeof(ParticleUniforms));
}
@@ -146,35 +155,26 @@ Item::Bound ParticleEffectEntityRenderer::getBound() {
static const size_t VERTEX_PER_PARTICLE = 4;
-bool ParticleEffectEntityRenderer::emitting() const {
- return (
- _emitting &&
- _particleProperties.emission.rate > 0.0f &&
- _particleProperties.lifespan > 0.0f &&
- _particleProperties.polar.start <= _particleProperties.polar.finish
- );
-}
-
-void ParticleEffectEntityRenderer::createParticle(uint64_t now) {
+ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties) {
CpuParticle particle;
- const auto& accelerationSpread = _particleProperties.emission.acceleration.spread;
- const auto& azimuthStart = _particleProperties.azimuth.start;
- const auto& azimuthFinish = _particleProperties.azimuth.finish;
- const auto& emitDimensions = _particleProperties.emission.dimensions;
- const auto& emitAcceleration = _particleProperties.emission.acceleration.target;
- auto emitOrientation = _particleProperties.emission.orientation;
- const auto& emitRadiusStart = glm::max(_particleProperties.radiusStart, EPSILON); // Avoid math complications at center
- const auto& emitSpeed = _particleProperties.emission.speed.target;
- const auto& speedSpread = _particleProperties.emission.speed.spread;
- const auto& polarStart = _particleProperties.polar.start;
- const auto& polarFinish = _particleProperties.polar.finish;
+ const auto& accelerationSpread = particleProperties.emission.acceleration.spread;
+ const auto& azimuthStart = particleProperties.azimuth.start;
+ const auto& azimuthFinish = particleProperties.azimuth.finish;
+ const auto& emitDimensions = particleProperties.emission.dimensions;
+ const auto& emitAcceleration = particleProperties.emission.acceleration.target;
+ auto emitOrientation = particleProperties.emission.orientation;
+ const auto& emitRadiusStart = glm::max(particleProperties.radiusStart, EPSILON); // Avoid math complications at center
+ const auto& emitSpeed = particleProperties.emission.speed.target;
+ const auto& speedSpread = particleProperties.emission.speed.spread;
+ const auto& polarStart = particleProperties.polar.start;
+ const auto& polarFinish = particleProperties.polar.finish;
particle.seed = randFloatInRange(-1.0f, 1.0f);
- particle.expiration = now + (uint64_t)(_particleProperties.lifespan * USECS_PER_SECOND);
- if (_particleProperties.emission.shouldTrail) {
- particle.position = _modelTransform.getTranslation();
- emitOrientation = _modelTransform.getRotation() * emitOrientation;
+ particle.expiration = now + (uint64_t)(particleProperties.lifespan * USECS_PER_SECOND);
+ if (particleProperties.emission.shouldTrail) {
+ particle.position = baseTransform.getTranslation();
+ emitOrientation = baseTransform.getRotation() * emitOrientation;
}
// Position, velocity, and acceleration
@@ -232,7 +232,7 @@ void ParticleEffectEntityRenderer::createParticle(uint64_t now) {
particle.acceleration = emitAcceleration + randFloatInRange(-1.0f, 1.0f) * accelerationSpread;
}
- _cpuParticles.push_back(particle);
+ return particle;
}
void ParticleEffectEntityRenderer::stepSimulation() {
@@ -244,14 +244,19 @@ void ParticleEffectEntityRenderer::stepSimulation() {
const auto now = usecTimestampNow();
const auto interval = std::min(USECS_PER_SECOND / 60, now - _lastSimulated);
_lastSimulated = now;
+
+ particle::Properties particleProperties;
+ withReadLock([&]{
+ particleProperties = _particleProperties;
+ });
- if (emitting()) {
- uint64_t emitInterval = (uint64_t)(USECS_PER_SECOND / _particleProperties.emission.rate);
- if (interval >= _timeUntilNextEmit) {
+ if (_emitting && particleProperties.emitting()) {
+ uint64_t emitInterval = particleProperties.emitIntervalUsecs();
+ if (emitInterval > 0 && interval >= _timeUntilNextEmit) {
auto timeRemaining = interval;
while (timeRemaining > _timeUntilNextEmit) {
// emit particle
- createParticle(now);
+ _cpuParticles.push_back(createParticle(now, _modelTransform, particleProperties));
_timeUntilNextEmit = emitInterval;
if (emitInterval < timeRemaining) {
timeRemaining -= emitInterval;
@@ -263,7 +268,7 @@ void ParticleEffectEntityRenderer::stepSimulation() {
}
// Kill any particles that have expired or are over the max size
- while (_cpuParticles.size() > _particleProperties.maxParticles || (!_cpuParticles.empty() && _cpuParticles.front().expiration <= now)) {
+ while (_cpuParticles.size() > particleProperties.maxParticles || (!_cpuParticles.empty() && _cpuParticles.front().expiration <= now)) {
_cpuParticles.pop_front();
}
diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h
index e4c1d4be13..be2641c0c9 100644
--- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h
@@ -93,9 +93,8 @@ private:
};
- void createParticle(uint64_t now);
+ static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties);
void stepSimulation();
- bool emitting() const;
particle::Properties _particleProperties;
CpuParticles _cpuParticles;
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
index d39139257b..8757bcbb0f 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
@@ -30,14 +30,11 @@ TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) :
}
-
-void TextEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) {
+TextEntityRenderer::~TextEntityRenderer() {
auto geometryCache = DependencyManager::get();
if (_geometryID && geometryCache) {
geometryCache->releaseID(_geometryID);
}
- delete _textRenderer;
- _textRenderer = nullptr;
}
bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h
index 1d8227069e..b0a72cf253 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h
@@ -24,14 +24,13 @@ class TextEntityRenderer : public TypedEntityRenderer {
using Pointer = std::shared_ptr;
public:
TextEntityRenderer(const EntityItemPointer& entity);
-
+ ~TextEntityRenderer();
private:
- virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override;
int _geometryID{ 0 };
- TextRenderer3D* _textRenderer;
+ std::shared_ptr _textRenderer;
bool _faceCamera;
glm::vec3 _dimensions;
glm::vec3 _textColor;
diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp
index bc6830e1a7..9bbf4323da 100644
--- a/libraries/entities/src/ParticleEffectEntityItem.cpp
+++ b/libraries/entities/src/ParticleEffectEntityItem.cpp
@@ -104,7 +104,7 @@ bool operator!=(const Properties& a, const Properties& b) {
return !(a == b);
}
-bool particle::Properties::valid() const {
+bool Properties::valid() const {
if (glm::any(glm::isnan(emission.orientation))) {
qCWarning(entities) << "Bad particle data";
return false;
@@ -133,6 +133,19 @@ bool particle::Properties::valid() const {
(radius.gradient.spread == glm::clamp(radius.gradient.spread, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS));
}
+bool Properties::emitting() const {
+ return emission.rate > 0.0f && lifespan > 0.0f && polar.start <= polar.finish;
+
+}
+
+uint64_t Properties::emitIntervalUsecs() const {
+ if (emission.rate > 0.0f) {
+ return (uint64_t)(USECS_PER_SECOND / emission.rate);
+ }
+ return 0;
+}
+
+
EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItemPointer entity { new ParticleEffectEntityItem(entityID) };
entity->setProperties(properties);
diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h
index 6a3791f77e..9c0fd0ef95 100644
--- a/libraries/entities/src/ParticleEffectEntityItem.h
+++ b/libraries/entities/src/ParticleEffectEntityItem.h
@@ -160,7 +160,9 @@ namespace particle {
Properties() {};
Properties(const Properties& other) { *this = other; }
bool valid() const;
-
+ bool emitting() const;
+ uint64_t emitIntervalUsecs() const;
+
Properties& operator =(const Properties& other) {
color = other.color;
alpha = other.alpha;
diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp
index ab91ca0902..28982703dd 100644
--- a/libraries/gl/src/gl/GLHelpers.cpp
+++ b/libraries/gl/src/gl/GLHelpers.cpp
@@ -40,8 +40,11 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
int glVersionToInteger(QString glVersion) {
QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]"));
- int majorNumber = versionParts[0].toInt();
- int minorNumber = versionParts[1].toInt();
+ int majorNumber = 0, minorNumber = 0;
+ if (versionParts.size() >= 2) {
+ majorNumber = versionParts[0].toInt();
+ minorNumber = versionParts[1].toInt();
+ }
return (majorNumber << 16) | minorNumber;
}
diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp
index 7758ddaf49..943b8148ef 100644
--- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp
+++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp
@@ -467,12 +467,14 @@ void GLVariableAllocationSupport::updateMemoryPressure() {
_demoteQueue = WorkQueue();
// Populate the existing textures into the queue
- for (const auto& texture : strongTextures) {
- // Race conditions can still leave nulls in the list, so we need to check
- if (!texture) {
- continue;
+ if (_memoryPressureState != MemoryPressureState::Idle) {
+ for (const auto& texture : strongTextures) {
+ // Race conditions can still leave nulls in the list, so we need to check
+ if (!texture) {
+ continue;
+ }
+ addToWorkQueue(texture);
}
- addToWorkQueue(texture);
}
}
}
diff --git a/libraries/shared/src/Interpolate.cpp b/libraries/shared/src/Interpolate.cpp
index 35c164f0f2..cd595ee1ed 100644
--- a/libraries/shared/src/Interpolate.cpp
+++ b/libraries/shared/src/Interpolate.cpp
@@ -69,7 +69,7 @@ float Interpolate::simpleNonLinearBlend(float fraction) {
}
float Interpolate::calculateFadeRatio(quint64 start) {
- const float FADE_TIME = 1.0f;
+ const float FADE_TIME = 0.5f;
float t = 2.0f * std::min(((float)(usecTimestampNow() - start)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-powf(2.0f, -10.0f * (t - 1.0f)) + 2.0f);
diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp
index d29dc7a0f9..d03842d45a 100644
--- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp
+++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp
@@ -528,6 +528,12 @@ void OffscreenQmlSurface::create() {
connect(_quickWindow, &QQuickWindow::focusObjectChanged, this, &OffscreenQmlSurface::onFocusObjectChanged);
+ // acquireEngine interrogates the GL context, so we need to have the context current here
+ if (!_canvas->makeCurrent()) {
+ qFatal("Failed to make context current for QML Renderer");
+ return;
+ }
+
// Create a QML engine.
auto qmlEngine = acquireEngine(_quickWindow);
@@ -540,11 +546,6 @@ void OffscreenQmlSurface::create() {
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
_qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, _qmlContext));
-
- if (!_canvas->makeCurrent()) {
- qWarning("Failed to make context current for QML Renderer");
- return;
- }
_renderControl->initialize(_canvas->getContext());
// When Quick says there is a need to render, we will not render immediately. Instead,
@@ -639,7 +640,7 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {
_qmlContext->setBaseUrl(baseUrl);
}
-void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function f) {
+void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function onQmlLoadedCallback) {
if (QThread::currentThread() != thread()) {
qCWarning(uiLogging) << "Called load on a non-surface thread";
}
@@ -659,28 +660,28 @@ void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std
auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous);
if (qmlComponent->isLoading()) {
connect(qmlComponent, &QQmlComponent::statusChanged, this,
- [this, qmlComponent, targetContext, f](QQmlComponent::Status) {
- finishQmlLoad(qmlComponent, targetContext, f);
+ [this, qmlComponent, targetContext, onQmlLoadedCallback](QQmlComponent::Status) {
+ finishQmlLoad(qmlComponent, targetContext, onQmlLoadedCallback);
});
return;
}
- finishQmlLoad(qmlComponent, targetContext, f);
+ finishQmlLoad(qmlComponent, targetContext, onQmlLoadedCallback);
}
-void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function f) {
- load(qmlSource, true, f);
+void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function onQmlLoadedCallback) {
+ load(qmlSource, true, onQmlLoadedCallback);
}
-void OffscreenQmlSurface::load(const QUrl& qmlSource, std::function f) {
- load(qmlSource, false, f);
+void OffscreenQmlSurface::load(const QUrl& qmlSource, std::function onQmlLoadedCallback) {
+ load(qmlSource, false, onQmlLoadedCallback);
}
void OffscreenQmlSurface::clearCache() {
_qmlContext->engine()->clearComponentCache();
}
-void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function f) {
+void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function onQmlLoadedCallback) {
disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0);
if (qmlComponent->isError()) {
for (const auto& error : qmlComponent->errors()) {
@@ -690,7 +691,6 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
return;
}
-
QObject* newObject = qmlComponent->beginCreate(qmlContext);
if (qmlComponent->isError()) {
for (const auto& error : qmlComponent->errors()) {
@@ -704,7 +704,20 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
}
qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
- f(qmlContext, newObject);
+
+ // All quick items should be focusable
+ QQuickItem* newItem = qobject_cast(newObject);
+ if (newItem) {
+ // Make sure we make items focusable (critical for
+ // supporting keyboard shortcuts)
+ newItem->setFlag(QQuickItem::ItemIsFocusScope, true);
+ }
+
+ // Make sure we will call callback for this codepath
+ // Call this before qmlComponent->completeCreate() otherwise ghost window appears
+ if (newItem && _rootItem) {
+ onQmlLoadedCallback(qmlContext, newObject);
+ }
QObject* eventBridge = qmlContext->contextProperty("eventBridge").value();
if (qmlContext != _qmlContext && eventBridge && eventBridge != this) {
@@ -716,15 +729,6 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
qmlComponent->completeCreate();
qmlComponent->deleteLater();
-
- // All quick items should be focusable
- QQuickItem* newItem = qobject_cast(newObject);
- if (newItem) {
- // Make sure we make items focusable (critical for
- // supporting keyboard shortcuts)
- newItem->setFlag(QQuickItem::ItemIsFocusScope, true);
- }
-
// If we already have a root, just set a couple of flags and the ancestry
if (newItem && _rootItem) {
// Allow child windows to be destroyed from JS
@@ -747,6 +751,8 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
_rootItem = newItem;
_rootItem->setParentItem(_quickWindow->contentItem());
_rootItem->setSize(_quickWindow->renderTargetSize());
+ // Call this callback after rootitem is set, otherwise VrMenu wont work
+ onQmlLoadedCallback(qmlContext, newObject);
}
void OffscreenQmlSurface::updateQuick() {
diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h
index 7da929723c..95dabdef0f 100644
--- a/libraries/ui/src/ui/OffscreenQmlSurface.h
+++ b/libraries/ui/src/ui/OffscreenQmlSurface.h
@@ -50,11 +50,11 @@ public:
void resize(const QSize& size, bool forceResize = false);
QSize size() const;
- Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, std::function f = [](QQmlContext*, QObject*) {});
- Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {});
- Q_INVOKABLE void load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {});
- Q_INVOKABLE void load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) {
- return load(QUrl(qmlSourceFile), f);
+ Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {});
+ Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {});
+ Q_INVOKABLE void load(const QUrl& qmlSource, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {});
+ Q_INVOKABLE void load(const QString& qmlSourceFile, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {}) {
+ return load(QUrl(qmlSourceFile), onQmlLoadedCallback);
}
void clearCache();
void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; }
@@ -120,7 +120,7 @@ protected:
private:
static QOpenGLContext* getSharedContext();
- void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function f);
+ void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function onQmlLoadedCallback);
QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
void setupFbo();
bool allowNewFrame(uint8_t fps);
diff --git a/plugins/hifiSdl2/src/SDL2Manager.cpp b/plugins/hifiSdl2/src/SDL2Manager.cpp
index 021cb4dfec..a5376af24e 100644
--- a/plugins/hifiSdl2/src/SDL2Manager.cpp
+++ b/plugins/hifiSdl2/src/SDL2Manager.cpp
@@ -54,49 +54,6 @@ SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) {
}
void SDL2Manager::init() {
- loadSettings();
-
- auto preferences = DependencyManager::get();
- static const QString SDL2_PLUGIN { "Game Controller" };
- {
- auto getter = [this]()->bool { return _isEnabled; };
- auto setter = [this](bool value) {
- _isEnabled = value;
- saveSettings();
- };
- auto preference = new CheckPreference(SDL2_PLUGIN, "Enabled", getter, setter);
- preferences->addPreference(preference);
- }
-
- bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == 0);
-
- if (initSuccess) {
- int joystickCount = SDL_NumJoysticks();
-
- for (int i = 0; i < joystickCount; i++) {
- SDL_GameController* controller = SDL_GameControllerOpen(i);
-
- if (controller) {
- SDL_JoystickID id = getInstanceId(controller);
- if (!_openJoysticks.contains(id)) {
- //Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
- Joystick::Pointer joystick = std::make_shared(id, controller);
- _openJoysticks[id] = joystick;
- auto userInputMapper = DependencyManager::get();
- userInputMapper->registerDevice(joystick);
- auto name = SDL_GameControllerName(controller);
- _subdeviceNames << name;
- emit joystickAdded(joystick.get());
- emit subdeviceConnected(getName(), name);
- }
- }
- }
-
- _isInitialized = true;
- }
- else {
- qDebug() << "Error initializing SDL2 Manager";
- }
}
QStringList SDL2Manager::getSubdeviceNames() {
@@ -110,8 +67,61 @@ void SDL2Manager::deinit() {
}
bool SDL2Manager::activate() {
+
+ // FIXME for some reason calling this code in the `init` function triggers a crash
+ // on OSX in PR builds, but not on my local debug build. Attempting a workaround by
+ //
+ static std::once_flag once;
+ std::call_once(once, [&]{
+ loadSettings();
+
+ auto preferences = DependencyManager::get();
+ static const QString SDL2_PLUGIN { "Game Controller" };
+ {
+ auto getter = [this]()->bool { return _isEnabled; };
+ auto setter = [this](bool value) {
+ _isEnabled = value;
+ saveSettings();
+ };
+ auto preference = new CheckPreference(SDL2_PLUGIN, "Enabled", getter, setter);
+ preferences->addPreference(preference);
+ }
+
+ bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == 0);
+
+ if (initSuccess) {
+ int joystickCount = SDL_NumJoysticks();
+
+ for (int i = 0; i < joystickCount; i++) {
+ SDL_GameController* controller = SDL_GameControllerOpen(i);
+
+ if (controller) {
+ SDL_JoystickID id = getInstanceId(controller);
+ if (!_openJoysticks.contains(id)) {
+ //Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
+ Joystick::Pointer joystick = std::make_shared(id, controller);
+ _openJoysticks[id] = joystick;
+ auto userInputMapper = DependencyManager::get();
+ userInputMapper->registerDevice(joystick);
+ auto name = SDL_GameControllerName(controller);
+ _subdeviceNames << name;
+ emit joystickAdded(joystick.get());
+ emit subdeviceConnected(getName(), name);
+ }
+ }
+ }
+
+ _isInitialized = true;
+ } else {
+ qDebug() << "Error initializing SDL2 Manager";
+ }
+ });
+
+ if (!_isInitialized) {
+ return false;
+ }
+
InputPlugin::activate();
-
auto userInputMapper = DependencyManager::get();
for (auto joystick : _openJoysticks) {
userInputMapper->registerDevice(joystick);