Merge branch 'master' of https://github.com/highfidelity/hifi into supportLargeClockSkew

This commit is contained in:
Brad Hefta-Gaub 2016-04-21 16:26:44 -07:00
commit 72c81e2378
25 changed files with 365 additions and 116 deletions

View file

@ -201,7 +201,7 @@ var toolBar = (function() {
}, true, false); }, true, false);
newModelButton = toolBar.addTool({ newModelButton = toolBar.addTool({
imageURL: toolIconUrl + "upload-01.svg", imageURL: toolIconUrl + "model-01.svg",
subImage: { subImage: {
x: 0, x: 0,
y: Tool.IMAGE_WIDTH, y: Tool.IMAGE_WIDTH,

View file

@ -1,5 +1,6 @@
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import Qt.labs.settings 1.0
Rectangle { Rectangle {
id: root id: root
@ -8,61 +9,61 @@ Rectangle {
signal sendToScript(var message); signal sendToScript(var message);
property var values: []; 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
function statusReport() {
Component.onCompleted: { console.log("PERF status connected: " + AddressManager.isConnected);
Window.domainChanged.connect(function(newDomain){
if (newDomain !== root.host) {
root.host = AddressManager.hostname;
}
});
} }
onHostChanged: { Timer {
if (root.running) { id: readyStateTimer
if (host !== "Dreaming" && host !== "Playa") { interval: 500
repeat: true
running: false
onTriggered: {
if (!root.running) {
stop();
return; return;
} }
console.log("PERF new domain " + host) if (AddressManager.isConnected) {
if (host === "Dreaming") { console.log("PERF already connected, disconnecting");
AddressManager.handleLookupString("Playa"); AddressManager.handleLookupString(root.nullDestination);
return; return;
} }
if (host === "Playa") { stop();
console.log("PERF starting timers and frame timing"); console.log("PERF disconnected, moving to target " + root.destination);
AddressManager.handleLookupString(root.destination);
// If we've arrived, start running the test // If we've arrived, start running the test
console.log("PERF starting timers and frame timing");
FrameTimings.start(); FrameTimings.start();
rotationTimer.start(); rotationTimer.start();
stopTimer.start(); stopTimer.start();
} }
} }
}
function startTest() { function startTest() {
console.log("PERF startTest()"); console.log("PERF startTest()");
if (!root.running) {
root.running = true root.running = true
console.log("PERF current host: " + AddressManager.hostname) readyStateTimer.start();
// 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");
} }
} }
function stopTest() { function stopTest() {
console.log("PERF stopTest()"); console.log("PERF stopTest()");
if (root.running) {
root.running = false; root.running = false;
stopTimer.stop(); stopTimer.stop();
rotationTimer.stop(); rotationTimer.stop();
FrameTimings.finish(); FrameTimings.finish();
root.values = FrameTimings.getValues(); root.values = FrameTimings.getValues();
AddressManager.handleLookupString("Dreaming/0,0,0"); AddressManager.handleLookupString(root.nullDestination);
resultGraph.requestPaint(); resultGraph.requestPaint();
console.log("PERF Value Count: " + root.values.length); console.log("PERF Value Count: " + root.values.length);
console.log("PERF Max: " + FrameTimings.max); console.log("PERF Max: " + FrameTimings.max);
@ -70,6 +71,7 @@ Rectangle {
console.log("PERF Avg: " + FrameTimings.mean); console.log("PERF Avg: " + FrameTimings.mean);
console.log("PERF StdDev: " + FrameTimings.standardDeviation); console.log("PERF StdDev: " + FrameTimings.standardDeviation);
} }
}
function yaw(a) { function yaw(a) {
var y = -Math.sin( a / 2.0 ); var y = -Math.sin( a / 2.0 );
@ -82,7 +84,6 @@ Rectangle {
MyAvatar.setOrientationVar(yaw(Date.now() / 1000)); MyAvatar.setOrientationVar(yaw(Date.now() / 1000));
} }
property bool running: false
Timer { Timer {
id: stopTimer id: stopTimer
@ -102,13 +103,42 @@ Rectangle {
Row { Row {
id: row id: row
anchors { left: parent.left; right: parent.right; } anchors { left: parent.left; right: parent.right; top: parent.top; margins: 16 }
spacing: 8 spacing: 8
Button { Button {
text: root.running ? "Stop" : "Run" text: root.running ? "Stop" : "Run"
onClicked: root.running ? stopTest() : startTest(); 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 { // Rectangle {
// anchors { left: parent.left; right: parent.right; top: row.bottom; topMargin: 8; bottom: parent.bottom; } // anchors { left: parent.left; right: parent.right; top: row.bottom; topMargin: 8; bottom: parent.bottom; }
@ -130,7 +160,7 @@ Rectangle {
Canvas { Canvas {
id: resultGraph 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 maxValue: 200;
property real perFrame: 10000; property real perFrame: 10000;
property real k1: (5 / maxValue) * height; property real k1: (5 / maxValue) * height;

View file

@ -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]
}]
}
}
}
}
}

View file

@ -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(); });

View file

@ -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()
}
}

View file

@ -24,7 +24,7 @@ Window {
resizable: true resizable: true
destroyOnInvisible: true destroyOnInvisible: true
x: 40; y: 40 x: 40; y: 40
implicitWidth: 400; implicitHeight: 695 implicitWidth: 400; implicitHeight: 728
minSize: Qt.vector2d(200, 300) minSize: Qt.vector2d(200, 300)
HifiConstants { id: hifi } 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
}
} }
} }
} }

View file

@ -131,6 +131,7 @@
#include "scripting/WebWindowClass.h" #include "scripting/WebWindowClass.h"
#include "scripting/WindowScriptingInterface.h" #include "scripting/WindowScriptingInterface.h"
#include "scripting/ControllerScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h"
#include "scripting/RatesScriptingInterface.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) #if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h" #include "SpeechRecognizer.h"
#endif #endif
@ -1384,6 +1385,7 @@ void Application::initializeUi() {
rootContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data()); rootContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
rootContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data()); rootContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
rootContext->setContextProperty("FrameTimings", &_frameTimingsScriptingInterface); rootContext->setContextProperty("FrameTimings", &_frameTimingsScriptingInterface);
rootContext->setContextProperty("Rates", new RatesScriptingInterface(this));
rootContext->setContextProperty("TREE_SCALE", TREE_SCALE); rootContext->setContextProperty("TREE_SCALE", TREE_SCALE);
rootContext->setContextProperty("Quat", new Quat()); rootContext->setContextProperty("Quat", new Quat());
@ -4439,6 +4441,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
// AvatarManager has some custom types // AvatarManager has some custom types
AvatarManager::registerMetaTypes(scriptEngine); AvatarManager::registerMetaTypes(scriptEngine);
scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this));
// hook our avatar and avatar hash map object into this script engine // hook our avatar and avatar hash map object into this script engine
scriptEngine->registerGlobalObject("MyAvatar", getMyAvatar()); scriptEngine->registerGlobalObject("MyAvatar", getMyAvatar());
qScriptRegisterMetaType(scriptEngine, audioListenModeToScriptValue, audioListenModeFromScriptValue); qScriptRegisterMetaType(scriptEngine, audioListenModeToScriptValue, audioListenModeFromScriptValue);

