diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 85a2c95b4d..0455377d89 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -235,6 +235,11 @@ void AssignmentClient::sendAssignmentRequest() { void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer message) { qCDebug(assigmnentclient) << "Received a PacketType::CreateAssignment - attempting to unpack."; + if (_currentAssignment) { + qCWarning(assigmnentclient) << "Received a PacketType::CreateAssignment while still running an active assignment. Ignoring."; + return; + } + // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(*message); diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index d8795341bb..2e9129f3ac 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -7,7 +7,6 @@ // Copyright 2015 High Fidelity, Inc. // // Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. -// Also supports touch and equipping objects. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -305,7 +304,6 @@ function MyController(hand) { switch (this.state) { case STATE_OFF: this.off(); - this.touchTest(); break; case STATE_SEARCHING: case STATE_HOLD_SEARCHING: @@ -1672,71 +1670,6 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("continueFarTrigger"); }; - _this.allTouchedIDs = {}; - - this.touchTest = function() { - var maxDistance = 0.05; - var leftHandPosition = MyAvatar.getLeftPalmPosition(); - var rightHandPosition = MyAvatar.getRightPalmPosition(); - var leftEntities = Entities.findEntities(leftHandPosition, maxDistance); - var rightEntities = Entities.findEntities(rightHandPosition, maxDistance); - var ids = []; - - if (leftEntities.length !== 0) { - leftEntities.forEach(function(entity) { - ids.push(entity); - }); - - } - - if (rightEntities.length !== 0) { - rightEntities.forEach(function(entity) { - ids.push(entity); - }); - } - - ids.forEach(function(id) { - var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); - if (!props || - !props.boundingBox || - props.name === 'pointer') { - return; - } - var entityMinPoint = props.boundingBox.brn; - var entityMaxPoint = props.boundingBox.tfl; - var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint); - var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint); - - if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) { - // we haven't been touched before, but either right or left is touching us now - _this.allTouchedIDs[id] = true; - _this.startTouch(id); - } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { - // we have been touched before and are still being touched - // continue touch - _this.continueTouch(id); - } else if (_this.allTouchedIDs[id]) { - delete _this.allTouchedIDs[id]; - _this.stopTouch(id); - } - }); - }; - - this.startTouch = function(entityID) { - var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(entityID, "startTouch", args); - }; - - this.continueTouch = function(entityID) { - var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(entityID, "continueTouch", args); - }; - - this.stopTouch = function(entityID) { - var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(entityID, "stopTouch", args); - }; - this.release = function() { this.turnLightsOff(); this.turnOffVisualizations(); diff --git a/examples/utilities/cache/cacheStats.js b/examples/utilities/cache/cacheStats.js new file mode 100644 index 0000000000..e58a5d0e21 --- /dev/null +++ b/examples/utilities/cache/cacheStats.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('stats.qml'); +var window = new OverlayWindow({ + title: 'Cache Stats', + source: qml, + width: 300, + height: 200 +}); +window.setPosition(500, 50); +window.closed.connect(function() { Script.stop(); }); diff --git a/examples/utilities/cache/stats.qml b/examples/utilities/cache/stats.qml new file mode 100644 index 0000000000..d7888eb1aa --- /dev/null +++ b/examples/utilities/cache/stats.qml @@ -0,0 +1,77 @@ +// +// 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: [["Animation", AnimationCache], ["Model", ModelCache], ["Texture", TextureCache], ["Sound", SoundCache]] + + Grid { + id: grid + rows: root.caches.length; columns: 1; spacing: 8 + anchors.fill: parent + + Repeater { + id: repeater + + model: root.caches + + Row { + PlotPerf { + title: modelData[0] + " Count" + anchors.left: parent + height: (grid.height - (grid.spacing * (root.caches.length + 1))) / root.caches.length + width: grid.width / 2 - grid.spacing * 1.5 + object: modelData[1] + valueNumDigits: "1" + plots: [ + { + prop: "numTotal", + label: "total", + color: "#00B4EF" + }, + { + prop: "numCached", + label: "cached", + color: "#1AC567" + } + ] + } + PlotPerf { + title: modelData[0] + " Size" + anchors.right: parent + height: (grid.height - (grid.spacing * (root.caches.length + 1))) / root.caches.length + width: grid.width / 2 - grid.spacing * 1.5 + object: modelData[1] + valueScale: 1048576 + valueUnit: "Mb" + valueNumDigits: "1" + plots: [ + { + prop: "sizeTotal", + label: "total", + color: "#00B4EF" + }, + { + prop: "sizeCached", + label: "cached", + color: "#1AC567" + } + ] + } + } + } + } +} diff --git a/examples/utilities/render/plotperf/PlotPerf.qml b/examples/utilities/lib/plotperf/PlotPerf.qml similarity index 79% rename from examples/utilities/render/plotperf/PlotPerf.qml rename to examples/utilities/lib/plotperf/PlotPerf.qml index 179707c0f1..6871ffe6a6 100644 --- a/examples/utilities/render/plotperf/PlotPerf.qml +++ b/examples/utilities/lib/plotperf/PlotPerf.qml @@ -19,12 +19,8 @@ Item { // The title of the graph property string title - // THe object used as the default source object for the prop plots + // The object used as the default source object for the prop plots property var object - - // THis is my hack to get a property and assign it to a trigger var in order to get - // a signal called whenever the value changed - property var trigger // Plots is an array of plot descriptor // a default plot descriptor expects the following object: @@ -55,45 +51,38 @@ Item { property var tick : 0 function createValues() { - print("trigger is: " + JSON.stringify(trigger)) - if (Array.isArray(plots)) { - for (var i =0; i < plots.length; i++) { - var plot = plots[i]; - print(" a pnew Plot:" + JSON.stringify(plot)); - _values.push( { - object: (plot["object"] !== undefined ? plot["object"] : root.object), - value: plot["prop"], - valueMax: 1, - numSamplesConstantMax: 0, - valueHistory: new Array(), - label: (plot["label"] !== undefined ? plot["label"] : ""), - color: (plot["color"] !== undefined ? plot["color"] : "white"), - scale: (plot["scale"] !== undefined ? plot["scale"] : 1), - unit: (plot["unit"] !== undefined ? plot["unit"] : valueUnit) - }) - } + for (var i =0; i < plots.length; i++) { + var plot = plots[i]; + _values.push( { + object: (plot["object"] !== undefined ? plot["object"] : root.object), + value: plot["prop"], + valueMax: 1, + numSamplesConstantMax: 0, + valueHistory: new Array(), + label: (plot["label"] !== undefined ? plot["label"] : ""), + color: (plot["color"] !== undefined ? plot["color"] : "white"), + scale: (plot["scale"] !== undefined ? plot["scale"] : 1), + unit: (plot["unit"] !== undefined ? plot["unit"] : valueUnit) + }) } - print("in creator" + JSON.stringify(_values)); - + pullFreshValues(); } Component.onCompleted: { createValues(); - print(JSON.stringify(_values)); - } function pullFreshValues() { - //print("pullFreshValues"); + // Wait until values are created to begin pulling + if (!_values) { return; } + var VALUE_HISTORY_SIZE = 100; - var UPDATE_CANVAS_RATE = 20; tick++; - var currentValueMax = 0 for (var i = 0; i < _values.length; i++) { - var currentVal = _values[i].object[_values[i].value] * _values[i].scale; + var currentVal = (+_values[i].object[_values[i].value]) * _values[i].scale; _values[i].valueHistory.push(currentVal) _values[i].numSamplesConstantMax++; @@ -125,11 +114,13 @@ Item { valueMax = currentValueMax; } - if (tick % UPDATE_CANVAS_RATE == 0) { - mycanvas.requestPaint() - } + mycanvas.requestPaint() + } + + Timer { + interval: 100; running: true; repeat: true + onTriggered: pullFreshValues() } - onTriggerChanged: pullFreshValues() Canvas { id: mycanvas @@ -165,9 +156,9 @@ Item { ctx.fillStyle = val.color; var bestValue = val.valueHistory[val.valueHistory.length -1]; ctx.textAlign = "right"; - ctx.fillText(displayValue(bestValue, val.unit), width, (num + 2) * lineHeight * 1.5); + ctx.fillText(displayValue(bestValue, val.unit), width, (num + 2) * lineHeight * 1); ctx.textAlign = "left"; - ctx.fillText(val.label, 0, (num + 2) * lineHeight * 1.5); + ctx.fillText(val.label, 0, (num + 2) * lineHeight * 1); } function displayTitle(ctx, text, maxVal) { diff --git a/examples/utilities/render/plotperf/qmldir b/examples/utilities/lib/plotperf/qmldir similarity index 100% rename from examples/utilities/render/plotperf/qmldir rename to examples/utilities/lib/plotperf/qmldir diff --git a/examples/utilities/render/stats.qml b/examples/utilities/render/stats.qml index 6d2371fd53..e6cd03b3c3 100644 --- a/examples/utilities/render/stats.qml +++ b/examples/utilities/render/stats.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 import QtQuick.Controls 1.4 -import "plotperf" +import "../lib/plotperf" Item { id: statsUI @@ -32,7 +32,6 @@ Item { title: "Num Buffers" height: parent.evalEvenHeight() object: stats.config - trigger: stats.config["bufferCPUCount"] plots: [ { prop: "bufferCPUCount", @@ -50,7 +49,6 @@ Item { title: "gpu::Buffer Memory" height: parent.evalEvenHeight() object: stats.config - trigger: stats.config["bufferCPUMemoryUsage"] valueScale: 1048576 valueUnit: "Mb" valueNumDigits: "1" @@ -71,7 +69,6 @@ Item { title: "Num Textures" height: parent.evalEvenHeight() object: stats.config - trigger: stats.config["textureCPUCount"] plots: [ { prop: "textureCPUCount", @@ -99,7 +96,6 @@ Item { title: "gpu::Texture Memory" height: parent.evalEvenHeight() object: stats.config - trigger: stats.config["textureCPUMemoryUsage"] valueScale: 1048576 valueUnit: "Mb" valueNumDigits: "1" @@ -132,7 +128,6 @@ Item { title: "Triangles" height: parent.evalEvenHeight() object: stats.config - trigger: stats.config["frameTriangleCount"] valueScale: 1000 valueUnit: "K" plots: [ @@ -154,7 +149,6 @@ Item { title: "Drawcalls" height: parent.evalEvenHeight() object: stats.config - trigger: stats.config["frameDrawcallCount"] plots: [ { prop: "frameAPIDrawcallCount", @@ -184,7 +178,6 @@ Item { title: "Items" height: parent.evalEvenHeight() object: parent.drawOpaqueConfig - trigger: Render.getConfig("DrawOpaqueDeferred")["numDrawn"] plots: [ { object: Render.getConfig("DrawOpaqueDeferred"), diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 311583acb7..adcd43952c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -38,6 +38,8 @@ #include +#include + #include #include @@ -561,9 +563,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Model background downloads need to happen on the Datagram Processor Thread. The idle loop will // emit checkBackgroundDownloads to cause the ModelCache to check it's queue for requested background // downloads. - QSharedPointer modelCacheP = DependencyManager::get(); - ResourceCache* modelCache = modelCacheP.data(); - connect(this, &Application::checkBackgroundDownloads, modelCache, &ResourceCache::checkAsynchronousGets); + auto modelCache = DependencyManager::get(); + connect(this, &Application::checkBackgroundDownloads, modelCache.data(), &ModelCache::checkAsynchronousGets); // put the audio processing on a separate thread QThread* audioThread = new QThread(); @@ -1008,6 +1009,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); if (webEntity) { webEntity->setProxyWindow(_window->windowHandle()); + if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + _keyboardMouseDevice->pluginFocusOutEvent(); + } _keyboardFocusedItem = entityItemID; _lastAcceptedKeyPress = usecTimestampNow(); if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { @@ -1120,6 +1124,10 @@ void Application::aboutToQuit() { } void Application::cleanupBeforeQuit() { + // add a logline indicating if QTWEBENGINE_REMOTE_DEBUGGING is set or not + QString webengineRemoteDebugging = QProcessEnvironment::systemEnvironment().value("QTWEBENGINE_REMOTE_DEBUGGING", "false"); + qCDebug(interfaceapp) << "QTWEBENGINE_REMOTE_DEBUGGING =" << webengineRemoteDebugging; + // Stop third party processes so that they're not left running in the event of a subsequent shutdown crash. #ifdef HAVE_DDE DependencyManager::get()->setEnabled(false); @@ -1329,7 +1337,6 @@ void Application::initializeUi() { // though I can't find it. Hence, "ApplicationInterface" rootContext->setContextProperty("SnapshotUploader", new SnapshotUploader()); rootContext->setContextProperty("ApplicationInterface", this); - rootContext->setContextProperty("AnimationCache", DependencyManager::get().data()); rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); rootContext->setContextProperty("Controller", DependencyManager::get().data()); rootContext->setContextProperty("Entities", DependencyManager::get().data()); @@ -1359,8 +1366,13 @@ void Application::initializeUi() { rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); rootContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); + + // Caches rootContext->setContextProperty("AnimationCache", DependencyManager::get().data()); + rootContext->setContextProperty("TextureCache", DependencyManager::get().data()); + rootContext->setContextProperty("ModelCache", DependencyManager::get().data()); rootContext->setContextProperty("SoundCache", DependencyManager::get().data()); + rootContext->setContextProperty("Account", AccountScriptingInterface::getInstance()); rootContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface); rootContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); @@ -2564,6 +2576,15 @@ void Application::idle(uint64_t now) { _overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays)); } + // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus. + auto offscreenUi = DependencyManager::get(); + if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) { + _keyboardMouseDevice->pluginFocusOutEvent(); + _keyboardDeviceHasFocus = false; + } else if (offscreenUi && offscreenUi->getWindow()->activeFocusItem() == offscreenUi->getRootItem()) { + _keyboardDeviceHasFocus = true; + } + auto displayPlugin = getActiveDisplayPlugin(); // depending on whether we're throttling or not. // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in @@ -4353,8 +4374,13 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Stats", Stats::getInstance()); scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); + + // Caches scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface); diff --git a/interface/src/Application.h b/interface/src/Application.h index e3196febd5..f2b6edeea4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -518,6 +518,8 @@ private: std::mutex _preRenderLambdasLock; std::atomic _processOctreeStatsCounter { 0 }; + + bool _keyboardDeviceHasFocus { true }; }; #endif // hifi_Application_h diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index a9dcb3883c..b510f58b12 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -434,6 +434,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _showCollisionHull = shouldShowCollisionHull; render::PendingChanges pendingChanges; + _model->removeFromScene(scene, pendingChanges); + render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(getThisPointer(), statusGetters); _model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 51db1965bf..f314f6cb06 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -371,7 +371,7 @@ void NetworkTexture::setImage(void* voidTexture, int originalWidth, if (gpuTexture) { _width = gpuTexture->getWidth(); _height = gpuTexture->getHeight(); - setBytes(gpuTexture->getStoredSize()); + setSize(gpuTexture->getStoredSize()); } else { // FIXME: If !gpuTexture, we failed to load! _width = _height = 0; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index b5f5ca7c25..c661c2f32a 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -38,6 +38,7 @@ ResourceCache::~ResourceCache() { void ResourceCache::refreshAll() { // Clear all unused resources so we don't have to reload them clearUnusedResource(); + resetResourceCounters(); // Refresh all remaining resources in use foreach (auto resource, _resources) { @@ -53,9 +54,27 @@ void ResourceCache::refresh(const QUrl& url) { resource->refresh(); } else { _resources.remove(url); + resetResourceCounters(); } } +QVariantList ResourceCache::getResourceList() { + QVariantList list; + if (QThread::currentThread() != thread()) { + // NOTE: invokeMethod does not allow a const QObject* + QMetaObject::invokeMethod(this, "getResourceList", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QVariantList, list)); + } else { + auto resources = _resources.uniqueKeys(); + list.reserve(resources.size()); + for (auto& resource : resources) { + list << resource; + } + } + + return list; +} + void ResourceCache::setRequestLimit(int limit) { _requestLimit = limit; @@ -114,6 +133,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& void ResourceCache::setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize) { _unusedResourcesMaxSize = clamp(unusedResourcesMaxSize, MIN_UNUSED_MAX_SIZE, MAX_UNUSED_MAX_SIZE); reserveUnusedResource(0); + resetResourceCounters(); } void ResourceCache::addUnusedResource(const QSharedPointer& resource) { @@ -127,6 +147,8 @@ void ResourceCache::addUnusedResource(const QSharedPointer& resource) resource->setLRUKey(++_lastLRUKey); _unusedResources.insert(resource->getLRUKey(), resource); _unusedResourcesSize += resource->getBytes(); + + resetResourceCounters(); } void ResourceCache::removeUnusedResource(const QSharedPointer& resource) { @@ -134,6 +156,7 @@ void ResourceCache::removeUnusedResource(const QSharedPointer& resourc _unusedResources.remove(resource->getLRUKey()); _unusedResourcesSize -= resource->getBytes(); } + resetResourceCounters(); } void ResourceCache::reserveUnusedResource(qint64 resourceSize) { @@ -142,8 +165,13 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) { // unload the oldest resource QMap >::iterator it = _unusedResources.begin(); - _unusedResourcesSize -= it.value()->getBytes(); it.value()->setCache(nullptr); + auto size = it.value()->getBytes(); + + _totalResourcesSize -= size; + _resources.remove(it.value()->getURL()); + + _unusedResourcesSize -= size; _unusedResources.erase(it); } } @@ -159,6 +187,17 @@ void ResourceCache::clearUnusedResource() { } } +void ResourceCache::resetResourceCounters() { + _numTotalResources = _resources.size(); + _numUnusedResources = _unusedResources.size(); + emit dirty(); +} + +void ResourceCache::updateTotalSize(const qint64& oldSize, const qint64& newSize) { + _totalResourcesSize += (newSize - oldSize); + emit dirty(); +} + void ResourceCacheSharedItems::appendActiveRequest(Resource* resource) { Lock lock(_mutex); _loadingRequests.append(resource); @@ -377,6 +416,11 @@ void Resource::finishedLoading(bool success) { emit finished(success); } +void Resource::setSize(const qint64& bytes) { + QMetaObject::invokeMethod(_cache.data(), "updateTotalSize", Q_ARG(qint64, _bytes), Q_ARG(qint64, bytes)); + _bytes = bytes; +} + void Resource::reinsert() { _cache->_resources.insert(_url, _self); } @@ -412,7 +456,7 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota void Resource::handleReplyFinished() { Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); - _bytes = _bytesTotal; + setSize(_bytesTotal); if (!_request || _request != sender()) { // This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted. diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index f674c96a1e..84eba1cdc0 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -12,7 +12,9 @@ #ifndef hifi_ResourceCache_h #define hifi_ResourceCache_h +#include #include + #include #include #include @@ -29,6 +31,8 @@ #include "ResourceManager.h" +Q_DECLARE_METATYPE(size_t) + class QNetworkReply; class QTimer; @@ -79,8 +83,20 @@ private: /// Base class for resource caches. class ResourceCache : public QObject { Q_OBJECT + Q_PROPERTY(size_t numTotal READ getNumTotalResources NOTIFY dirty) + Q_PROPERTY(size_t numCached READ getNumCachedResources NOTIFY dirty) + Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty) + Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty) public: + size_t getNumTotalResources() const { return _numTotalResources; } + size_t getSizeTotalResources() const { return _totalResourcesSize; } + + size_t getNumCachedResources() const { return _numUnusedResources; } + size_t getSizeCachedResources() const { return _unusedResourcesSize; } + + Q_INVOKABLE QVariantList getResourceList(); + static void setRequestLimit(int limit); static int getRequestLimit() { return _requestLimit; } @@ -101,15 +117,21 @@ public: void refreshAll(); void refresh(const QUrl& url); +signals: + void dirty(); + public slots: void checkAsynchronousGets(); +protected slots: + void updateTotalSize(const qint64& oldSize, const qint64& newSize); + protected: /// Loads a resource from the specified URL. /// \param fallback a fallback URL to load if the desired one is unavailable /// \param delayLoad if true, don't load the resource immediately; wait until load is first requested /// \param extra extra data to pass to the creator, if appropriate - Q_INVOKABLE QSharedPointer getResource(const QUrl& url, const QUrl& fallback = QUrl(), + QSharedPointer getResource(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false, void* extra = NULL); /// Creates a new resource. @@ -118,18 +140,20 @@ protected: void addUnusedResource(const QSharedPointer& resource); void removeUnusedResource(const QSharedPointer& resource); - void reserveUnusedResource(qint64 resourceSize); - void clearUnusedResource(); /// Attempt to load a resource if requests are below the limit, otherwise queue the resource for loading /// \return true if the resource began loading, otherwise false if the resource is in the pending queue - Q_INVOKABLE static bool attemptRequest(Resource* resource); + static bool attemptRequest(Resource* resource); static void requestCompleted(Resource* resource); static bool attemptHighestPriorityRequest(); private: friend class Resource; + void reserveUnusedResource(qint64 resourceSize); + void clearUnusedResource(); + void resetResourceCounters(); + QHash> _resources; int _lastLRUKey = 0; @@ -140,8 +164,13 @@ private: QReadWriteLock _resourcesToBeGottenLock; QQueue _resourcesToBeGotten; + std::atomic _numTotalResources { 0 }; + std::atomic _numUnusedResources { 0 }; + + std::atomic _totalResourcesSize { 0 }; + std::atomic _unusedResourcesSize { 0 }; + qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE; - qint64 _unusedResourcesSize = 0; QMap> _unusedResources; }; @@ -226,7 +255,7 @@ protected: virtual void downloadFinished(const QByteArray& data) { finishedLoading(true); } /// Called when the download is finished and processed, sets the number of actual bytes. - void setBytes(qint64 bytes) { _bytes = bytes; } + void setSize(const qint64& bytes); /// Called when the download is finished and processed. /// This should be called by subclasses that override downloadFinished to mark the end of processing. diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 745a28eb0b..269cf86e2d 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -447,7 +447,7 @@ void OffscreenUi::createDesktop(const QUrl& url) { new KeyboardFocusHack(); - connect(_desktop, SIGNAL(showDesktop()), this, SLOT(showDesktop())); + connect(_desktop, SIGNAL(showDesktop()), this, SIGNAL(showDesktop())); } QQuickItem* OffscreenUi::getDesktop() { diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index a53643ba21..78a38d2b6a 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -52,6 +52,16 @@ void OculusDisplayPlugin::customizeContext() { } void OculusDisplayPlugin::uncustomizeContext() { + using namespace oglplus; + + // Present a final black frame to the HMD + _compositeFramebuffer->Bound(FramebufferTarget::Draw, [] { + Context::ClearColor(0, 0, 0, 1); + Context::Clear().ColorBuffer(); + }); + + hmdPresent(); + #if (OVR_MAJOR_VERSION >= 6) _sceneFbo.reset(); #endif diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index c9e702ecd0..71342f6e1b 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -90,12 +90,16 @@ ovrSession acquireOculusSession() { void releaseOculusSession() { Q_ASSERT(refCount > 0 && session); + // HACK the Oculus runtime doesn't seem to play well with repeated shutdown / restart. + // So for now we'll just hold on to the session +#if 0 if (!--refCount) { qCDebug(oculus) << "oculus: zero refcount, shutdown SDK and session"; ovr_Destroy(session); ovr_Shutdown(); session = nullptr; } +#endif }