diff --git a/examples/edit.js b/examples/edit.js index e3bea2e954..c58bdb8110 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -201,7 +201,7 @@ var toolBar = (function() { }, true, false); newModelButton = toolBar.addTool({ - imageURL: toolIconUrl + "upload-01.svg", + imageURL: toolIconUrl + "model-01.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, diff --git a/examples/tests/playaPerformanceTest.qml b/examples/tests/playaPerformanceTest.qml index f1382358ae..6eb0061598 100644 --- a/examples/tests/playaPerformanceTest.qml +++ b/examples/tests/playaPerformanceTest.qml @@ -1,5 +1,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import Qt.labs.settings 1.0 Rectangle { id: root @@ -8,67 +9,68 @@ Rectangle { signal sendToScript(var message); property var values: []; - property var host: AddressManager.hostname + property alias destination: addressLine.text + readonly property string nullDestination: "169.254.0.1" + property bool running: false - - Component.onCompleted: { - Window.domainChanged.connect(function(newDomain){ - if (newDomain !== root.host) { - root.host = AddressManager.hostname; - } - }); + function statusReport() { + console.log("PERF status connected: " + AddressManager.isConnected); } - - onHostChanged: { - if (root.running) { - if (host !== "Dreaming" && host !== "Playa") { + + Timer { + id: readyStateTimer + interval: 500 + repeat: true + running: false + onTriggered: { + if (!root.running) { + stop(); return; } - console.log("PERF new domain " + host) - if (host === "Dreaming") { - AddressManager.handleLookupString("Playa"); + if (AddressManager.isConnected) { + console.log("PERF already connected, disconnecting"); + AddressManager.handleLookupString(root.nullDestination); return; } + + stop(); + console.log("PERF disconnected, moving to target " + root.destination); + AddressManager.handleLookupString(root.destination); - if (host === "Playa") { - console.log("PERF starting timers and frame timing"); - // If we've arrived, start running the test - FrameTimings.start(); - rotationTimer.start(); - stopTimer.start(); - } + // If we've arrived, start running the test + console.log("PERF starting timers and frame timing"); + FrameTimings.start(); + rotationTimer.start(); + stopTimer.start(); } } + function startTest() { console.log("PERF startTest()"); - root.running = true - console.log("PERF current host: " + AddressManager.hostname) - // If we're already in playa, we need to go somewhere else... - if ("Playa" === AddressManager.hostname) { - console.log("PERF Navigating to dreaming") - AddressManager.handleLookupString("Dreaming/0,0,0"); - } else { - console.log("PERF Navigating to playa") - AddressManager.handleLookupString("Playa"); + if (!root.running) { + root.running = true + readyStateTimer.start(); } } function stopTest() { console.log("PERF stopTest()"); - root.running = false; - stopTimer.stop(); - rotationTimer.stop(); - FrameTimings.finish(); - root.values = FrameTimings.getValues(); - AddressManager.handleLookupString("Dreaming/0,0,0"); - resultGraph.requestPaint(); - console.log("PERF Value Count: " + root.values.length); - console.log("PERF Max: " + FrameTimings.max); - console.log("PERF Min: " + FrameTimings.min); - console.log("PERF Avg: " + FrameTimings.mean); - console.log("PERF StdDev: " + FrameTimings.standardDeviation); + if (root.running) { + root.running = false; + stopTimer.stop(); + rotationTimer.stop(); + FrameTimings.finish(); + root.values = FrameTimings.getValues(); + AddressManager.handleLookupString(root.nullDestination); + resultGraph.requestPaint(); + console.log("PERF Value Count: " + root.values.length); + console.log("PERF Max: " + FrameTimings.max); + console.log("PERF Min: " + FrameTimings.min); + console.log("PERF Avg: " + FrameTimings.mean); + console.log("PERF StdDev: " + FrameTimings.standardDeviation); + } } function yaw(a) { @@ -82,7 +84,6 @@ Rectangle { MyAvatar.setOrientationVar(yaw(Date.now() / 1000)); } - property bool running: false Timer { id: stopTimer @@ -102,14 +103,43 @@ Rectangle { Row { id: row - anchors { left: parent.left; right: parent.right; } + anchors { left: parent.left; right: parent.right; top: parent.top; margins: 16 } spacing: 8 Button { text: root.running ? "Stop" : "Run" onClicked: root.running ? stopTest() : startTest(); } + Button { + text: "Disconnect" + onClicked: AddressManager.handleLookupString(root.nullDestination); + } + Button { + text: "Connect" + onClicked: AddressManager.handleLookupString(root.destination); + } + Button { + text: "Status" + onClicked: statusReport(); + } } + TextField { + id: addressLine + focus: true + anchors { + left: parent.left; right: parent.right; + top: row.bottom; margins: 16; + } + text: "Playa" + onTextChanged: console.log("PERF new target " + text); + } + + Settings { + category: "Qml.Performance.RenderTest" + property alias destination: addressLine.text + } + + // Rectangle { // anchors { left: parent.left; right: parent.right; top: row.bottom; topMargin: 8; bottom: parent.bottom; } // //anchors.fill: parent @@ -130,7 +160,7 @@ Rectangle { Canvas { id: resultGraph - anchors { left: parent.left; right: parent.right; top: row.bottom; margins: 16; bottom: parent.bottom; } + anchors { left: parent.left; right: parent.right; top: addressLine.bottom; margins: 16; bottom: parent.bottom; } property real maxValue: 200; property real perFrame: 10000; property real k1: (5 / maxValue) * height; diff --git a/examples/utilities/render/rates.qml b/examples/utilities/render/rates.qml new file mode 100644 index 0000000000..f4a4ee2c6c --- /dev/null +++ b/examples/utilities/render/rates.qml @@ -0,0 +1,49 @@ +// +// stats.qml +// examples/utilities/cache +// +// Created by Zach Pomerantz on 4/1/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "../lib/plotperf" + +Item { + id: root + anchors.fill: parent + + property var caches: [ ["Present", "present"], ["Present", "present"], ["New", "newFrame"], ["Dropped", "dropped"], ["Simulation", "simulation"], ["Avatar", "avatar"] ] + property var colors: [ "#1AC567", "#00B4EF" ] + + Grid { + id: grid + rows: (root.caches.length / 2); columns: 2; spacing: 8 + anchors.fill: parent + + Repeater { + id: repeater + + model: root.caches + + Row { + PlotPerf { + title: modelData[0] + " Rate" + height: (grid.height - (grid.spacing * ((root.caches.length / 2) + 1))) / (root.caches.length / 2) + width: grid.width / 2 - grid.spacing * 1.5 + object: Rates + valueScale: 1 + valueUnit: "fps" + valueNumDigits: "2" + plots: [{ + prop: modelData[1], + color: root.colors[index % 2] + }] + } + } + } + } +} diff --git a/examples/utilities/render/renderRates.js b/examples/utilities/render/renderRates.js new file mode 100644 index 0000000000..1d15f4041c --- /dev/null +++ b/examples/utilities/render/renderRates.js @@ -0,0 +1,21 @@ +// +// cacheStats.js +// examples/utilities/cache +// +// Zach Pomerantz, created on 4/1/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// Set up the qml ui +var qml = Script.resolvePath('rates.qml'); +var window = new OverlayWindow({ + title: 'Render Rates', + source: qml, + width: 300, + height: 200 +}); +window.setPosition(500, 50); +window.closed.connect(function() { Script.stop(); }); diff --git a/interface/resources/qml/controls-uit/TextAction.qml b/interface/resources/qml/controls-uit/TextAction.qml new file mode 100644 index 0000000000..1c66c3630a --- /dev/null +++ b/interface/resources/qml/controls-uit/TextAction.qml @@ -0,0 +1,67 @@ +// +// TextField.qml +// +// Created by David Rowe on 21 Apr 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import "../styles-uit" +import "../controls-uit" as HifiControls + +Item { + property string icon: "" + property int iconSize: 30 + property string text: "" + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + + signal clicked() + + height: Math.max(glyph.visible ? glyph.height - 4 : 0, string.visible ? string.height : 0) + width: glyph.width + string.anchors.leftMargin + string.width + + HiFiGlyphs { + id: glyph + anchors.left: parent.left + anchors.top: parent.top + anchors.topMargin: -2 + text: parent.icon + size: parent.iconSize + color: isLightColorScheme + ? (mouseArea.containsMouse ? hifi.colors.baseGrayHighlight : hifi.colors.lightGray) + : (mouseArea.containsMouse ? hifi.colors.faintGray : hifi.colors.lightGrayText) + visible: text !== "" + width: visible ? implicitWidth : 0 + } + + RalewaySemiBold { + id: string + anchors { + left: glyph.visible ? glyph.right : parent.left + leftMargin: visible && glyph.visible ? hifi.dimensions.contentSpacing.x : 0 + verticalCenter: glyph.visible ? glyph.verticalCenter : undefined + } + text: parent.text + size: hifi.fontSizes.inputLabel + color: isLightColorScheme + ? (mouseArea.containsMouse ? hifi.colors.baseGrayHighlight : hifi.colors.lightGray) + : (mouseArea.containsMouse ? hifi.colors.faintGray : hifi.colors.lightGrayText) + font.underline: true; + visible: text !== "" + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: parent.clicked() + } +} diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 215710cc81..cd3b2fdf20 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -24,7 +24,7 @@ Window { resizable: true destroyOnInvisible: true x: 40; y: 40 - implicitWidth: 400; implicitHeight: 695 + implicitWidth: 400; implicitHeight: 728 minSize: Qt.vector2d(200, 300) HifiConstants { id: hifi } @@ -236,7 +236,23 @@ Window { } } - HifiControls.VerticalSpacer { } + HifiControls.VerticalSpacer { + height: hifi.dimensions.controlInterlineHeight - 3 + } + + HifiControls.TextAction { + id: directoryButton + icon: hifi.glyphs.script + iconSize: 24 + text: "Reveal Scripts Folder" + onClicked: fileDialogHelper.openScriptsDirectory() + colorScheme: hifi.colorSchemes.dark + anchors.left: parent.left + } + + HifiControls.VerticalSpacer { + height: hifi.dimensions.controlInterlineHeight - 3 + } } } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4e01fe85c3..4071043c87 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -131,6 +131,7 @@ #include "scripting/WebWindowClass.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h" +#include "scripting/RatesScriptingInterface.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif @@ -1384,6 +1385,7 @@ void Application::initializeUi() { rootContext->setContextProperty("Preferences", DependencyManager::get().data()); rootContext->setContextProperty("AddressManager", DependencyManager::get().data()); rootContext->setContextProperty("FrameTimings", &_frameTimingsScriptingInterface); + rootContext->setContextProperty("Rates", new RatesScriptingInterface(this)); rootContext->setContextProperty("TREE_SCALE", TREE_SCALE); rootContext->setContextProperty("Quat", new Quat()); @@ -4439,6 +4441,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri // AvatarManager has some custom types AvatarManager::registerMetaTypes(scriptEngine); + scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this)); + // hook our avatar and avatar hash map object into this script engine scriptEngine->registerGlobalObject("MyAvatar", getMyAvatar()); qScriptRegisterMetaType(scriptEngine, audioListenModeToScriptValue, audioListenModeFromScriptValue); diff --git a/interface/src/scripting/RatesScriptingInterface.h b/interface/src/scripting/RatesScriptingInterface.h new file mode 100644 index 0000000000..7bcab0ea70 --- /dev/null +++ b/interface/src/scripting/RatesScriptingInterface.h @@ -0,0 +1,37 @@ +// +// RatesScriptingInterface.h +// interface/src/scripting +// +// Created by Zach Pomerantz on 4/20/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef HIFI_RATES_SCRIPTING_INTERFACE_H +#define HIFI_RATES_SCRIPTING_INTERFACE_H + +#include + +class RatesScriptingInterface : public QObject { + Q_OBJECT + + Q_PROPERTY(float render READ getRenderRate) + Q_PROPERTY(float present READ getPresentRate) + Q_PROPERTY(float newFrame READ getNewFrameRate) + Q_PROPERTY(float dropped READ getDropRate) + Q_PROPERTY(float simulation READ getSimulationRate) + Q_PROPERTY(float avatar READ getAvatarRate) + +public: + RatesScriptingInterface(QObject* parent) : QObject(parent) {} + float getRenderRate() { return qApp->getFps(); } + float getPresentRate() { return qApp->getActiveDisplayPlugin()->presentRate(); } + float getNewFrameRate() { return qApp->getActiveDisplayPlugin()->newFramePresentRate(); } + float getDropRate() { return qApp->getActiveDisplayPlugin()->droppedFrameRate(); } + float getSimulationRate() { return qApp->getAverageSimsPerSecond(); } + float getAvatarRate() { return qApp->getAvatarSimrate(); } +}; + +#endif // HIFI_INTERFACE_RATES_SCRIPTING_INTERFACE_H diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index a502e608e2..9364045857 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -64,9 +64,9 @@ void AnimationReader::run() { if (urlValid) { // Parse the FBX directly from the QNetworkReply - FBXGeometry* fbxgeo = nullptr; + FBXGeometry::Pointer fbxgeo; if (_url.path().toLower().endsWith(".fbx")) { - fbxgeo = readFBX(_data, QVariantHash(), _url.path()); + fbxgeo.reset(readFBX(_data, QVariantHash(), _url.path())); } else { QString errorStr("usupported format"); emit onError(299, errorStr); @@ -117,16 +117,16 @@ const QVector& Animation::getFramesReference() const { void Animation::downloadFinished(const QByteArray& data) { // parse the animation/fbx file on a background thread. AnimationReader* animationReader = new AnimationReader(_url, data); - connect(animationReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(animationParseSuccess(FBXGeometry*))); + connect(animationReader, SIGNAL(onSuccess(FBXGeometry::Pointer)), SLOT(animationParseSuccess(FBXGeometry::Pointer))); connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString))); QThreadPool::globalInstance()->start(animationReader); } -void Animation::animationParseSuccess(FBXGeometry* geometry) { +void Animation::animationParseSuccess(FBXGeometry::Pointer geometry) { qCDebug(animation) << "Animation parse success" << _url.toDisplayString(); - _geometry.reset(geometry); + _geometry = geometry; finishedLoading(true); } diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index e6a795c864..59a4ad0498 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -68,12 +68,12 @@ protected: virtual void downloadFinished(const QByteArray& data) override; protected slots: - void animationParseSuccess(FBXGeometry* geometry); + void animationParseSuccess(FBXGeometry::Pointer geometry); void animationParseError(int error, QString str); private: - std::unique_ptr _geometry; + FBXGeometry::Pointer _geometry; }; /// Reads geometry in a worker thread. @@ -85,7 +85,7 @@ public: virtual void run(); signals: - void onSuccess(FBXGeometry* geometry); + void onSuccess(FBXGeometry::Pointer geometry); void onError(int error, QString str); private: diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 65acedfc96..2df388e1d4 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -39,6 +39,8 @@ using namespace std; +static int FBXGeometryPointerMetaTypeId = qRegisterMetaType(); + QStringList FBXGeometry::getJointNames() const { QStringList names; foreach (const FBXJoint& joint, joints) { diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index a1fc30d1f4..c1952fc550 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -270,6 +270,7 @@ inline bool operator!=(const SittingPoint& lhs, const SittingPoint& rhs) /// A set of meshes extracted from an FBX document. class FBXGeometry { public: + using Pointer = std::shared_ptr; QString author; QString applicationName; ///< the name of the application that generated the model @@ -330,6 +331,7 @@ public: }; Q_DECLARE_METATYPE(FBXGeometry) +Q_DECLARE_METATYPE(FBXGeometry::Pointer) /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 822c1392df..2a19dc926b 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -17,6 +17,7 @@ using namespace gpu; +static int TexturePointerMetaTypeId = qRegisterMetaType(); std::atomic Texture::_textureCPUCount{ 0 }; std::atomic Texture::_textureCPUMemoryUsage{ 0 }; @@ -857,8 +858,8 @@ void TextureSource::reset(const QUrl& url) { _imageUrl = url; } -void TextureSource::resetTexture(gpu::Texture* texture) { - _gpuTexture.reset(texture); +void TextureSource::resetTexture(gpu::TexturePointer texture) { + _gpuTexture = texture; } bool TextureSource::isDefined() const { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 4715170cef..69d50617bf 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -16,6 +16,7 @@ #include //min max and more #include +#include #include namespace gpu { @@ -469,7 +470,6 @@ protected: typedef std::shared_ptr TexturePointer; typedef std::vector< TexturePointer > Textures; - // TODO: For now TextureView works with Texture as a place holder for the Texture. // The overall logic should be about the same except that the Texture will be a real GL Texture under the hood class TextureView { @@ -526,7 +526,7 @@ public: void reset(const QUrl& url); - void resetTexture(gpu::Texture* texture); + void resetTexture(gpu::TexturePointer texture); bool isDefined() const; @@ -538,5 +538,6 @@ typedef std::shared_ptr< TextureSource > TextureSourcePointer; }; +Q_DECLARE_METATYPE(gpu::TexturePointer) #endif diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index a565b249d8..6dd1d97d7f 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -143,40 +143,38 @@ void GeometryReader::run() { QString urlname = _url.path().toLower(); if (!urlname.isEmpty() && !_url.path().isEmpty() && (_url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"))) { - FBXGeometry* fbxGeometry = nullptr; + FBXGeometry::Pointer fbxGeometry; if (_url.path().toLower().endsWith(".fbx")) { - fbxGeometry = readFBX(_data, _mapping, _url.path()); + fbxGeometry.reset(readFBX(_data, _mapping, _url.path())); if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) { throw QString("empty geometry, possibly due to an unsupported FBX version"); } } else if (_url.path().toLower().endsWith(".obj")) { - fbxGeometry = OBJReader().readOBJ(_data, _mapping, _url); + fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _url)); } else { throw QString("unsupported format"); } - // Ensure the resource has not been deleted, and won't be while invokeMethod is in flight. + // Ensure the resource has not been deleted auto resource = _resource.toStrongRef(); if (!resource) { qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - delete fbxGeometry; } else { - QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", Qt::BlockingQueuedConnection, Q_ARG(void*, fbxGeometry)); + QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", + Q_ARG(FBXGeometry::Pointer, fbxGeometry)); } } else { throw QString("url is invalid"); } } catch (const QString& error) { - qCDebug(modelnetworking) << "Error reading " << _url << ": " << error; + qCDebug(modelnetworking) << "Error parsing model for" << _url << ":" << error; auto resource = _resource.toStrongRef(); - // Ensure the resoruce has not been deleted, and won't be while invokeMethod is in flight. - if (!resource) { - qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - } else { - QMetaObject::invokeMethod(resource.data(), "finishedLoading", Qt::BlockingQueuedConnection, Q_ARG(bool, false)); + if (resource) { + QMetaObject::invokeMethod(resource.data(), "finishedLoading", + Q_ARG(bool, false)); } } } @@ -190,7 +188,7 @@ public: virtual void downloadFinished(const QByteArray& data) override; protected: - Q_INVOKABLE void setGeometryDefinition(void* fbxGeometry); + Q_INVOKABLE void setGeometryDefinition(FBXGeometry::Pointer fbxGeometry); private: QVariantHash _mapping; @@ -200,9 +198,9 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data)); } -void GeometryDefinitionResource::setGeometryDefinition(void* fbxGeometry) { +void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxGeometry) { // Assume ownership of the geometry pointer - _geometry.reset(static_cast(fbxGeometry)); + _geometry = fbxGeometry; // Copy materials QHash materialIDAtlas; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 0e93119fe3..7d18151f2c 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -330,7 +330,7 @@ void ImageReader::run() { return; } - gpu::Texture* texture = nullptr; + gpu::TexturePointer texture = nullptr; { // Double-check the resource still exists between long operations. auto resource = _resource.toStrongRef(); @@ -342,36 +342,32 @@ void ImageReader::run() { auto url = _url.toString().toStdString(); PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr); - texture = resource.dynamicCast()->getTextureLoader()(image, url); + texture.reset(resource.dynamicCast()->getTextureLoader()(image, url)); } - // Ensure the resource has not been deleted, and won't be while invokeMethod is in flight. + // Ensure the resource has not been deleted auto resource = _resource.toStrongRef(); if (!resource) { qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; - delete texture; } else { - QMetaObject::invokeMethod(resource.data(), "setImage", Qt::BlockingQueuedConnection, - Q_ARG(void*, texture), + QMetaObject::invokeMethod(resource.data(), "setImage", + Q_ARG(gpu::TexturePointer, texture), Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); } } -void NetworkTexture::setImage(void* voidTexture, int originalWidth, +void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight) { _originalWidth = originalWidth; _originalHeight = originalHeight; - gpu::Texture* texture = static_cast(voidTexture); - // Passing ownership _textureSource->resetTexture(texture); - auto gpuTexture = _textureSource->getGPUTexture(); - if (gpuTexture) { - _width = gpuTexture->getWidth(); - _height = gpuTexture->getHeight(); - setSize(gpuTexture->getStoredSize()); + if (texture) { + _width = texture->getWidth(); + _height = texture->getHeight(); + setSize(texture->getStoredSize()); } else { // FIXME: If !gpuTexture, we failed to load! _width = _height = 0; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index a392117958..c614a7ceb3 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -128,7 +128,6 @@ public: signals: void networkTextureCreated(const QWeakPointer& self); - protected: virtual bool isCacheable() const override { return _loaded; } @@ -136,9 +135,7 @@ protected: virtual void downloadFinished(const QByteArray& data) override; Q_INVOKABLE void loadContent(const QByteArray& content); - // FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on... - Q_INVOKABLE void setImage(void* texture, int originalWidth, int originalHeight); - + Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); private: TextureType _type; diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 0d640b4e80..8fb0001b57 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -32,7 +32,6 @@ MessageID AssetClient::_currentID = 0; AssetClient::AssetClient() { - setCustomDeleter([](Dependency* dependency){ static_cast(dependency)->deleteLater(); }); @@ -502,7 +501,18 @@ bool AssetClient::cancelGetAssetInfoRequest(MessageID id) { bool AssetClient::cancelGetAssetRequest(MessageID id) { // Search through each pending mapping request for id `id` for (auto& kv : _pendingRequests) { - if (kv.second.erase(id)) { + auto& messageCallbackMap = kv.second; + auto requestIt = messageCallbackMap.find(id); + if (requestIt != kv.second.end()) { + + auto& message = requestIt->second.message; + if (message) { + // disconnect from all signals emitting from the pending message + disconnect(message.data(), nullptr, this, nullptr); + } + + messageCallbackMap.erase(requestIt); + return true; } } diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 68d9a61226..b6f482dbe7 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -606,8 +606,9 @@ void Resource::handleReplyFinished() { const int BASE_DELAY_MS = 1000; if (_attempts++ < MAX_ATTEMPTS) { auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts); - qCDebug(networking).nospace() << "Retrying to load the asset in " << waitTime - << "ms, attempt " << _attempts << " of " << MAX_ATTEMPTS; + qCDebug(networking) << "Server unavailable for" << _url << + "retrying in " << waitTime << "ms," << + "attempt " << _attempts + 1 << "of" << MAX_ATTEMPTS; QTimer::singleShot(waitTime, this, &Resource::attemptRequest); break; } diff --git a/libraries/render-utils/src/starsGrid.slf b/libraries/render-utils/src/starsGrid.slf index c52ccb54d9..ad9a45a4f5 100644 --- a/libraries/render-utils/src/starsGrid.slf +++ b/libraries/render-utils/src/starsGrid.slf @@ -1,6 +1,5 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> -#line __LINE__ // Generated on <$_SCRIBE_DATE$> // stars.frag // fragment shader diff --git a/libraries/shared/src/shared/RateCounter.h b/libraries/shared/src/shared/RateCounter.h index e24b1e6eca..d04d87493a 100644 --- a/libraries/shared/src/shared/RateCounter.h +++ b/libraries/shared/src/shared/RateCounter.h @@ -11,6 +11,7 @@ #define hifi_Shared_RateCounter_h #include +#include #include #include @@ -20,6 +21,8 @@ template class RateCounter { public: + RateCounter() { _rate = 0; } // avoid use of std::atomic copy ctor + void increment(size_t count = 1) { auto now = usecTimestampNow(); float currentIntervalMs = (now - _start) / (float) USECS_PER_MSEC; @@ -42,8 +45,8 @@ public: private: uint64_t _start { usecTimestampNow() }; size_t _count { 0 }; - float _rate { 0 }; const float _scale { powf(10, PRECISION) }; + std::atomic _rate; }; #endif diff --git a/libraries/ui/src/FileDialogHelper.cpp b/libraries/ui/src/FileDialogHelper.cpp index 82ad877573..9e22d7e441 100644 --- a/libraries/ui/src/FileDialogHelper.cpp +++ b/libraries/ui/src/FileDialogHelper.cpp @@ -14,6 +14,9 @@ #include #include #include +#include + +#include "PathUtils.h" QUrl FileDialogHelper::home() { @@ -103,3 +106,7 @@ QStringList FileDialogHelper::drives() { } return result; } + +void FileDialogHelper::openScriptsDirectory() { + QDesktopServices::openUrl(defaultScriptsLocation()); +} diff --git a/libraries/ui/src/FileDialogHelper.h b/libraries/ui/src/FileDialogHelper.h index 2119b77917..55bc922839 100644 --- a/libraries/ui/src/FileDialogHelper.h +++ b/libraries/ui/src/FileDialogHelper.h @@ -58,6 +58,8 @@ public: Q_INVOKABLE bool validFolder(const QString& path); Q_INVOKABLE QUrl pathToUrl(const QString& path); Q_INVOKABLE QUrl saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters); + + Q_INVOKABLE void openScriptsDirectory(); }; diff --git a/libraries/ui/src/MainWindow.cpp b/libraries/ui/src/MainWindow.cpp index ce23bd02a2..bc67a726bd 100644 --- a/libraries/ui/src/MainWindow.cpp +++ b/libraries/ui/src/MainWindow.cpp @@ -64,6 +64,15 @@ void MainWindow::saveGeometry() { } void MainWindow::closeEvent(QCloseEvent* event) { + // It is the job of Application::quit() to shut things down properly when it is finished with its event loop. + // But if we don't explicitly ignore this event now, the window and application event loop will close + // before we've had a chance to act on the aboutToClose signal. This will leaves a zombie process on all platforms. + // To repro: + // Open a QML modal dialog (e.g., select an avatar to wear), but don't dismiss it. + // Close the application with the operating system window close button (not the menu Quit) + // With ignore: App will wait until you accept or dismiss the dialog and log "Normal exit". + // Without ignore: App will close immediately, and nothing will log about quitting or exit. + event->ignore(); qApp->quit(); } diff --git a/server-console/src/modules/path-finder.js b/server-console/src/modules/path-finder.js index 1898db91ac..d0e4639e4a 100644 --- a/server-console/src/modules/path-finder.js +++ b/server-console/src/modules/path-finder.js @@ -1,21 +1,22 @@ var fs = require('fs'); var path = require('path'); -exports.searchPaths = function(name, binaryType, releaseType) { - function platformExtension(name) { - if (name == "Interface" || name == "High Fidelity") { - if (process.platform == "darwin") { - return ".app/Contents/MacOS/" + name - } else if (process.platform == "win32") { - return ".exe" - } else { - return "" - } +function platformExtension(name) { + if (name == "Interface") { + if (process.platform == "darwin") { + return ".app/Contents/MacOS/" + name + } else if (process.platform == "win32") { + return ".exe" } else { - return process.platform == "win32" ? ".exe" : "" + return "" } + } else { + return process.platform == "win32" ? ".exe" : "" } +} + +exports.searchPaths = function(name, binaryType, releaseType) { var extension = platformExtension(name); var devBasePath = "../build/" + name + "/"; @@ -68,6 +69,7 @@ exports.discoveredPath = function (name, binaryType, releaseType) { try { var stats = fs.lstatSync(testPath); + var extension = platformExtension(name); if (stats.isFile() || (stats.isDirectory() && extension == ".app")) { console.log("Found " + name + " at " + testPath); @@ -81,11 +83,6 @@ exports.discoveredPath = function (name, binaryType, releaseType) { return null; } - // for a released server console on OS X, assume the name of the interface executable is "High Fidelity" - if (releaseType && process.platform == "darwin" && name == "Interface") { - name = "High Fidelity"; - } - // attempt to find a binary at the usual paths, return null if it doesn't exist return binaryFromPaths(name, this.searchPaths(name, binaryType, releaseType)); }