View file

@ -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 <display-plugins/DisplayPlugin.h>
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

View file

@ -64,9 +64,9 @@ void AnimationReader::run() {
if (urlValid) { if (urlValid) {
// Parse the FBX directly from the QNetworkReply // Parse the FBX directly from the QNetworkReply
FBXGeometry* fbxgeo = nullptr; FBXGeometry::Pointer fbxgeo;
if (_url.path().toLower().endsWith(".fbx")) { if (_url.path().toLower().endsWith(".fbx")) {
fbxgeo = readFBX(_data, QVariantHash(), _url.path()); fbxgeo.reset(readFBX(_data, QVariantHash(), _url.path()));
} else { } else {
QString errorStr("usupported format"); QString errorStr("usupported format");
emit onError(299, errorStr); emit onError(299, errorStr);
@ -117,16 +117,16 @@ const QVector<FBXAnimationFrame>& Animation::getFramesReference() const {
void Animation::downloadFinished(const QByteArray& data) { void Animation::downloadFinished(const QByteArray& data) {
// parse the animation/fbx file on a background thread. // parse the animation/fbx file on a background thread.
AnimationReader* animationReader = new AnimationReader(_url, data); 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))); connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString)));
QThreadPool::globalInstance()->start(animationReader); QThreadPool::globalInstance()->start(animationReader);
} }
void Animation::animationParseSuccess(FBXGeometry* geometry) { void Animation::animationParseSuccess(FBXGeometry::Pointer geometry) {
qCDebug(animation) << "Animation parse success" << _url.toDisplayString(); qCDebug(animation) << "Animation parse success" << _url.toDisplayString();
_geometry.reset(geometry); _geometry = geometry;
finishedLoading(true); finishedLoading(true);
} }

View file

@ -68,12 +68,12 @@ protected:
virtual void downloadFinished(const QByteArray& data) override; virtual void downloadFinished(const QByteArray& data) override;
protected slots: protected slots:
void animationParseSuccess(FBXGeometry* geometry); void animationParseSuccess(FBXGeometry::Pointer geometry);
void animationParseError(int error, QString str); void animationParseError(int error, QString str);
private: private:
std::unique_ptr<FBXGeometry> _geometry; FBXGeometry::Pointer _geometry;
}; };
/// Reads geometry in a worker thread. /// Reads geometry in a worker thread.
@ -85,7 +85,7 @@ public:
virtual void run(); virtual void run();
signals: signals:
void onSuccess(FBXGeometry* geometry); void onSuccess(FBXGeometry::Pointer geometry);
void onError(int error, QString str); void onError(int error, QString str);
private: private:

View file

@ -39,6 +39,8 @@
using namespace std; using namespace std;
static int FBXGeometryPointerMetaTypeId = qRegisterMetaType<FBXGeometry::Pointer>();
QStringList FBXGeometry::getJointNames() const { QStringList FBXGeometry::getJointNames() const {
QStringList names; QStringList names;
foreach (const FBXJoint& joint, joints) { foreach (const FBXJoint& joint, joints) {

View file

@ -270,6 +270,7 @@ inline bool operator!=(const SittingPoint& lhs, const SittingPoint& rhs)
/// A set of meshes extracted from an FBX document. /// A set of meshes extracted from an FBX document.
class FBXGeometry { class FBXGeometry {
public: public:
using Pointer = std::shared_ptr<FBXGeometry>;
QString author; QString author;
QString applicationName; ///< the name of the application that generated the model QString applicationName; ///< the name of the application that generated the model
@ -330,6 +331,7 @@ public:
}; };
Q_DECLARE_METATYPE(FBXGeometry) Q_DECLARE_METATYPE(FBXGeometry)
Q_DECLARE_METATYPE(FBXGeometry::Pointer)
/// Reads FBX geometry from the supplied model and mapping data. /// Reads FBX geometry from the supplied model and mapping data.
/// \exception QString if an error occurs in parsing /// \exception QString if an error occurs in parsing

View file

@ -17,6 +17,7 @@
using namespace gpu; using namespace gpu;
static int TexturePointerMetaTypeId = qRegisterMetaType<TexturePointer>();
std::atomic<uint32_t> Texture::_textureCPUCount{ 0 }; std::atomic<uint32_t> Texture::_textureCPUCount{ 0 };
std::atomic<Texture::Size> Texture::_textureCPUMemoryUsage{ 0 }; std::atomic<Texture::Size> Texture::_textureCPUMemoryUsage{ 0 };
@ -857,8 +858,8 @@ void TextureSource::reset(const QUrl& url) {
_imageUrl = url; _imageUrl = url;
} }
void TextureSource::resetTexture(gpu::Texture* texture) { void TextureSource::resetTexture(gpu::TexturePointer texture) {
_gpuTexture.reset(texture); _gpuTexture = texture;
} }
bool TextureSource::isDefined() const { bool TextureSource::isDefined() const {

View file

@ -16,6 +16,7 @@
#include <algorithm> //min max and more #include <algorithm> //min max and more
#include <bitset> #include <bitset>
#include <QMetaType>
#include <QUrl> #include <QUrl>
namespace gpu { namespace gpu {
@ -469,7 +470,6 @@ protected:
typedef std::shared_ptr<Texture> TexturePointer; typedef std::shared_ptr<Texture> TexturePointer;
typedef std::vector< TexturePointer > Textures; typedef std::vector< TexturePointer > Textures;
// TODO: For now TextureView works with Texture as a place holder for the Texture. // 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 // The overall logic should be about the same except that the Texture will be a real GL Texture under the hood
class TextureView { class TextureView {
@ -526,7 +526,7 @@ public:
void reset(const QUrl& url); void reset(const QUrl& url);
void resetTexture(gpu::Texture* texture); void resetTexture(gpu::TexturePointer texture);
bool isDefined() const; bool isDefined() const;
@ -538,5 +538,6 @@ typedef std::shared_ptr< TextureSource > TextureSourcePointer;
}; };
Q_DECLARE_METATYPE(gpu::TexturePointer)
#endif #endif

View file

@ -143,40 +143,38 @@ void GeometryReader::run() {
QString urlname = _url.path().toLower(); QString urlname = _url.path().toLower();
if (!urlname.isEmpty() && !_url.path().isEmpty() && if (!urlname.isEmpty() && !_url.path().isEmpty() &&
(_url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"))) { (_url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"))) {
FBXGeometry* fbxGeometry = nullptr; FBXGeometry::Pointer fbxGeometry;
if (_url.path().toLower().endsWith(".fbx")) { 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) { if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) {
throw QString("empty geometry, possibly due to an unsupported FBX version"); throw QString("empty geometry, possibly due to an unsupported FBX version");
} }
} else if (_url.path().toLower().endsWith(".obj")) { } else if (_url.path().toLower().endsWith(".obj")) {
fbxGeometry = OBJReader().readOBJ(_data, _mapping, _url); fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _url));
} else { } else {
throw QString("unsupported format"); 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(); auto resource = _resource.toStrongRef();
if (!resource) { if (!resource) {
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
delete fbxGeometry;
} else { } else {
QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", Qt::BlockingQueuedConnection, Q_ARG(void*, fbxGeometry)); QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition",
Q_ARG(FBXGeometry::Pointer, fbxGeometry));
} }
} else { } else {
throw QString("url is invalid"); throw QString("url is invalid");
} }
} catch (const QString& error) { } catch (const QString& error) {
qCDebug(modelnetworking) << "Error reading " << _url << ": " << error; qCDebug(modelnetworking) << "Error parsing model for" << _url << ":" << error;
auto resource = _resource.toStrongRef(); auto resource = _resource.toStrongRef();
// Ensure the resoruce has not been deleted, and won't be while invokeMethod is in flight. if (resource) {
if (!resource) { QMetaObject::invokeMethod(resource.data(), "finishedLoading",
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; Q_ARG(bool, false));
} else {
QMetaObject::invokeMethod(resource.data(), "finishedLoading", Qt::BlockingQueuedConnection, Q_ARG(bool, false));
} }
} }
} }
@ -190,7 +188,7 @@ public:
virtual void downloadFinished(const QByteArray& data) override; virtual void downloadFinished(const QByteArray& data) override;
protected: protected:
Q_INVOKABLE void setGeometryDefinition(void* fbxGeometry); Q_INVOKABLE void setGeometryDefinition(FBXGeometry::Pointer fbxGeometry);
private: private:
QVariantHash _mapping; QVariantHash _mapping;
@ -200,9 +198,9 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, 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 // Assume ownership of the geometry pointer
_geometry.reset(static_cast<FBXGeometry*>(fbxGeometry)); _geometry = fbxGeometry;
// Copy materials // Copy materials
QHash<QString, size_t> materialIDAtlas; QHash<QString, size_t> materialIDAtlas;

View file

@ -330,7 +330,7 @@ void ImageReader::run() {
return; return;
} }
gpu::Texture* texture = nullptr; gpu::TexturePointer texture = nullptr;
{ {
// Double-check the resource still exists between long operations. // Double-check the resource still exists between long operations.
auto resource = _resource.toStrongRef(); auto resource = _resource.toStrongRef();
@ -342,36 +342,32 @@ void ImageReader::run() {
auto url = _url.toString().toStdString(); auto url = _url.toString().toStdString();
PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr); PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr);
texture = resource.dynamicCast<NetworkTexture>()->getTextureLoader()(image, url); texture.reset(resource.dynamicCast<NetworkTexture>()->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(); auto resource = _resource.toStrongRef();
if (!resource) { if (!resource) {
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
delete texture;
} else { } else {
QMetaObject::invokeMethod(resource.data(), "setImage", Qt::BlockingQueuedConnection, QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(void*, texture), Q_ARG(gpu::TexturePointer, texture),
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); 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) { int originalHeight) {
_originalWidth = originalWidth; _originalWidth = originalWidth;
_originalHeight = originalHeight; _originalHeight = originalHeight;
gpu::Texture* texture = static_cast<gpu::Texture*>(voidTexture);
// Passing ownership // Passing ownership
_textureSource->resetTexture(texture); _textureSource->resetTexture(texture);
auto gpuTexture = _textureSource->getGPUTexture();
if (gpuTexture) { if (texture) {
_width = gpuTexture->getWidth(); _width = texture->getWidth();
_height = gpuTexture->getHeight(); _height = texture->getHeight();
setSize(gpuTexture->getStoredSize()); setSize(texture->getStoredSize());
} else { } else {
// FIXME: If !gpuTexture, we failed to load! // FIXME: If !gpuTexture, we failed to load!
_width = _height = 0; _width = _height = 0;

View file

@ -128,7 +128,6 @@ public:
signals: signals:
void networkTextureCreated(const QWeakPointer<NetworkTexture>& self); void networkTextureCreated(const QWeakPointer<NetworkTexture>& self);
protected: protected:
virtual bool isCacheable() const override { return _loaded; } virtual bool isCacheable() const override { return _loaded; }
@ -136,9 +135,7 @@ protected:
virtual void downloadFinished(const QByteArray& data) override; virtual void downloadFinished(const QByteArray& data) override;
Q_INVOKABLE void loadContent(const QByteArray& content); 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(gpu::TexturePointer texture, int originalWidth, int originalHeight);
Q_INVOKABLE void setImage(void* texture, int originalWidth, int originalHeight);
private: private:
TextureType _type; TextureType _type;

View file

@ -32,7 +32,6 @@
MessageID AssetClient::_currentID = 0; MessageID AssetClient::_currentID = 0;
AssetClient::AssetClient() { AssetClient::AssetClient() {
setCustomDeleter([](Dependency* dependency){ setCustomDeleter([](Dependency* dependency){
static_cast<AssetClient*>(dependency)->deleteLater(); static_cast<AssetClient*>(dependency)->deleteLater();
}); });
@ -502,7 +501,18 @@ bool AssetClient::cancelGetAssetInfoRequest(MessageID id) {
bool AssetClient::cancelGetAssetRequest(MessageID id) { bool AssetClient::cancelGetAssetRequest(MessageID id) {
// Search through each pending mapping request for id `id` // Search through each pending mapping request for id `id`
for (auto& kv : _pendingRequests) { 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; return true;
} }
} }

View file

@ -606,8 +606,9 @@ void Resource::handleReplyFinished() {
const int BASE_DELAY_MS = 1000; const int BASE_DELAY_MS = 1000;
if (_attempts++ < MAX_ATTEMPTS) { if (_attempts++ < MAX_ATTEMPTS) {
auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts); auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts);
qCDebug(networking).nospace() << "Retrying to load the asset in " << waitTime qCDebug(networking) << "Server unavailable for" << _url <<
<< "ms, attempt " << _attempts << " of " << MAX_ATTEMPTS; "retrying in " << waitTime << "ms," <<
"attempt " << _attempts + 1 << "of" << MAX_ATTEMPTS;
QTimer::singleShot(waitTime, this, &Resource::attemptRequest); QTimer::singleShot(waitTime, this, &Resource::attemptRequest);
break; break;
} }

View file

@ -1,6 +1,5 @@
<@include gpu/Config.slh@> <@include gpu/Config.slh@>
<$VERSION_HEADER$> <$VERSION_HEADER$>
#line __LINE__
// Generated on <$_SCRIBE_DATE$> // Generated on <$_SCRIBE_DATE$>
// stars.frag // stars.frag
// fragment shader // fragment shader

View file

@ -11,6 +11,7 @@
#define hifi_Shared_RateCounter_h #define hifi_Shared_RateCounter_h
#include <stdint.h> #include <stdint.h>
#include <atomic>
#include <functional> #include <functional>
#include <QtCore/QElapsedTimer> #include <QtCore/QElapsedTimer>
@ -20,6 +21,8 @@
template <uint32_t INTERVAL = MSECS_PER_SECOND, uint8_t PRECISION = 2> template <uint32_t INTERVAL = MSECS_PER_SECOND, uint8_t PRECISION = 2>
class RateCounter { class RateCounter {
public: public:
RateCounter() { _rate = 0; } // avoid use of std::atomic copy ctor
void increment(size_t count = 1) { void increment(size_t count = 1) {
auto now = usecTimestampNow(); auto now = usecTimestampNow();
float currentIntervalMs = (now - _start) / (float) USECS_PER_MSEC; float currentIntervalMs = (now - _start) / (float) USECS_PER_MSEC;
@ -42,8 +45,8 @@ public:
private: private:
uint64_t _start { usecTimestampNow() }; uint64_t _start { usecTimestampNow() };
size_t _count { 0 }; size_t _count { 0 };
float _rate { 0 };
const float _scale { powf(10, PRECISION) }; const float _scale { powf(10, PRECISION) };
std::atomic<float> _rate;
}; };
#endif #endif

View file

@ -14,6 +14,9 @@
#include <QtCore/QFile> #include <QtCore/QFile>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QRegularExpression> #include <QtCore/QRegularExpression>
#include <QDesktopServices>
#include "PathUtils.h"
QUrl FileDialogHelper::home() { QUrl FileDialogHelper::home() {
@ -103,3 +106,7 @@ QStringList FileDialogHelper::drives() {
} }
return result; return result;
} }
void FileDialogHelper::openScriptsDirectory() {
QDesktopServices::openUrl(defaultScriptsLocation());
}

View file

@ -58,6 +58,8 @@ public:
Q_INVOKABLE bool validFolder(const QString& path); Q_INVOKABLE bool validFolder(const QString& path);
Q_INVOKABLE QUrl pathToUrl(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 QUrl saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters);
Q_INVOKABLE void openScriptsDirectory();
}; };

View file

@ -64,6 +64,15 @@ void MainWindow::saveGeometry() {
} }
void MainWindow::closeEvent(QCloseEvent* event) { 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(); qApp->quit();
} }

View file

@ -1,9 +1,8 @@
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
exports.searchPaths = function(name, binaryType, releaseType) {
function platformExtension(name) { function platformExtension(name) {
if (name == "Interface" || name == "High Fidelity") { if (name == "Interface") {
if (process.platform == "darwin") { if (process.platform == "darwin") {
return ".app/Contents/MacOS/" + name return ".app/Contents/MacOS/" + name
} else if (process.platform == "win32") { } else if (process.platform == "win32") {
@ -16,6 +15,8 @@ exports.searchPaths = function(name, binaryType, releaseType) {
} }
} }
exports.searchPaths = function(name, binaryType, releaseType) {
var extension = platformExtension(name); var extension = platformExtension(name);
var devBasePath = "../build/" + name + "/"; var devBasePath = "../build/" + name + "/";
@ -68,6 +69,7 @@ exports.discoveredPath = function (name, binaryType, releaseType) {
try { try {
var stats = fs.lstatSync(testPath); var stats = fs.lstatSync(testPath);
var extension = platformExtension(name);
if (stats.isFile() || (stats.isDirectory() && extension == ".app")) { if (stats.isFile() || (stats.isDirectory() && extension == ".app")) {
console.log("Found " + name + " at " + testPath); console.log("Found " + name + " at " + testPath);
@ -81,11 +83,6 @@ exports.discoveredPath = function (name, binaryType, releaseType) {
return null; 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 // attempt to find a binary at the usual paths, return null if it doesn't exist
return binaryFromPaths(name, this.searchPaths(name, binaryType, releaseType)); return binaryFromPaths(name, this.searchPaths(name, binaryType, releaseType));
} }