From e89b6c5d2c457be9b7c70a3140237a07aa4d1c04 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Apr 2017 17:26:31 -0700 Subject: [PATCH 001/119] some initial debugging on tpose avatars --- interface/src/avatar/Avatar.cpp | 17 ++++++++++++----- libraries/avatars/src/AvatarData.cpp | 5 +++++ libraries/avatars/src/AvatarHashMap.cpp | 4 ++++ .../src/model-networking/ModelCache.h | 1 + .../networking/src/HTTPResourceRequest.cpp | 17 +++++++++++++---- libraries/networking/src/ReceivedMessage.cpp | 6 ++++-- libraries/networking/src/ReceivedMessage.h | 5 +++++ libraries/networking/src/ResourceCache.cpp | 5 +++-- libraries/networking/src/ResourceCache.h | 3 +++ libraries/networking/src/udt/PacketList.h | 2 ++ libraries/render-utils/src/Model.h | 1 + 11 files changed, 53 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 5b996a3cdf..563de9434d 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -341,6 +341,8 @@ void Avatar::updateAvatarEntities() { void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); + //qDebug() << __FUNCTION__ << "skeleton download attempts:" << _skeletonModel->getResourceDownloadAttempts(); + _simulationRate.increment(); if (inView) { _simulationInViewRate.increment(); @@ -1112,11 +1114,16 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void Avatar::setModelURLFinished(bool success) { if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) { - qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL; - // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that - // we don't redo this every time we receive an identity packet from the avatar with the bad url. - QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", - Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); + // FIXME -- + if (_skeletonModel->getResourceDownloadAttempts() > 4) { + qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL; + // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that + // we don't redo this every time we receive an identity packet from the avatar with the bad url. + QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", + Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); + } else { + qCWarning(interfaceapp) << "Avatar model: " << _skeletonModelURL << "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts(); + } } } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 9802630cf5..278ce53472 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1468,6 +1468,7 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged) { if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { + qDebug() << __FUNCTION__ << "about to call setSkeletonModelURL(identity.skeletonModelURL);... identity.skeletonModelURL:" << identity.skeletonModelURL; setSkeletonModelURL(identity.skeletonModelURL); identityChanged = true; if (_firstSkeletonCheck) { @@ -1514,6 +1515,10 @@ QByteArray AvatarData::identityByteArray() const { } void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { + if (skeletonModelURL.isEmpty()) { + qDebug() << __FUNCTION__ << "caller called with empty URL."; + } + const QUrl& expanded = skeletonModelURL.isEmpty() ? AvatarData::defaultFullAvatarModelUrl() : skeletonModelURL; if (expanded == _skeletonModelURL) { return; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 0d341c684e..46a3a67976 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -126,6 +126,10 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer message, SharedNodePointer sendingNode) { + qDebug() << __FUNCTION__ << "sendingNode:" << sendingNode << "message.failed():" << message->failed() + << "isComplete:" << message->isComplete() + << "messageNumber:" << message->getMessageNumber(); + AvatarData::Identity identity; AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 967897477d..67d57eab19 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -112,6 +112,7 @@ public: void setResource(GeometryResource::Pointer resource); QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); } + int getResourceDownloadAttempts() { return _resource ? _resource->getDownloadAttempts() : 0; } private: void startWatching(); diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 85da5de5b8..e43545f38e 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -75,10 +75,19 @@ void HTTPResourceRequest::onRequestFinished() { switch(_reply->error()) { case QNetworkReply::NoError: - _data = _reply->readAll(); - _loadedFromCache = _reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); - _result = Success; - break; + { + // For debugging, have a random chance of treating this like a failure + bool randFailure = false; // _url.toString().contains(".fst") ? (rand() % 100) > 10 : false; + + if (!randFailure) { + _data = _reply->readAll(); + _loadedFromCache = _reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); + _result = Success; + break; + } + // else fall through to timeout + qDebug() << "**** Randomly pretending to timeout instead of successfully download HTTP resource:" << _url << " ******************************"; + } case QNetworkReply::TimeoutError: _result = Timeout; diff --git a/libraries/networking/src/ReceivedMessage.cpp b/libraries/networking/src/ReceivedMessage.cpp index 02cb58fb2d..5c8239f227 100644 --- a/libraries/networking/src/ReceivedMessage.cpp +++ b/libraries/networking/src/ReceivedMessage.cpp @@ -27,7 +27,8 @@ ReceivedMessage::ReceivedMessage(const NLPacketList& packetList) _packetType(packetList.getType()), _packetVersion(packetList.getVersion()), _senderSockAddr(packetList.getSenderSockAddr()), - _isComplete(true) + _isComplete(true), + _messageNumber(packetList.getMessageNumber()) { } @@ -39,7 +40,8 @@ ReceivedMessage::ReceivedMessage(NLPacket& packet) _packetType(packet.getType()), _packetVersion(packet.getVersion()), _senderSockAddr(packet.getSenderSockAddr()), - _isComplete(packet.getPacketPosition() == NLPacket::ONLY) + _isComplete(packet.getPacketPosition() == NLPacket::ONLY), + _messageNumber(packet.getMessageNumber()) { } diff --git a/libraries/networking/src/ReceivedMessage.h b/libraries/networking/src/ReceivedMessage.h index 3acb7163e7..d154794719 100644 --- a/libraries/networking/src/ReceivedMessage.h +++ b/libraries/networking/src/ReceivedMessage.h @@ -50,6 +50,8 @@ public: qint64 getBytesLeftToRead() const { return _data.size() - _position; } + qint64 getMessageNumber() const { return _messageNumber; } + void seek(qint64 position) { _position = position; } qint64 peek(char* data, qint64 size); @@ -98,6 +100,9 @@ private: std::atomic _isComplete { true }; std::atomic _failed { false }; + + udt::Packet::MessageNumber _messageNumber { 0 }; + }; Q_DECLARE_METATYPE(ReceivedMessage*) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 4031ff8bf7..ed0d5fd20b 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -724,10 +724,11 @@ void Resource::handleReplyFinished() { } else { switch (result) { case ResourceRequest::Result::Timeout: { - qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal; + qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal << "attempt:" << _attempts << "of" << MAX_ATTEMPTS; // Fall through to other cases } case ResourceRequest::Result::ServerUnavailable: { + qCDebug(networking) << "Server Unavailable loading" << _url << "attempt:" << _attempts << "of" << MAX_ATTEMPTS; // retry with increasing delays const int BASE_DELAY_MS = 1000; if (_attempts++ < MAX_ATTEMPTS) { @@ -742,7 +743,7 @@ void Resource::handleReplyFinished() { // fall through to final failure } default: { - qCDebug(networking) << "Error loading " << _url; + qCDebug(networking) << "Error loading " << _url << "attempt:" << _attempts << "of" << MAX_ATTEMPTS; auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError : QNetworkReply::UnknownNetworkError; emit failed(error); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 53ccd2c386..df21a296a6 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -395,6 +395,9 @@ public: const QUrl& getURL() const { return _url; } + int getDownloadAttempts() { return _attempts; } + + signals: /// Fired when the resource begins downloading. void loading(); diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index 5083d77ec9..09857c795e 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -43,6 +43,8 @@ public: size_t getDataSize() const; size_t getMessageSize() const; QByteArray getMessage() const; + udt::Packet::MessageNumber getMessageNumber() const { return _messageNumber; } + QByteArray getExtendedHeader() const { return _extendedHeader; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 62e045a3c1..3e67f2a296 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -252,6 +252,7 @@ public: void renderDebugMeshBoxes(gpu::Batch& batch); + int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } public slots: void loadURLFinished(bool success); From 7f1a87ac4ff3019fc8cfabe1e0b0ff2f94f96c6b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Apr 2017 17:31:20 -0700 Subject: [PATCH 002/119] remove random http failure --- .../networking/src/HTTPResourceRequest.cpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index e43545f38e..3249f40b0e 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -72,22 +72,13 @@ void HTTPResourceRequest::onRequestFinished() { Q_ASSERT(_reply); cleanupTimer(); - + switch(_reply->error()) { case QNetworkReply::NoError: - { - // For debugging, have a random chance of treating this like a failure - bool randFailure = false; // _url.toString().contains(".fst") ? (rand() % 100) > 10 : false; - - if (!randFailure) { - _data = _reply->readAll(); - _loadedFromCache = _reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); - _result = Success; - break; - } - // else fall through to timeout - qDebug() << "**** Randomly pretending to timeout instead of successfully download HTTP resource:" << _url << " ******************************"; - } + _data = _reply->readAll(); + _loadedFromCache = _reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); + _result = Success; + break; case QNetworkReply::TimeoutError: _result = Timeout; From c19797935449f93bc367c4223cf1f0c17bf74034 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Apr 2017 18:36:00 -0700 Subject: [PATCH 003/119] add more logging --- libraries/avatars/src/AvatarData.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 278ce53472..b95387a456 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1457,6 +1457,14 @@ void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& ide QDataStream packetStream(data); packetStream >> identityOut.uuid >> identityOut.skeletonModelURL >> identityOut.attachmentData >> identityOut.displayName >> identityOut.sessionDisplayName >> identityOut.avatarEntityData; + + qDebug() << __FUNCTION__ + << "identityOut.uuid:" << identityOut.uuid + << "identityOut.skeletonModelURL:" << identityOut.skeletonModelURL + << "identityOut.displayName:" << identityOut.displayName + << "identityOut.sessionDisplayName:" << identityOut.sessionDisplayName + ; + } static const QUrl emptyURL(""); From 125b3d5f4e87dd9ffc7fa326cb4878d6aeca7ba1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Apr 2017 07:51:17 -0700 Subject: [PATCH 004/119] ignore old identity packets --- assignment-client/src/avatars/AvatarMixer.cpp | 5 +++-- libraries/avatars/src/AvatarData.cpp | 15 +++++++++++++-- libraries/avatars/src/AvatarData.h | 6 ++++-- libraries/avatars/src/AvatarHashMap.cpp | 5 +++-- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index f3da74ce5e..6e163e6d0b 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -404,10 +404,11 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes // parse the identity packet and update the change timestamp if appropriate AvatarData::Identity identity; - AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); + quint64 messageNumber; + AvatarData::parseAvatarIdentityPacket(message, identity, messageNumber); bool identityChanged = false; bool displayNameChanged = false; - avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged); + avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged, messageNumber); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); nodeData->flagIdentityChange(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index b95387a456..d06fdf502b 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1453,12 +1453,15 @@ QStringList AvatarData::getJointNames() const { return _jointNames; } -void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) { +void AvatarData::parseAvatarIdentityPacket(QSharedPointer message, Identity& identityOut, quint64& messageNumberOut) { + const QByteArray& data = message->getMessage(); + messageNumberOut = message->getMessageNumber(); QDataStream packetStream(data); packetStream >> identityOut.uuid >> identityOut.skeletonModelURL >> identityOut.attachmentData >> identityOut.displayName >> identityOut.sessionDisplayName >> identityOut.avatarEntityData; qDebug() << __FUNCTION__ + << "messageNumberOut:" << messageNumberOut << "identityOut.uuid:" << identityOut.uuid << "identityOut.skeletonModelURL:" << identityOut.skeletonModelURL << "identityOut.displayName:" << identityOut.displayName @@ -1473,7 +1476,15 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; } -void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged) { +void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, quint64 messageNumber) { + qDebug() << __FUNCTION__ << "messageNumber:" << messageNumber << "_lastIdentityPacketMessageNumber:" << _lastIdentityPacketMessageNumber; + + if (messageNumber < _lastIdentityPacketMessageNumber) { + qDebug() << "ignoring late identity packet for avatar " << getSessionUUID(); + return; + } + + _lastIdentityPacketMessageNumber = messageNumber; if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { qDebug() << __FUNCTION__ << "about to call setSkeletonModelURL(identity.skeletonModelURL);... identity.skeletonModelURL:" << identity.skeletonModelURL; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 8319eb5249..0eb22fd938 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -532,11 +532,11 @@ public: AvatarEntityMap avatarEntityData; }; - static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut); + static void parseAvatarIdentityPacket(QSharedPointer message, Identity& identityOut, quint64& messageNumberOut); // identityChanged returns true if identity has changed, false otherwise. // displayNameChanged returns true if displayName has changed, false otherwise. - void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged); + void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, quint64 messageNumber); QByteArray identityByteArray() const; @@ -778,6 +778,8 @@ protected: quint64 _audioLoudnessChanged { 0 }; float _audioAverageLoudness { 0.0f }; + quint64 _lastIdentityPacketMessageNumber { 0 }; + private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 46a3a67976..f371159e6e 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -131,7 +131,8 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer << "messageNumber:" << message->getMessageNumber(); AvatarData::Identity identity; - AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); + quint64 messageNumber; + AvatarData::parseAvatarIdentityPacket(message, identity, messageNumber); // make sure this isn't for an ignored avatar auto nodeList = DependencyManager::get(); @@ -152,7 +153,7 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer auto avatar = newOrExistingAvatar(identity.uuid, sendingNode); bool identityChanged = false; bool displayNameChanged = false; - avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged); + avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged, messageNumber); } } From c03182546bb4b41e0dc54b767873016cf21e5f23 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Apr 2017 09:11:51 -0700 Subject: [PATCH 005/119] some cleanup --- interface/src/avatar/Avatar.cpp | 10 +++++----- libraries/avatars/src/AvatarData.cpp | 15 +++++++-------- libraries/avatars/src/AvatarHashMap.cpp | 4 ---- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 563de9434d..9d79a40c11 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -341,8 +341,6 @@ void Avatar::updateAvatarEntities() { void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); - //qDebug() << __FUNCTION__ << "skeleton download attempts:" << _skeletonModel->getResourceDownloadAttempts(); - _simulationRate.increment(); if (inView) { _simulationInViewRate.increment(); @@ -1114,15 +1112,17 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void Avatar::setModelURLFinished(bool success) { if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) { - // FIXME -- - if (_skeletonModel->getResourceDownloadAttempts() > 4) { + const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: must be less than MAX_ATTEMPTS in ResourceCache.cpp + if (_skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) { qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL; // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that // we don't redo this every time we receive an identity packet from the avatar with the bad url. QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); } else { - qCWarning(interfaceapp) << "Avatar model: " << _skeletonModelURL << "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts(); + qCWarning(interfaceapp) << "Avatar model: " << _skeletonModelURL + << "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts() + << "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS; } } } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d06fdf502b..b81bf86559 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1460,13 +1460,14 @@ void AvatarData::parseAvatarIdentityPacket(QSharedPointer messa packetStream >> identityOut.uuid >> identityOut.skeletonModelURL >> identityOut.attachmentData >> identityOut.displayName >> identityOut.sessionDisplayName >> identityOut.avatarEntityData; - qDebug() << __FUNCTION__ +#ifdef WANT_DEBUG + qCDebug(avatars) << __FUNCTION__ << "messageNumberOut:" << messageNumberOut << "identityOut.uuid:" << identityOut.uuid << "identityOut.skeletonModelURL:" << identityOut.skeletonModelURL << "identityOut.displayName:" << identityOut.displayName - << "identityOut.sessionDisplayName:" << identityOut.sessionDisplayName - ; + << "identityOut.sessionDisplayName:" << identityOut.sessionDisplayName; +#endif } @@ -1477,17 +1478,15 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { } void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, quint64 messageNumber) { - qDebug() << __FUNCTION__ << "messageNumber:" << messageNumber << "_lastIdentityPacketMessageNumber:" << _lastIdentityPacketMessageNumber; - if (messageNumber < _lastIdentityPacketMessageNumber) { - qDebug() << "ignoring late identity packet for avatar " << getSessionUUID(); + qCDebug(avatars) << "Ignoring late identity packet for avatar " << getSessionUUID() + << "messageNumber:" << messageNumber << "_lastIdentityPacketMessageNumber:" << _lastIdentityPacketMessageNumber; return; } _lastIdentityPacketMessageNumber = messageNumber; if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { - qDebug() << __FUNCTION__ << "about to call setSkeletonModelURL(identity.skeletonModelURL);... identity.skeletonModelURL:" << identity.skeletonModelURL; setSkeletonModelURL(identity.skeletonModelURL); identityChanged = true; if (_firstSkeletonCheck) { @@ -1535,7 +1534,7 @@ QByteArray AvatarData::identityByteArray() const { void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { if (skeletonModelURL.isEmpty()) { - qDebug() << __FUNCTION__ << "caller called with empty URL."; + qCDebug(avatars) << __FUNCTION__ << "caller called with empty URL."; } const QUrl& expanded = skeletonModelURL.isEmpty() ? AvatarData::defaultFullAvatarModelUrl() : skeletonModelURL; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index f371159e6e..c4aa6c8479 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -126,10 +126,6 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer message, SharedNodePointer sendingNode) { - qDebug() << __FUNCTION__ << "sendingNode:" << sendingNode << "message.failed():" << message->failed() - << "isComplete:" << message->isComplete() - << "messageNumber:" << message->getMessageNumber(); - AvatarData::Identity identity; quint64 messageNumber; AvatarData::parseAvatarIdentityPacket(message, identity, messageNumber); From fbc699d6b261fd6d137628139d4048643fa84e88 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Apr 2017 09:30:00 -0700 Subject: [PATCH 006/119] handle overflow case --- libraries/avatars/src/AvatarData.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index b81bf86559..b904c2b6e8 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -1478,7 +1479,10 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { } void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, quint64 messageNumber) { - if (messageNumber < _lastIdentityPacketMessageNumber) { + + if (messageNumber < _lastIdentityPacketMessageNumber && + _lastIdentityPacketMessageNumber < std::numeric_limits::max()) { + qCDebug(avatars) << "Ignoring late identity packet for avatar " << getSessionUUID() << "messageNumber:" << messageNumber << "_lastIdentityPacketMessageNumber:" << _lastIdentityPacketMessageNumber; return; From f133713d0e2dabb114bde522e8d4defc7362d542 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Apr 2017 10:21:34 -0700 Subject: [PATCH 007/119] CR feedback --- interface/src/avatar/Avatar.cpp | 5 +++-- libraries/avatars/src/AvatarData.cpp | 2 +- libraries/avatars/src/AvatarData.h | 2 +- .../src/model-networking/ModelCache.h | 1 + libraries/networking/src/ReceivedMessage.cpp | 1 - libraries/networking/src/ReceivedMessage.h | 2 +- libraries/networking/src/ResourceCache.cpp | 12 +++++++----- libraries/networking/src/ResourceCache.h | 6 +++++- libraries/render-utils/src/Model.h | 1 + 9 files changed, 20 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9d79a40c11..0a4cdefef1 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1112,8 +1112,9 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void Avatar::setModelURLFinished(bool success) { if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) { - const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: must be less than MAX_ATTEMPTS in ResourceCache.cpp - if (_skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) { + const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts + if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 || + _skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) { qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL; // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that // we don't redo this every time we receive an identity packet from the avatar with the bad url. diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index b904c2b6e8..f541610ae9 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1454,7 +1454,7 @@ QStringList AvatarData::getJointNames() const { return _jointNames; } -void AvatarData::parseAvatarIdentityPacket(QSharedPointer message, Identity& identityOut, quint64& messageNumberOut) { +void AvatarData::parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut, quint64& messageNumberOut) { const QByteArray& data = message->getMessage(); messageNumberOut = message->getMessageNumber(); QDataStream packetStream(data); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 0eb22fd938..2ffa3c020c 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -532,7 +532,7 @@ public: AvatarEntityMap avatarEntityData; }; - static void parseAvatarIdentityPacket(QSharedPointer message, Identity& identityOut, quint64& messageNumberOut); + static void parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut, quint64& messageNumberOut); // identityChanged returns true if identity has changed, false otherwise. // displayNameChanged returns true if displayName has changed, false otherwise. diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 67d57eab19..4c68e1b6c3 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -113,6 +113,7 @@ public: QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); } int getResourceDownloadAttempts() { return _resource ? _resource->getDownloadAttempts() : 0; } + int getResourceDownloadAttemptsRemaining() { return _resource ? _resource->getDownloadAttemptsRemaining() : 0; } private: void startWatching(); diff --git a/libraries/networking/src/ReceivedMessage.cpp b/libraries/networking/src/ReceivedMessage.cpp index 5c8239f227..ecf7eb9fde 100644 --- a/libraries/networking/src/ReceivedMessage.cpp +++ b/libraries/networking/src/ReceivedMessage.cpp @@ -27,7 +27,6 @@ ReceivedMessage::ReceivedMessage(const NLPacketList& packetList) _packetType(packetList.getType()), _packetVersion(packetList.getVersion()), _senderSockAddr(packetList.getSenderSockAddr()), - _isComplete(true), _messageNumber(packetList.getMessageNumber()) { } diff --git a/libraries/networking/src/ReceivedMessage.h b/libraries/networking/src/ReceivedMessage.h index d154794719..ba61ef7044 100644 --- a/libraries/networking/src/ReceivedMessage.h +++ b/libraries/networking/src/ReceivedMessage.h @@ -101,7 +101,7 @@ private: std::atomic _isComplete { true }; std::atomic _failed { false }; - udt::Packet::MessageNumber _messageNumber { 0 }; + const udt::Packet::MessageNumber _messageNumber; // only settable on construction }; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index ed0d5fd20b..1e09bd4608 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -621,8 +621,6 @@ void Resource::init() { } } -const int MAX_ATTEMPTS = 8; - void Resource::attemptRequest() { _startedLoading = true; @@ -724,14 +722,17 @@ void Resource::handleReplyFinished() { } else { switch (result) { case ResourceRequest::Result::Timeout: { - qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal << "attempt:" << _attempts << "of" << MAX_ATTEMPTS; + qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal + << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining; // Fall through to other cases } case ResourceRequest::Result::ServerUnavailable: { - qCDebug(networking) << "Server Unavailable loading" << _url << "attempt:" << _attempts << "of" << MAX_ATTEMPTS; + qCDebug(networking) << "Server Unavailable loading" << _url << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining; // retry with increasing delays const int BASE_DELAY_MS = 1000; if (_attempts++ < MAX_ATTEMPTS) { + _attemptsRemaining--; + auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts); qCDebug(networking).noquote() << "Server unavailable for" << _url << "- may retry in" << waitTime << "ms" @@ -743,7 +744,8 @@ void Resource::handleReplyFinished() { // fall through to final failure } default: { - qCDebug(networking) << "Error loading " << _url << "attempt:" << _attempts << "of" << MAX_ATTEMPTS; + _attemptsRemaining = 0; + qCDebug(networking) << "Error loading " << _url << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining; auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError : QNetworkReply::UnknownNetworkError; emit failed(error); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index df21a296a6..8531560d99 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -396,7 +396,7 @@ public: const QUrl& getURL() const { return _url; } int getDownloadAttempts() { return _attempts; } - + int getDownloadAttemptsRemaining() { return _attemptsRemaining; } signals: /// Fired when the resource begins downloading. @@ -477,6 +477,10 @@ private: qint64 _bytesTotal{ 0 }; qint64 _bytes{ 0 }; int _attempts{ 0 }; + + const int MAX_ATTEMPTS = 8; + + int _attemptsRemaining { MAX_ATTEMPTS }; bool _isInScript{ false }; }; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 3e67f2a296..5899ccf6b5 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -253,6 +253,7 @@ public: void renderDebugMeshBoxes(gpu::Batch& batch); int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } + int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); } public slots: void loadURLFinished(bool success); From ff45ad53a3ac7a0d6fe4b5f243714c8d74dbedc3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Apr 2017 10:40:41 -0700 Subject: [PATCH 008/119] some more CR cleanup --- interface/src/avatar/Avatar.cpp | 3 ++- libraries/networking/src/ResourceCache.cpp | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 0a4cdefef1..ab3ba206f2 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1115,7 +1115,8 @@ void Avatar::setModelURLFinished(bool success) { const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 || _skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) { - qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL; + qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL + << "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts."; // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that // we don't redo this every time we receive an identity packet from the avatar with the bad url. QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 1e09bd4608..ba4de0e8ef 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -720,19 +720,18 @@ void Resource::handleReplyFinished() { emit loaded(data); downloadFinished(data); } else { + _attempts++; switch (result) { case ResourceRequest::Result::Timeout: { - qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal - << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining; + qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal ; // Fall through to other cases } case ResourceRequest::Result::ServerUnavailable: { + _attemptsRemaining--; qCDebug(networking) << "Server Unavailable loading" << _url << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining; // retry with increasing delays const int BASE_DELAY_MS = 1000; - if (_attempts++ < MAX_ATTEMPTS) { - _attemptsRemaining--; - + if (_attempts < MAX_ATTEMPTS) { auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts); qCDebug(networking).noquote() << "Server unavailable for" << _url << "- may retry in" << waitTime << "ms" From 088cdae320533494a97db424d20892359a2d9c4f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Apr 2017 11:14:47 -0700 Subject: [PATCH 009/119] CR feedback --- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 8 +++----- libraries/avatars/src/AvatarData.h | 6 +++--- libraries/avatars/src/AvatarHashMap.cpp | 2 +- libraries/networking/src/ReceivedMessage.h | 2 +- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 6e163e6d0b..532452a12d 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -404,7 +404,7 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes // parse the identity packet and update the change timestamp if appropriate AvatarData::Identity identity; - quint64 messageNumber; + udt::Packet::MessageNumber messageNumber; AvatarData::parseAvatarIdentityPacket(message, identity, messageNumber); bool identityChanged = false; bool displayNameChanged = false; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f541610ae9..4e9264829f 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1454,7 +1454,7 @@ QStringList AvatarData::getJointNames() const { return _jointNames; } -void AvatarData::parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut, quint64& messageNumberOut) { +void AvatarData::parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut, udt::Packet::MessageNumber& messageNumberOut) { const QByteArray& data = message->getMessage(); messageNumberOut = message->getMessageNumber(); QDataStream packetStream(data); @@ -1478,11 +1478,9 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; } -void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, quint64 messageNumber) { - - if (messageNumber < _lastIdentityPacketMessageNumber && - _lastIdentityPacketMessageNumber < std::numeric_limits::max()) { +void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, udt::Packet::MessageNumber messageNumber) { + if (messageNumber < _lastIdentityPacketMessageNumber) { qCDebug(avatars) << "Ignoring late identity packet for avatar " << getSessionUUID() << "messageNumber:" << messageNumber << "_lastIdentityPacketMessageNumber:" << _lastIdentityPacketMessageNumber; return; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 2ffa3c020c..fdf3f0530c 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -532,11 +532,11 @@ public: AvatarEntityMap avatarEntityData; }; - static void parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut, quint64& messageNumberOut); + static void parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut, udt::Packet::MessageNumber& messageNumberOut); // identityChanged returns true if identity has changed, false otherwise. // displayNameChanged returns true if displayName has changed, false otherwise. - void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, quint64 messageNumber); + void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, udt::Packet::MessageNumber messageNumber); QByteArray identityByteArray() const; @@ -778,7 +778,7 @@ protected: quint64 _audioLoudnessChanged { 0 }; float _audioAverageLoudness { 0.0f }; - quint64 _lastIdentityPacketMessageNumber { 0 }; + udt::Packet::MessageNumber _lastIdentityPacketMessageNumber{ 0 }; private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index c4aa6c8479..c4cd303360 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -127,7 +127,7 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer message, SharedNodePointer sendingNode) { AvatarData::Identity identity; - quint64 messageNumber; + udt::Packet::MessageNumber messageNumber; AvatarData::parseAvatarIdentityPacket(message, identity, messageNumber); // make sure this isn't for an ignored avatar diff --git a/libraries/networking/src/ReceivedMessage.h b/libraries/networking/src/ReceivedMessage.h index ba61ef7044..4787c849a2 100644 --- a/libraries/networking/src/ReceivedMessage.h +++ b/libraries/networking/src/ReceivedMessage.h @@ -50,7 +50,7 @@ public: qint64 getBytesLeftToRead() const { return _data.size() - _position; } - qint64 getMessageNumber() const { return _messageNumber; } + udt::Packet::MessageNumber getMessageNumber() const { return _messageNumber; } void seek(qint64 position) { _position = position; } From f07884602a4df09a7c8008e8042ab935fd79514c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Apr 2017 13:13:29 -0700 Subject: [PATCH 010/119] more CR feedback --- libraries/networking/src/ReceivedMessage.h | 2 +- libraries/networking/src/ResourceCache.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/networking/src/ReceivedMessage.h b/libraries/networking/src/ReceivedMessage.h index 4787c849a2..414d9fee52 100644 --- a/libraries/networking/src/ReceivedMessage.h +++ b/libraries/networking/src/ReceivedMessage.h @@ -50,7 +50,7 @@ public: qint64 getBytesLeftToRead() const { return _data.size() - _position; } - udt::Packet::MessageNumber getMessageNumber() const { return _messageNumber; } + const udt::Packet::MessageNumber& getMessageNumber() const { return _messageNumber; } void seek(qint64 position) { _position = position; } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 8531560d99..1c586d53f6 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -395,8 +395,8 @@ public: const QUrl& getURL() const { return _url; } - int getDownloadAttempts() { return _attempts; } - int getDownloadAttemptsRemaining() { return _attemptsRemaining; } + unsigned int getDownloadAttempts() { return _attempts; } + unsigned int getDownloadAttemptsRemaining() { return _attemptsRemaining; } signals: /// Fired when the resource begins downloading. @@ -476,11 +476,11 @@ private: qint64 _bytesReceived{ 0 }; qint64 _bytesTotal{ 0 }; qint64 _bytes{ 0 }; - int _attempts{ 0 }; + unsigned int _attempts{ 0 }; - const int MAX_ATTEMPTS = 8; + static const int MAX_ATTEMPTS = 8; - int _attemptsRemaining { MAX_ATTEMPTS }; + unsigned int _attemptsRemaining { MAX_ATTEMPTS }; bool _isInScript{ false }; }; From da68c49dc158ab83cd004b8087a54a4115202d0d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Apr 2017 13:31:40 -0700 Subject: [PATCH 011/119] more cleanup --- libraries/networking/src/udt/PacketList.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index 09857c795e..9fc914e7fb 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -43,7 +43,7 @@ public: size_t getDataSize() const; size_t getMessageSize() const; QByteArray getMessage() const; - udt::Packet::MessageNumber getMessageNumber() const { return _messageNumber; } + MessageNumber getMessageNumber() const { return _messageNumber; } QByteArray getExtendedHeader() const { return _extendedHeader; } From c9aad6b762f40043b79fc621f7219deceb95ecb2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 24 Apr 2017 17:36:05 -0700 Subject: [PATCH 012/119] new approach to the tpose fix, as well as other improvements --- assignment-client/src/avatars/AvatarMixer.cpp | 6 +-- interface/src/Application.cpp | 2 + interface/src/avatar/AvatarManager.cpp | 1 + interface/src/avatar/MyAvatar.cpp | 7 +++- libraries/avatars/src/AvatarData.cpp | 38 ++++++++++++++----- libraries/avatars/src/AvatarData.h | 21 ++++++++-- libraries/avatars/src/AvatarHashMap.cpp | 5 +-- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- 9 files changed, 61 insertions(+), 24 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 532452a12d..182b38b124 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -393,6 +393,7 @@ void AvatarMixer::handleRequestsDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode) { + qDebug() << __FUNCTION__; auto start = usecTimestampNow(); auto nodeList = DependencyManager::get(); getOrCreateClientData(senderNode); @@ -404,11 +405,10 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes // parse the identity packet and update the change timestamp if appropriate AvatarData::Identity identity; - udt::Packet::MessageNumber messageNumber; - AvatarData::parseAvatarIdentityPacket(message, identity, messageNumber); + AvatarData::parseAvatarIdentityPacket(message, identity); bool identityChanged = false; bool displayNameChanged = false; - avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged, messageNumber); + avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); nodeData->flagIdentityChange(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d7005f2c0d..a785cb5d7a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5198,6 +5198,8 @@ void Application::resettingDomain() { void Application::nodeAdded(SharedNodePointer node) const { if (node->getType() == NodeType::AvatarMixer) { // new avatar mixer, send off our identity packet right away + + qDebug() << __FUNCTION__ << "about to call... getMyAvatar()->sendIdentityPacket();"; getMyAvatar()->sendIdentityPacket(); getMyAvatar()->resetLastSent(); } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 585776b395..6f9793ad34 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -213,6 +213,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } } avatar->animateScaleChanges(deltaTime); + if (avatar->shouldDie()) { avatar->die(); removeAvatar(avatar->getID()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f4f078c9e5..c7d6b03f46 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -414,8 +414,11 @@ void MyAvatar::update(float deltaTime) { uint64_t now = usecTimestampNow(); if (now > _identityPacketExpiry || _avatarEntityDataLocallyEdited) { - _identityPacketExpiry = now + AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS; - sendIdentityPacket(); + _identityPacketExpiry = now + AVATAR_IDENTITY_PACKET_SEND_INTERVAL_USECS; + if (getIdentityDataChanged()) { + qDebug() << __FUNCTION__ << "about to call... sendIdentityPacket(); --- _identityPacketExpiry:" << _identityPacketExpiry << "_avatarEntityDataLocallyEdited:" << _avatarEntityDataLocallyEdited; + sendIdentityPacket(); + } } simulate(deltaTime); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4e9264829f..a0a710cd82 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1454,16 +1454,20 @@ QStringList AvatarData::getJointNames() const { return _jointNames; } -void AvatarData::parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut, udt::Packet::MessageNumber& messageNumberOut) { +void AvatarData::parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut) { const QByteArray& data = message->getMessage(); - messageNumberOut = message->getMessageNumber(); QDataStream packetStream(data); - packetStream >> identityOut.uuid >> identityOut.skeletonModelURL >> identityOut.attachmentData >> identityOut.displayName >> identityOut.sessionDisplayName >> identityOut.avatarEntityData; + packetStream >> identityOut.uuid + >> identityOut.skeletonModelURL + >> identityOut.attachmentData + >> identityOut.displayName + >> identityOut.sessionDisplayName + >> identityOut.avatarEntityData + >> identityOut.updatedAt; #ifdef WANT_DEBUG qCDebug(avatars) << __FUNCTION__ - << "messageNumberOut:" << messageNumberOut << "identityOut.uuid:" << identityOut.uuid << "identityOut.skeletonModelURL:" << identityOut.skeletonModelURL << "identityOut.displayName:" << identityOut.displayName @@ -1478,15 +1482,16 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; } -void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, udt::Packet::MessageNumber messageNumber) { +void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged) { - if (messageNumber < _lastIdentityPacketMessageNumber) { + if (identity.updatedAt < _identityUpdatedAt) { qCDebug(avatars) << "Ignoring late identity packet for avatar " << getSessionUUID() - << "messageNumber:" << messageNumber << "_lastIdentityPacketMessageNumber:" << _lastIdentityPacketMessageNumber; + << "identity.updatedAt:" << identity.updatedAt << "_identityUpdatedAt:" << _identityUpdatedAt; return; } - _lastIdentityPacketMessageNumber = messageNumber; + _identityUpdatedAt = identity.updatedAt; + qDebug() << __FUNCTION__ << "_identityUpdatedAt:" << _identityUpdatedAt; if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { setSkeletonModelURL(identity.skeletonModelURL); @@ -1525,10 +1530,16 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC QByteArray AvatarData::identityByteArray() const { QByteArray identityData; QDataStream identityStream(&identityData, QIODevice::Append); - const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); + const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL _avatarEntitiesLock.withReadLock([&] { - identityStream << getSessionUUID() << urlToSend << _attachmentData << _displayName << getSessionDisplayNameForTransport() << _avatarEntityData; + identityStream << getSessionUUID() + << urlToSend + << _attachmentData + << _displayName + << getSessionDisplayNameForTransport() // depends on _sessionDisplayName + << _avatarEntityData + << _identityUpdatedAt; }); return identityData; @@ -1547,6 +1558,7 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { qCDebug(avatars) << "Changing skeleton model for avatar" << getSessionUUID() << "to" << _skeletonModelURL.toString(); updateJointMappings(); + markIdentityDataChanged(); } void AvatarData::setDisplayName(const QString& displayName) { @@ -1556,6 +1568,7 @@ void AvatarData::setDisplayName(const QString& displayName) { sendIdentityPacket(); qCDebug(avatars) << "Changing display name for avatar to" << displayName; + markIdentityDataChanged(); } QVector AvatarData::getAttachmentData() const { @@ -1574,6 +1587,7 @@ void AvatarData::setAttachmentData(const QVector& attachmentData return; } _attachmentData = attachmentData; + markIdentityDataChanged(); } void AvatarData::attach(const QString& modelURL, const QString& jointName, @@ -1717,6 +1731,7 @@ void AvatarData::sendIdentityPacket() { }); _avatarEntityDataLocallyEdited = false; + _identityDataChanged = false; } void AvatarData::updateJointMappings() { @@ -2253,10 +2268,12 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent if (_avatarEntityData.size() < MAX_NUM_AVATAR_ENTITIES) { _avatarEntityData.insert(entityID, entityData); _avatarEntityDataLocallyEdited = true; + markIdentityDataChanged(); } } else { itr.value() = entityData; _avatarEntityDataLocallyEdited = true; + markIdentityDataChanged(); } }); } @@ -2270,6 +2287,7 @@ void AvatarData::clearAvatarEntity(const QUuid& entityID) { _avatarEntitiesLock.withWriteLock([&] { _avatarEntityData.remove(entityID); _avatarEntityDataLocallyEdited = true; + markIdentityDataChanged(); }); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index fdf3f0530c..b7877f29df 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -249,6 +249,7 @@ static const float MIN_AVATAR_SCALE = .005f; const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000; +const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_USECS = AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS * USECS_PER_MSEC; // See also static AvatarData::defaultFullAvatarModelUrl(). const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default"); @@ -530,13 +531,14 @@ public: QString displayName; QString sessionDisplayName; AvatarEntityMap avatarEntityData; + quint64 updatedAt; }; - static void parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut, udt::Packet::MessageNumber& messageNumberOut); + static void parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut); // identityChanged returns true if identity has changed, false otherwise. // displayNameChanged returns true if displayName has changed, false otherwise. - void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, udt::Packet::MessageNumber messageNumber); + void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged); QByteArray identityByteArray() const; @@ -546,7 +548,10 @@ public: virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setDisplayName(const QString& displayName); - virtual void setSessionDisplayName(const QString& sessionDisplayName) { _sessionDisplayName = sessionDisplayName; }; + virtual void setSessionDisplayName(const QString& sessionDisplayName) { + _sessionDisplayName = sessionDisplayName; + markIdentityDataChanged(); + } Q_INVOKABLE QVector getAttachmentData() const; Q_INVOKABLE virtual void setAttachmentData(const QVector& attachmentData); @@ -619,6 +624,7 @@ public: static float _avatarSortCoefficientCenter; static float _avatarSortCoefficientAge; + bool getIdentityDataChanged() const { return _identityDataChanged; } // has the identity data changed since the last time sendIdentityPacket() was called signals: @@ -778,7 +784,14 @@ protected: quint64 _audioLoudnessChanged { 0 }; float _audioAverageLoudness { 0.0f }; - udt::Packet::MessageNumber _lastIdentityPacketMessageNumber{ 0 }; + bool _identityDataChanged { false }; + quint64 _identityUpdatedAt { 0 }; + + void markIdentityDataChanged() { + _identityDataChanged = true; + _identityUpdatedAt = usecTimestampNow(); + } + private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index c4cd303360..9d4d96c5d0 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -127,8 +127,7 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer message, SharedNodePointer sendingNode) { AvatarData::Identity identity; - udt::Packet::MessageNumber messageNumber; - AvatarData::parseAvatarIdentityPacket(message, identity, messageNumber); + AvatarData::parseAvatarIdentityPacket(message, identity); // make sure this isn't for an ignored avatar auto nodeList = DependencyManager::get(); @@ -149,7 +148,7 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer auto avatar = newOrExistingAvatar(identity.uuid, sendingNode); bool identityChanged = false; bool displayNameChanged = false; - avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged, messageNumber); + avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged); } } diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 246821908a..67a63bb530 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -56,7 +56,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::StickAndBallDefaultAvatar); + return static_cast(AvatarMixerPacketVersion::IdentityPacketsIncludeUpdateTime); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); case PacketType::ICEServerHeartbeat: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 03a773f24f..bb743f4363 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -229,7 +229,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { ImmediateSessionDisplayNameUpdates, VariableAvatarData, AvatarAsChildFixes, - StickAndBallDefaultAvatar + StickAndBallDefaultAvatar, + IdentityPacketsIncludeUpdateTime }; enum class DomainConnectRequestVersion : PacketVersion { From 382645ddd034429923d7282d96abb45c02e62ad6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 24 Apr 2017 18:01:54 -0700 Subject: [PATCH 013/119] more cleanup --- assignment-client/src/Agent.cpp | 2 +- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 12 ++++-------- interface/src/avatar/MyAvatar.h | 2 -- libraries/avatars/src/AvatarData.cpp | 12 ++++++------ libraries/avatars/src/AvatarData.h | 3 +-- libraries/avatars/src/AvatarHashMap.cpp | 2 +- 7 files changed, 14 insertions(+), 21 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 46e826c596..bb72d679cb 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -537,7 +537,7 @@ void Agent::setIsAvatar(bool isAvatar) { connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket); // start the timers - _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); + _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets // tell the avatarAudioTimer to start ticking emit startAvatarAudioTimer(); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 182b38b124..43eac8ebbe 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -405,7 +405,7 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes // parse the identity packet and update the change timestamp if appropriate AvatarData::Identity identity; - AvatarData::parseAvatarIdentityPacket(message, identity); + AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); bool identityChanged = false; bool displayNameChanged = false; avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c7d6b03f46..59b262a308 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -412,13 +412,9 @@ void MyAvatar::update(float deltaTime) { Q_ARG(glm::vec3, (getPosition() - halfBoundingBoxDimensions)), Q_ARG(glm::vec3, (halfBoundingBoxDimensions*2.0f))); - uint64_t now = usecTimestampNow(); - if (now > _identityPacketExpiry || _avatarEntityDataLocallyEdited) { - _identityPacketExpiry = now + AVATAR_IDENTITY_PACKET_SEND_INTERVAL_USECS; - if (getIdentityDataChanged()) { - qDebug() << __FUNCTION__ << "about to call... sendIdentityPacket(); --- _identityPacketExpiry:" << _identityPacketExpiry << "_avatarEntityDataLocallyEdited:" << _avatarEntityDataLocallyEdited; - sendIdentityPacket(); - } + if (getIdentityDataChanged()) { + qDebug() << __FUNCTION__ << "about to call... sendIdentityPacket();"; + sendIdentityPacket(); } simulate(deltaTime); @@ -1261,7 +1257,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN setSkeletonModelURL(fullAvatarURL); UserActivityLogger::getInstance().changedModel("skeleton", urlString); } - _identityPacketExpiry = 0; // triggers an identity packet next update() + markIdentityDataChanged(); } void MyAvatar::setAttachmentData(const QVector& attachmentData) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 74af44c99a..a201ee54c9 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -701,8 +701,6 @@ private: std::mutex _holdActionsMutex; std::vector _holdActions; - uint64_t _identityPacketExpiry { 0 }; - float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f }; float AUDIO_ENERGY_CONSTANT { 0.000001f }; float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f }; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 491f667067..532e762e0e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include @@ -1455,8 +1454,7 @@ QStringList AvatarData::getJointNames() const { return _jointNames; } -void AvatarData::parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut) { - const QByteArray& data = message->getMessage(); +void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) { QDataStream packetStream(data); packetStream >> identityOut.uuid @@ -1491,9 +1489,6 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC return; } - _identityUpdatedAt = identity.updatedAt; - qDebug() << __FUNCTION__ << "_identityUpdatedAt:" << _identityUpdatedAt; - if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { setSkeletonModelURL(identity.skeletonModelURL); identityChanged = true; @@ -1526,6 +1521,11 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC // flag this avatar as non-stale by updating _averageBytesReceived const int BOGUS_NUM_BYTES = 1; _averageBytesReceived.updateAverage(BOGUS_NUM_BYTES); + + // use the timestamp from this identity, since we want to honor the updated times in "server clock" + // this will overwrite any changes we made locally to this AvatarData's _identityUpdatedAt + _identityUpdatedAt = identity.updatedAt; + qDebug() << __FUNCTION__ << "_identityUpdatedAt:" << _identityUpdatedAt; } QByteArray AvatarData::identityByteArray() const { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b7877f29df..b4d3839d8a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -249,7 +249,6 @@ static const float MIN_AVATAR_SCALE = .005f; const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000; -const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_USECS = AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS * USECS_PER_MSEC; // See also static AvatarData::defaultFullAvatarModelUrl(). const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default"); @@ -534,7 +533,7 @@ public: quint64 updatedAt; }; - static void parseAvatarIdentityPacket(const QSharedPointer& message, Identity& identityOut); + static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut); // identityChanged returns true if identity has changed, false otherwise. // displayNameChanged returns true if displayName has changed, false otherwise. diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 9d4d96c5d0..0d341c684e 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -127,7 +127,7 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer message, SharedNodePointer sendingNode) { AvatarData::Identity identity; - AvatarData::parseAvatarIdentityPacket(message, identity); + AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); // make sure this isn't for an ignored avatar auto nodeList = DependencyManager::get(); From 95a3e0a09295cd6ed83c128f4a0461d97448f3d9 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 24 Apr 2017 18:06:33 -0700 Subject: [PATCH 014/119] fix whitespace --- libraries/networking/src/ResourceCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index ba4de0e8ef..fcf199bec3 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -723,7 +723,7 @@ void Resource::handleReplyFinished() { _attempts++; switch (result) { case ResourceRequest::Result::Timeout: { - qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal ; + qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal; // Fall through to other cases } case ResourceRequest::Result::ServerUnavailable: { From d179135a87568b882a28ded16d1ce5ad3fbbc768 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 24 Apr 2017 18:21:22 -0700 Subject: [PATCH 015/119] remove dead code --- libraries/networking/src/ReceivedMessage.cpp | 6 ++---- libraries/networking/src/ReceivedMessage.h | 5 ----- libraries/networking/src/udt/PacketList.h | 2 -- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/libraries/networking/src/ReceivedMessage.cpp b/libraries/networking/src/ReceivedMessage.cpp index ecf7eb9fde..2c5a11334b 100644 --- a/libraries/networking/src/ReceivedMessage.cpp +++ b/libraries/networking/src/ReceivedMessage.cpp @@ -26,8 +26,7 @@ ReceivedMessage::ReceivedMessage(const NLPacketList& packetList) _sourceID(packetList.getSourceID()), _packetType(packetList.getType()), _packetVersion(packetList.getVersion()), - _senderSockAddr(packetList.getSenderSockAddr()), - _messageNumber(packetList.getMessageNumber()) + _senderSockAddr(packetList.getSenderSockAddr()) { } @@ -39,8 +38,7 @@ ReceivedMessage::ReceivedMessage(NLPacket& packet) _packetType(packet.getType()), _packetVersion(packet.getVersion()), _senderSockAddr(packet.getSenderSockAddr()), - _isComplete(packet.getPacketPosition() == NLPacket::ONLY), - _messageNumber(packet.getMessageNumber()) + _isComplete(packet.getPacketPosition() == NLPacket::ONLY) { } diff --git a/libraries/networking/src/ReceivedMessage.h b/libraries/networking/src/ReceivedMessage.h index 414d9fee52..3acb7163e7 100644 --- a/libraries/networking/src/ReceivedMessage.h +++ b/libraries/networking/src/ReceivedMessage.h @@ -50,8 +50,6 @@ public: qint64 getBytesLeftToRead() const { return _data.size() - _position; } - const udt::Packet::MessageNumber& getMessageNumber() const { return _messageNumber; } - void seek(qint64 position) { _position = position; } qint64 peek(char* data, qint64 size); @@ -100,9 +98,6 @@ private: std::atomic _isComplete { true }; std::atomic _failed { false }; - - const udt::Packet::MessageNumber _messageNumber; // only settable on construction - }; Q_DECLARE_METATYPE(ReceivedMessage*) diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index 9fc914e7fb..5083d77ec9 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -43,8 +43,6 @@ public: size_t getDataSize() const; size_t getMessageSize() const; QByteArray getMessage() const; - MessageNumber getMessageNumber() const { return _messageNumber; } - QByteArray getExtendedHeader() const { return _extendedHeader; } From 5164aa405696d01879a040db90103e1660d4b59f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 25 Apr 2017 18:21:09 -0700 Subject: [PATCH 016/119] hacking --- interface/src/Application.cpp | 1 + libraries/avatars/src/AvatarData.cpp | 3 +++ libraries/avatars/src/AvatarData.h | 11 ++++------- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dfe563b65a..0b8e37c21f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5209,6 +5209,7 @@ void Application::nodeAdded(SharedNodePointer node) const { // new avatar mixer, send off our identity packet right away qDebug() << __FUNCTION__ << "about to call... getMyAvatar()->sendIdentityPacket();"; + getMyAvatar()->markIdentityDataChanged(); getMyAvatar()->sendIdentityPacket(); getMyAvatar()->resetLastSent(); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 532e762e0e..af43b16861 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1489,6 +1489,9 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC return; } + qCDebug(avatars) << __FUNCTION__ << "got identity packet for avatar " << getSessionUUID() + << "identity.updatedAt:" << identity.updatedAt << "_identityUpdatedAt:" << _identityUpdatedAt; + if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { setSkeletonModelURL(identity.skeletonModelURL); identityChanged = true; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b4d3839d8a..7b24ae6cd7 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -624,7 +624,10 @@ public: static float _avatarSortCoefficientAge; bool getIdentityDataChanged() const { return _identityDataChanged; } // has the identity data changed since the last time sendIdentityPacket() was called - + void markIdentityDataChanged() { + _identityDataChanged = true; + _identityUpdatedAt = usecTimestampNow(); + } signals: void displayNameChanged(); @@ -786,12 +789,6 @@ protected: bool _identityDataChanged { false }; quint64 _identityUpdatedAt { 0 }; - void markIdentityDataChanged() { - _identityDataChanged = true; - _identityUpdatedAt = usecTimestampNow(); - } - - private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl; From 105d17e85ef36a70208b1a354b5d3ff6d0f7609f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 31 Mar 2017 15:07:34 -0700 Subject: [PATCH 017/119] Add byte range support to HTTPResourceRequest --- interface/src/Application.cpp | 9 +++++++++ interface/src/ui/overlays/Image3DOverlay.cpp | 1 + libraries/ktx/src/ktx/KTX.h | 2 ++ .../src/model-networking/TextureCache.cpp | 8 +++++++- .../model-networking/src/model-networking/TextureCache.h | 1 + libraries/networking/src/HTTPResourceRequest.cpp | 6 ++++++ libraries/networking/src/ResourceCache.cpp | 2 ++ libraries/networking/src/ResourceCache.h | 1 + libraries/networking/src/ResourceManager.h | 1 + libraries/networking/src/ResourceRequest.h | 9 +++++++++ libraries/script-engine/src/ScriptEngine.cpp | 6 ++++-- 11 files changed, 43 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f51c3d2b46..886487603e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -188,6 +188,7 @@ #include #include #include +#include // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. @@ -604,6 +605,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo { const QString TEST_SCRIPT = "--testScript"; const QString TRACE_FILE = "--traceFile"; + const QString HTTP_PROXY = "--httpProxy"; const QStringList args = arguments(); for (int i = 0; i < args.size() - 1; ++i) { if (args.at(i) == TEST_SCRIPT) { @@ -615,10 +617,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QString traceFilePath = args.at(i + 1); setProperty(hifi::properties::TRACING, traceFilePath); DependencyManager::get()->startTracing(); + } else if (args.at(i) == HTTP_PROXY) { } } } + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName("127.0.0.1"); + proxy.setPort(8888); + QNetworkProxy::setApplicationProxy(proxy); + // make sure the debug draw singleton is initialized on the main thread. DebugDraw::getInstance().removeMarker(""); diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 45d63d9cf1..f06efcef1c 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -53,6 +53,7 @@ void Image3DOverlay::update(float deltatime) { } void Image3DOverlay::render(RenderArgs* args) { + qDebug() << _url; if (!_isLoaded) { _isLoaded = true; _texture = DependencyManager::get()->getTexture(_url); diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 043de573ed..7eab67c0db 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -379,6 +379,8 @@ namespace ktx { void setCubeArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1), NUM_CUBEMAPFACES); } }; + static const size_t KTX_HEADER_SIZE = 64; + static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static"); // Key Values struct KeyValue { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f6e256bb06..45000b30a8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -266,7 +266,8 @@ QSharedPointer TextureCache::createResource(const QUrl& url, const QSh NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) : Resource(url), _type(type), - _maxNumPixels(maxNumPixels) + _maxNumPixels(maxNumPixels), + _sourceIsKTX(url.path().endsWith(".ktx")) { _textureSource = std::make_shared(); @@ -274,6 +275,11 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _loaded = true; } + if (_sourceIsKTX) { + _requestByteRange.fromInclusive = 0; + _requestByteRange.toExclusive = 1000; + } + // if we have content, load it after we have our self pointer if (!content.isEmpty()) { _startedLoading = true; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index d0600c3dce..d0916d61eb 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -73,6 +73,7 @@ private: image::TextureUsage::Type _type; KTXFilePointer _file; + bool _sourceIsKTX { false }; int _originalWidth { 0 }; int _originalHeight { 0 }; int _width { 0 }; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 85da5de5b8..28d0485383 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -59,6 +59,12 @@ void HTTPResourceRequest::doSend() { networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); } + if (_byteRange.isSet()) { + auto byteRange = QString("bytes={}-{}").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + networkRequest.setRawHeader("Range", byteRange.toLatin1()); + } + networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + _reply = NetworkAccessManager::getInstance().get(networkRequest); connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 4031ff8bf7..7fdbc7885b 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -676,6 +676,8 @@ void Resource::makeRequest() { return; } + _request->setByteRange(_requestByteRange); + qCDebug(resourceLog).noquote() << "Starting request for:" << _url.toDisplayString(); emit loading(); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 53ccd2c386..32364bf71c 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -442,6 +442,7 @@ protected: QUrl _url; QUrl _activeUrl; + ByteRange _requestByteRange; bool _startedLoading = false; bool _failedToLoad = false; bool _loaded = false; diff --git a/libraries/networking/src/ResourceManager.h b/libraries/networking/src/ResourceManager.h index 162892abaf..d193c39cae 100644 --- a/libraries/networking/src/ResourceManager.h +++ b/libraries/networking/src/ResourceManager.h @@ -26,6 +26,7 @@ const QString URL_SCHEME_ATP = "atp"; class ResourceManager { public: + static void setUrlPrefixOverride(const QString& prefix, const QString& replacement); static QString normalizeURL(const QString& urlString); static QUrl normalizeURL(const QUrl& url); diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 7588fca046..03b46e715d 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -17,6 +17,13 @@ #include +struct ByteRange { + int64_t fromInclusive { 0 }; + int64_t toExclusive { 0 }; + + bool isSet() { return fromInclusive < -1 || fromInclusive < toExclusive; } +}; + class ResourceRequest : public QObject { Q_OBJECT public: @@ -48,6 +55,7 @@ public: bool loadedFromCache() const { return _loadedFromCache; } void setCacheEnabled(bool value) { _cacheEnabled = value; } + void setByteRange(ByteRange byteRange) { _byteRange = byteRange; } public slots: void send(); @@ -65,6 +73,7 @@ protected: QByteArray _data; bool _cacheEnabled { true }; bool _loadedFromCache { false }; + ByteRange _byteRange; }; #endif diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 067c7c1412..575d23c4c4 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -2317,6 +2317,8 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR if (_entityScripts.contains(entityID)) { const EntityScriptDetails &oldDetails = _entityScripts[entityID]; + auto scriptText = oldDetails.scriptText; + if (isEntityScriptRunning(entityID)) { callEntityScriptMethod(entityID, "unload"); } @@ -2334,14 +2336,14 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldR newDetails.status = EntityScriptStatus::UNLOADED; newDetails.lastModified = QDateTime::currentMSecsSinceEpoch(); // keep scriptText populated for the current need to "debouce" duplicate calls to unloadEntityScript - newDetails.scriptText = oldDetails.scriptText; + newDetails.scriptText = scriptText; setEntityScriptDetails(entityID, newDetails); } stopAllTimersForEntityScript(entityID); { // FIXME: shouldn't have to do this here, but currently something seems to be firing unloads moments after firing initial load requests - processDeferredEntityLoads(oldDetails.scriptText, entityID); + processDeferredEntityLoads(scriptText, entityID); } } } From 00cbfa0f70c0776a0e38739e7e9d692ecfa5e9ec Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 7 Apr 2017 16:48:22 -0700 Subject: [PATCH 018/119] Add start of progressive ktx-loading --- interface/src/ui/overlays/Image3DOverlay.cpp | 1 - libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 8 ++- libraries/gpu/src/gpu/Texture.h | 10 ++- libraries/gpu/src/gpu/Texture_ktx.cpp | 11 +++ libraries/ktx/src/ktx/KTX.cpp | 23 +++++- libraries/ktx/src/ktx/KTX.h | 11 ++- libraries/ktx/src/ktx/Writer.cpp | 62 ++++++++++++++++ .../src/model-networking/TextureCache.cpp | 72 +++++++++++++++++++ .../src/model-networking/TextureCache.h | 8 +++ .../networking/src/HTTPResourceRequest.cpp | 53 +++++++++++++- .../networking/src/NetworkAccessManager.cpp | 10 ++- libraries/networking/src/ResourceRequest.h | 4 ++ libraries/shared/src/shared/Storage.cpp | 1 + libraries/shared/src/shared/Storage.h | 1 + 14 files changed, 264 insertions(+), 11 deletions(-) diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index f06efcef1c..45d63d9cf1 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -53,7 +53,6 @@ void Image3DOverlay::update(float deltatime) { } void Image3DOverlay::render(RenderArgs* args) { - qDebug() << _url; if (!_isLoaded) { _isLoaded = true; _texture = DependencyManager::get()->getTexture(_url); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index a6e6bf4fa3..b7d2ee0b0f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -217,8 +217,12 @@ TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t t _transferSize = mipSize; _bufferingLambda = [=] { auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - _buffer.resize(_transferSize); - memcpy(&_buffer[0], mipData->readData(), _transferSize); + if (!mipData) { + qWarning() << "Mip not available: " << sourceMip; + } else { + _buffer.resize(_transferSize); + memcpy(&_buffer[0], mipData->readData(), _transferSize); + } _bufferingCompleted = true; }; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 2f63bd6719..756748497d 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -28,6 +28,8 @@ namespace ktx { struct KTXDescriptor; using KTXDescriptorPointer = std::unique_ptr; struct Header; + struct KeyValue; + using KeyValues = std::list; } namespace gpu { @@ -503,9 +505,15 @@ public: ExternalUpdates getUpdates() const; - // Textures can be serialized directly to ktx data file, here is how + // Serialize ktx header and keyvalues directly to a file, and return a Texture representing that file + static Texture* serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues); + + // Serialize a texture into a KTX file static ktx::KTXUniquePointer serialize(const Texture& texture); + static TexturePointer unserialize(const std::string& ktxFile, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); + static TexturePointer unserialize(const std::string& ktxFile, const ktx::KTXDescriptor& descriptor, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); + static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 50e9cb6d07..db6808a866 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -207,6 +207,10 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, TextureUsageType } ktx::KTXDescriptor descriptor { ktxPointer->toDescriptor() }; + return unserialize(ktxfile, ktxPointer->toDescriptor(), usageType, usage, sampler); +} + +TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDescriptor& descriptor, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) { const auto& header = descriptor.header; Format mipFormat = Format::COLOR_BGRA_32; @@ -256,6 +260,13 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, TextureUsageType return tex; } +Texture* Texture::serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues) { + // Create a memory-backed KTX object + auto ktxBuffer = ktx::KTX::createBare(header, keyValues); + + return unserialize(ktxfile, ktxBuffer->toDescriptor()); +} + bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 6fca39788b..0580d9a8c3 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -45,7 +45,7 @@ uint32_t Header::evalPixelDepth(uint32_t level) const { } size_t Header::evalPixelSize() const { - return glTypeSize; // Really we should generate the size from the FOrmat etc + return 4;//glTypeSize; // Really we should generate the size from the FOrmat etc } size_t Header::evalRowSize(uint32_t level) const { @@ -70,6 +70,25 @@ size_t Header::evalImageSize(uint32_t level) const { } } +ImageDescriptors Header::generateImageDescriptors() const { + ImageDescriptors descriptors; + + for (auto level = 0; level < numberOfMipmapLevels; ++level) { + ImageHeader header { + numberOfFaces == NUM_CUBEMAPFACES, + static_cast(evalImageSize(level)), + 0 + }; + ImageHeader::FaceOffsets offsets; + for (auto i = 0; i < numberOfFaces; ++i) { + offsets.push_back(0); + } + descriptors.push_back(ImageDescriptor(header, offsets)); + } + + return descriptors; +} + KeyValue::KeyValue(const std::string& key, uint32_t valueByteSize, const Byte* value) : _byteSize((uint32_t) key.size() + 1 + valueByteSize), // keyString size + '\0' ending char + the value size @@ -209,4 +228,4 @@ KTXDescriptor KTX::toDescriptor() const { KTX::KTX(const StoragePointer& storage, const Header& header, const KeyValues& keyValues, const Images& images) : _header(header), _storage(storage), _keyValues(keyValues), _images(images) { -} \ No newline at end of file +} diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 7eab67c0db..fb8927eca0 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -292,6 +292,9 @@ namespace ktx { using Storage = storage::Storage; using StoragePointer = std::shared_ptr; + struct ImageDescriptor; + using ImageDescriptors = std::vector; + // Header struct Header { static const size_t IDENTIFIER_LENGTH = 12; @@ -378,6 +381,7 @@ namespace ktx { void setCube(uint32_t width, uint32_t height) { setDimensions(width, height, 0, 0, NUM_CUBEMAPFACES); } void setCubeArray(uint32_t width, uint32_t height, uint32_t numSlices) { setDimensions(width, height, 0, (numSlices > 0 ? numSlices : 1), NUM_CUBEMAPFACES); } + ImageDescriptors generateImageDescriptors() const; }; static const size_t KTX_HEADER_SIZE = 64; static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static"); @@ -421,14 +425,14 @@ namespace ktx { struct Image; + // Image without the image data itself struct ImageDescriptor : public ImageHeader { const FaceOffsets _faceOffsets; ImageDescriptor(const ImageHeader& header, const FaceOffsets& offsets) : ImageHeader(header), _faceOffsets(offsets) {} Image toImage(const ktx::StoragePointer& storage) const; }; - using ImageDescriptors = std::vector; - + // Image with the image data itself struct Image : public ImageHeader { FaceBytes _faceBytes; Image(const ImageHeader& header, const FaceBytes& faces) : ImageHeader(header), _faceBytes(faces) {} @@ -473,6 +477,7 @@ namespace ktx { // This path allocate the Storage where to store header, keyvalues and copy mips // Then COPY all the data static std::unique_ptr create(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static std::unique_ptr createBare(const Header& header, const KeyValues& keyValues = KeyValues()); // Instead of creating a full Copy of the src data in a KTX object, the write serialization can be performed with the // following two functions @@ -486,7 +491,9 @@ namespace ktx { // // This is exactly what is done in the create function static size_t evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static size_t evalStorageSize(const Header& header, const ImageDescriptors& images, const KeyValues& keyValues = KeyValues()); static size_t write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& images, const KeyValues& keyValues = KeyValues()); + static size_t writeWithoutImages(Byte* destBytes, size_t destByteSize, const Header& header, const ImageDescriptors& descriptors, const KeyValues& keyValues = KeyValues()); static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues); static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 25b363d31b..396aa09f9a 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -40,6 +40,20 @@ namespace ktx { return create(storagePointer); } + std::unique_ptr KTX::createBare(const Header& header, const KeyValues& keyValues) { + auto descriptors = header.generateImageDescriptors(); + + StoragePointer storagePointer; + { + auto storageSize = ktx::KTX::evalStorageSize(header, descriptors, keyValues); + auto memoryStorage = new storage::MemoryStorage(storageSize); + qDebug() << "Memory storage size is: " << storageSize; + ktx::KTX::writeWithoutImages(memoryStorage->data(), memoryStorage->size(), header, descriptors, keyValues); + storagePointer.reset(memoryStorage); + } + return create(storagePointer); + } + size_t KTX::evalStorageSize(const Header& header, const Images& images, const KeyValues& keyValues) { size_t storageSize = sizeof(Header); @@ -59,6 +73,25 @@ namespace ktx { return storageSize; } + size_t KTX::evalStorageSize(const Header& header, const ImageDescriptors& imageDescriptors, const KeyValues& keyValues) { + size_t storageSize = sizeof(Header); + + if (!keyValues.empty()) { + size_t keyValuesSize = KeyValue::serializedKeyValuesByteSize(keyValues); + storageSize += keyValuesSize; + } + + auto numMips = header.getNumberOfLevels(); + for (uint32_t l = 0; l < numMips; l++) { + if (imageDescriptors.size() > l) { + storageSize += sizeof(uint32_t); + storageSize += imageDescriptors[l]._imageSize; + storageSize += Header::evalPadding(imageDescriptors[l]._imageSize); + } + } + return storageSize; + } + size_t KTX::write(Byte* destBytes, size_t destByteSize, const Header& header, const Images& srcImages, const KeyValues& keyValues) { // Check again that we have enough destination capacity if (!destBytes || (destByteSize < evalStorageSize(header, srcImages, keyValues))) { @@ -87,6 +120,35 @@ namespace ktx { return destByteSize; } + size_t KTX::writeWithoutImages(Byte* destBytes, size_t destByteSize, const Header& header, const ImageDescriptors& descriptors, const KeyValues& keyValues) { + // Check again that we have enough destination capacity + if (!destBytes || (destByteSize < evalStorageSize(header, descriptors, keyValues))) { + return 0; + } + + auto currentDestPtr = destBytes; + // Header + auto destHeader = reinterpret_cast(currentDestPtr); + memcpy(currentDestPtr, &header, sizeof(Header)); + currentDestPtr += sizeof(Header); + + // KeyValues + if (!keyValues.empty()) { + destHeader->bytesOfKeyValueData = (uint32_t) writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); + } else { + // Make sure the header contains the right bytesOfKeyValueData size + destHeader->bytesOfKeyValueData = 0; + } + currentDestPtr += destHeader->bytesOfKeyValueData; + + for (int i = 0; i < descriptors.size(); ++i) { + *currentDestPtr = descriptors[i]._imageSize; + currentDestPtr += descriptors[i]._imageSize + sizeof(uint32_t); + } + + return destByteSize; + } + uint32_t KeyValue::writeSerializedKeyAndValue(Byte* destBytes, uint32_t destByteSize, const KeyValue& keyval) { uint32_t keyvalSize = keyval.serializedByteSize(); if (keyvalSize > destByteSize) { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 45000b30a8..6b6fc09975 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -335,6 +335,77 @@ void NetworkTexture::downloadFinished(const QByteArray& data) { } void NetworkTexture::loadContent(const QByteArray& content) { + if (_sourceIsKTX) { + if (_ktxLoadState == LOADING_HEADER) { + // TODO Handle case where we already have the source hash texture on disk + // TODO Handle case where data isn't as large as the ktx header + _ktxLoadState = LOADING_LOWEST_SIX; + auto header = reinterpret_cast(content.data()); + qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); + qDebug() << "Type:" << header->glType; + qDebug() << "TypeSize:" << header->glTypeSize; + qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; + qDebug() << "numberOfFaces:" << header->numberOfFaces; + qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; + auto kvSize = header->bytesOfKeyValueData; + if (kvSize > content.size() - ktx::KTX_HEADER_SIZE) { + qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; + return; + } + + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(content.data()) + ktx::KTX_HEADER_SIZE); + + // Create bare ktx in memory + std::string filename = "test"; + auto memKtx = ktx::KTX::createBare(*header, keyValues); + + + auto textureCache = DependencyManager::get(); + + // Move ktx to file + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { + qCWarning(modelnetworking) << _url << "file cache failed"; + } else { + _file = file; + } + + //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); + gpu::TexturePointer texture; + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), memKtx->toDescriptor())); + texture->setKtxBacking(file->getFilepath()); + + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner + if (textureCache) { + texture = textureCache->cacheTextureByHash(filename, texture); + } + + setImage(texture, header->getPixelWidth(), header->getPixelHeight()); + + + auto desc = memKtx->toDescriptor(); + int numMips = desc.images.size(); + auto numMipsToGet = glm::min(numMips, 6); + auto sizeOfTopMips = 0; + for (int i = 0; i < numMipsToGet; ++i) { + auto& img = desc.images[i]; + sizeOfTopMips += img._imageSize; + } + _requestByteRange.fromInclusive = length - sizeOfTopMips; + _requestByteRange.toExclusive = length; + attemptRequest(); + + } else { + qDebug() << "Got highest 6 mips"; + } + return; + } + QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels)); } @@ -457,6 +528,7 @@ void ImageReader::read() { if (texture && textureCache) { auto memKtx = gpu::Texture::serialize(*texture); + // Move the texture into a memory mapped file if (memKtx) { const char* data = reinterpret_cast(memKtx->_storage->data()); size_t length = memKtx->_storage->size(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index d0916d61eb..40eb29de35 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -72,6 +72,14 @@ private: friend class ImageReader; image::TextureUsage::Type _type; + + enum KTXLoadState { + LOADING_HEADER, + LOADING_LOWEST_SIX, + DONE_LOADING + }; + + KTXLoadState _ktxLoadState { LOADING_HEADER }; KTXFilePointer _file; bool _sourceIsKTX { false }; int _originalWidth { 0 }; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 28d0485383..5932193830 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -60,7 +60,7 @@ void HTTPResourceRequest::doSend() { } if (_byteRange.isSet()) { - auto byteRange = QString("bytes={}-{}").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + auto byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); networkRequest.setRawHeader("Range", byteRange.toLatin1()); } networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); @@ -78,12 +78,61 @@ void HTTPResourceRequest::onRequestFinished() { Q_ASSERT(_reply); cleanupTimer(); - + + // Content-Range headers have the form: + // + // Content-Range: -/ + // Content-Range: -/* + // Content-Range: */ + // + auto parseContentRangeHeader = [](QString contentRangeHeader) -> std::pair { + auto unitRangeParts = contentRangeHeader.split(' '); + if (unitRangeParts.size() != 2) { + return { false, 0 }; + } + + auto rangeSizeParts = unitRangeParts[1].split('/'); + if (rangeSizeParts.size() != 2) { + return { false, 0 }; + } + + auto sizeStr = rangeSizeParts[1]; + if (sizeStr == "*") { + return { true, 0 }; + } else { + bool ok; + auto size = sizeStr.toLong(&ok); + return { ok, size }; + } + }; + switch(_reply->error()) { case QNetworkReply::NoError: _data = _reply->readAll(); _loadedFromCache = _reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); _result = Success; + + if (_byteRange.isSet()) { + auto statusCode = _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 206) { + _rangeRequestSuccessful = true; + auto contentRangeHeader = _reply->rawHeader("Content-Range"); + bool success; + uint64_t size; + std::tie(success, size) = parseContentRangeHeader(contentRangeHeader); + if (success) { + qWarning(networking) << "Total http resource size is: " << size; + _totalSizeOfResource = size; + } else { + qWarning(networking) << "Error parsing content-range header: " << contentRangeHeader; + _totalSizeOfResource = 0; + } + } else { + _rangeRequestSuccessful = false; + _totalSizeOfResource = _data.size(); + } + } + break; case QNetworkReply::TimeoutError: diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index 73096825e0..6895118be5 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -13,12 +13,20 @@ #include "AtpReply.h" #include "NetworkAccessManager.h" +#include QThreadStorage networkAccessManagers; QNetworkAccessManager& NetworkAccessManager::getInstance() { if (!networkAccessManagers.hasLocalData()) { - networkAccessManagers.setLocalData(new QNetworkAccessManager()); + auto nm = new QNetworkAccessManager(); + networkAccessManagers.setLocalData(nm); + + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName("127.0.0.1"); + proxy.setPort(8888); + nm->setProxy(proxy); } return *networkAccessManagers.localData(); diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 03b46e715d..25db34ab0a 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -53,6 +53,8 @@ public: QString getResultString() const; QUrl getUrl() const { return _url; } bool loadedFromCache() const { return _loadedFromCache; } + bool getRangeRequestSuccessful() const { return _rangeRequestSuccessful; } + bool getTotalSizeOfResource() const { return _totalSizeOfResource; } void setCacheEnabled(bool value) { _cacheEnabled = value; } void setByteRange(ByteRange byteRange) { _byteRange = byteRange; } @@ -74,6 +76,8 @@ protected: bool _cacheEnabled { true }; bool _loadedFromCache { false }; ByteRange _byteRange; + bool _rangeRequestSuccessful { false }; + uint64_t _totalSizeOfResource { 0 }; }; #endif diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 3c46347a49..8999caf1e8 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -67,6 +67,7 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u return std::make_shared(filename); } +// Represents a memory mapped file FileStorage::FileStorage(const QString& filename) : _file(filename) { if (_file.open(QFile::ReadOnly)) { _mapped = _file.map(0, _file.size()); diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 306984040f..46f45cfdc5 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -20,6 +20,7 @@ namespace storage { class Storage; using StoragePointer = std::shared_ptr; + // Abstract class to represent memory that stored _somewhere_ (in system memory or in a file, for example) class Storage : public std::enable_shared_from_this { public: virtual ~Storage() {} From 1fec531c68418b6320924cb85c522c4457a9f25f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 10 Apr 2017 07:12:49 -0700 Subject: [PATCH 019/119] Add basic mip writing to gpu::Texture --- interface/src/Application.cpp | 2 +- libraries/gpu/src/gpu/Texture.h | 2 ++ libraries/ktx/src/ktx/Writer.cpp | 9 +++++++-- .../src/model-networking/TextureCache.cpp | 3 ++- libraries/networking/src/ResourceCache.cpp | 1 + 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 886487603e..8703c09497 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -626,7 +626,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo proxy.setType(QNetworkProxy::HttpProxy); proxy.setHostName("127.0.0.1"); proxy.setPort(8888); - QNetworkProxy::setApplicationProxy(proxy); + //QNetworkProxy::setApplicationProxy(proxy); // make sure the debug draw singleton is initialized on the main thread. DebugDraw::getInstance().removeMarker(""); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 756748497d..b01ec8f0bc 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -270,6 +270,7 @@ public: virtual void reset() = 0; virtual PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; virtual Size getMipFaceSize(uint16 level, uint8 face = 0) const = 0; + virtual void assignMipData(uint16 level, const char* data, const size_t length) = 0; virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; @@ -296,6 +297,7 @@ public: void reset() override; PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; Size getMipFaceSize(uint16 level, uint8 face = 0) const override; + void assignMipData(uint16 level, const char* data, const size_t length) override; void assignMipData(uint16 level, const storage::StoragePointer& storage) override; void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; bool isMipAvailable(uint16 level, uint8 face = 0) const override; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 396aa09f9a..e2803a2258 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -141,8 +141,13 @@ namespace ktx { } currentDestPtr += destHeader->bytesOfKeyValueData; - for (int i = 0; i < descriptors.size(); ++i) { - *currentDestPtr = descriptors[i]._imageSize; + for (size_t i = 0; i < descriptors.size(); ++i) { + auto ptr = reinterpret_cast(currentDestPtr); + *ptr = descriptors[i]._imageSize; + ptr++; + for (size_t k = 0; k < descriptors[i]._imageSize/4; k++) { + *(ptr + k) = 0xFFFF0000; + } currentDestPtr += descriptors[i]._imageSize + sizeof(uint32_t); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 6b6fc09975..593a7c163e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -393,7 +393,8 @@ void NetworkTexture::loadContent(const QByteArray& content) { auto numMipsToGet = glm::min(numMips, 6); auto sizeOfTopMips = 0; for (int i = 0; i < numMipsToGet; ++i) { - auto& img = desc.images[i]; + auto mipLevel = numMips - 1 - i; + auto& img = desc.images[mipLevel]; sizeOfTopMips += img._imageSize; } _requestByteRange.fromInclusive = length - sizeOfTopMips; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 7fdbc7885b..8373a9cf99 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -665,6 +665,7 @@ void Resource::makeRequest() { } PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); + qDebug() << "Making request to " << _url << " for byte range " << _requestByteRange.fromInclusive << "-" << _requestByteRange.toExclusive; _request = ResourceManager::createResourceRequest(this, _activeUrl); From ccd9c4697b2bf67f1c6912effcd5f0a40f9144aa Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 11 Apr 2017 22:44:43 -0700 Subject: [PATCH 020/119] Add extended ktx header/high-mip request handling to NetworkTexture --- interface/src/Application.cpp | 2 +- libraries/gpu/src/gpu/Texture.h | 13 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 20 +++ .../src/model-networking/TextureCache.cpp | 160 ++++++++++++++++++ .../src/model-networking/TextureCache.h | 21 +++ .../networking/src/HTTPResourceRequest.cpp | 7 +- libraries/networking/src/ResourceCache.cpp | 60 ++++--- libraries/networking/src/ResourceCache.h | 19 ++- libraries/networking/src/ResourceRequest.h | 2 +- 9 files changed, 257 insertions(+), 47 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8703c09497..886487603e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -626,7 +626,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo proxy.setType(QNetworkProxy::HttpProxy); proxy.setHostName("127.0.0.1"); proxy.setPort(8888); - //QNetworkProxy::setApplicationProxy(proxy); + QNetworkProxy::setApplicationProxy(proxy); // make sure the debug draw singleton is initialized on the main thread. DebugDraw::getInstance().removeMarker(""); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index b01ec8f0bc..e82566b852 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -270,7 +270,6 @@ public: virtual void reset() = 0; virtual PixelsPointer getMipFace(uint16 level, uint8 face = 0) const = 0; virtual Size getMipFaceSize(uint16 level, uint8 face = 0) const = 0; - virtual void assignMipData(uint16 level, const char* data, const size_t length) = 0; virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; @@ -297,7 +296,6 @@ public: void reset() override; PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; Size getMipFaceSize(uint16 level, uint8 face = 0) const override; - void assignMipData(uint16 level, const char* data, const size_t length) override; void assignMipData(uint16 level, const storage::StoragePointer& storage) override; void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; bool isMipAvailable(uint16 level, uint8 face = 0) const override; @@ -313,15 +311,12 @@ public: PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; Size getMipFaceSize(uint16 level, uint8 face = 0) const override; // By convention, all mip levels and faces MUST be populated when using KTX backing - bool isMipAvailable(uint16 level, uint8 face = 0) const override { return true; } + bool isMipAvailable(uint16 level, uint8 face = 0) const override; - void assignMipData(uint16 level, const storage::StoragePointer& storage) override { - throw std::runtime_error("Invalid call"); - } + void assignMipData(uint16 level, const storage::StoragePointer& storage) override; + + void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; - void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override { - throw std::runtime_error("Invalid call"); - } void reset() override { } protected: diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index db6808a866..487d32f91d 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -72,6 +72,25 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { return _ktxDescriptor->getMipFaceTexelsSize(level, face); } + +bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { + auto numLevels = _ktxDescriptor->header.numberOfMipmapLevels; + auto minLevel = 7 > numLevels ? 0 : numLevels - 7; + auto avail = level >= minLevel; + qDebug() << "isMipAvailable: " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; + //return true; + return level > _ktxDescriptor->header.numberOfMipmapLevels - 7; +} + +void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { + throw std::runtime_error("Invalid call"); +} + +void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) { + throw std::runtime_error("Invalid call"); +} + + void Texture::setKtxBacking(const std::string& filename) { // Check the KTX file for validity before using it as backing storage { @@ -86,6 +105,7 @@ void Texture::setKtxBacking(const std::string& filename) { setStorage(newBacking); } + ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { ktx::Header header; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 593a7c163e..f4fe192a4a 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -330,6 +330,166 @@ private: int _maxNumPixels; }; +const uint16_t NetworkTexture::NULL_MIP_LEVEL = std::numeric_limits::max(); +void NetworkTexture::makeRequest() { + if (!_sourceIsKTX) { + Resource::makeRequest(); + return; + } + + // We special-handle ktx requests to run 2 concurrent requests right off the bat + PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); + + if (!_ktxHeaderLoaded) { + qDebug() << ">>> Making request to " << _url << " for header"; + _ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl); + + if (!_ktxHeaderRequest) { + //qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); + ResourceCache::requestCompleted(_self); + finishedLoading(false); + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); + return; + } + + ByteRange range; + range.fromInclusive = 0; + range.toExclusive = 1000; + _ktxHeaderRequest->setByteRange(range); + + //qCDebug(resourceLog).noquote() << "Starting request for:" << _url.toDisplayString(); + emit loading(); + + connect(_ktxHeaderRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxHeaderRequestProgress); + //connect(this, &Resource::onProgress, this, &NetworkTexture::ktxHeaderRequestFinished); + + connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxHeaderRequestFinished); + + _bytesReceived = _bytesTotal = _bytes = 0; + + _ktxHeaderRequest->send(); + } + + startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); +} + +// Load mips in the range [low, high] (inclusive) +void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { + if (_ktxMipRequest) { + return; + } + + bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; + + if (!isHighMipRequest && !_ktxHeaderLoaded) { + return; + } + + _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); + qDebug() << ">>> Making request to " << _url << " for " << low << " to " << high; + + if (isHighMipRequest) { + // This is a special case where we load the high 7 mips + ByteRange range; + range.fromInclusive = -15000; + _ktxMipRequest->setByteRange(range); + } else { + // TODO: Discover range for other mips + } + + connect(_ktxMipRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxMipRequestProgress); + connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxMipRequestFinished); + + _ktxMipRequest->send(); +} + + +void NetworkTexture::ktxHeaderRequestFinished() { + assert(!_ktxHeaderLoaded); + + if (_ktxHeaderRequest->getResult() == ResourceRequest::Success) { + _ktxHeaderLoaded = true; + _ktxHeaderData = _ktxHeaderRequest->getData(); + maybeCreateKTX(); + } else { + handleFailedRequest(_ktxHeaderRequest->getResult()); + } + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; +} + +void NetworkTexture::ktxMipRequestFinished() { + bool isHighMipRequest = _ktxMipLevelRangeInFlight.first == NULL_MIP_LEVEL + && _ktxMipLevelRangeInFlight.second == NULL_MIP_LEVEL; + + if (_ktxMipRequest->getResult() == ResourceRequest::Success) { + _ktxHighMipData = _ktxMipRequest->getData(); + maybeCreateKTX(); + } else { + handleFailedRequest(_ktxHeaderRequest->getResult()); + } + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; +} + +// This is called when the header or top mips have been loaded +void NetworkTexture::maybeCreateKTX() { + qDebug() << "Maybe create ktx..."; + if (_ktxHeaderData.size() > 0 && _ktxHighMipData.size() > 0) { + // create ktx... + auto header = reinterpret_cast(_ktxHeaderData.data()); + + qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); + qDebug() << "Type:" << header->glType; + qDebug() << "TypeSize:" << header->glTypeSize; + qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; + qDebug() << "numberOfFaces:" << header->numberOfFaces; + qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; + auto kvSize = header->bytesOfKeyValueData; + if (kvSize > _ktxHeaderData.size() - ktx::KTX_HEADER_SIZE) { + qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; + return; + } + + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(_ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + + // Create bare ktx in memory + std::string filename = "test"; + auto memKtx = ktx::KTX::createBare(*header, keyValues); + + auto d = const_cast(memKtx->getStorage()->data()); + memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); + + auto textureCache = DependencyManager::get(); + + // Move ktx to file + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { + qCWarning(modelnetworking) << _url << "file cache failed"; + } else { + _file = file; + } + + //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); + gpu::TexturePointer texture; + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), memKtx->toDescriptor())); + texture->setKtxBacking(file->getFilepath()); + + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner + if (textureCache) { + texture = textureCache->cacheTextureByHash(filename, texture); + } + + setImage(texture, header->getPixelWidth(), header->getPixelHeight()); + + } +} + void NetworkTexture::downloadFinished(const QByteArray& data) { loadContent(data); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 40eb29de35..3998ba3408 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -59,7 +59,16 @@ public: signals: void networkTextureCreated(const QWeakPointer& self); +public slots: + void ktxHeaderRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { } + void ktxHeaderRequestFinished(); + + void ktxMipRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { } + void ktxMipRequestFinished(); + protected: + void makeRequest() override; + virtual bool isCacheable() const override { return _loaded; } virtual void downloadFinished(const QByteArray& data) override; @@ -67,6 +76,9 @@ protected: Q_INVOKABLE void loadContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); + void startMipRangeRequest(uint16_t low, uint16_t high); + void maybeCreateKTX(); + private: friend class KTXReader; friend class ImageReader; @@ -79,9 +91,18 @@ private: DONE_LOADING }; + KTXLoadState _ktxLoadState { LOADING_HEADER }; KTXFilePointer _file; + static const uint16_t NULL_MIP_LEVEL; bool _sourceIsKTX { false }; + bool _ktxHeaderLoaded { false }; + std::pair _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; + ResourceRequest* _ktxHeaderRequest { nullptr }; + ResourceRequest* _ktxMipRequest { nullptr }; + QByteArray _ktxHeaderData; + QByteArray _ktxHighMipData; + int _originalWidth { 0 }; int _originalHeight { 0 }; int _width { 0 }; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 5932193830..1bb292a3ef 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -60,7 +60,12 @@ void HTTPResourceRequest::doSend() { } if (_byteRange.isSet()) { - auto byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + QString byteRange; + if (_byteRange.fromInclusive < 0) { + auto byteRange = QString("bytes=%1").arg(_byteRange.fromInclusive); + } else { + auto byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + } networkRequest.setRawHeader("Range", byteRange.toLatin1()); } networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 8373a9cf99..62ea85bc86 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -725,34 +725,7 @@ void Resource::handleReplyFinished() { emit loaded(data); downloadFinished(data); } else { - switch (result) { - case ResourceRequest::Result::Timeout: { - qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal; - // Fall through to other cases - } - case ResourceRequest::Result::ServerUnavailable: { - // retry with increasing delays - const int BASE_DELAY_MS = 1000; - if (_attempts++ < MAX_ATTEMPTS) { - auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts); - - qCDebug(networking).noquote() << "Server unavailable for" << _url << "- may retry in" << waitTime << "ms" - << "if resource is still needed"; - - QTimer::singleShot(waitTime, this, &Resource::attemptRequest); - break; - } - // fall through to final failure - } - default: { - qCDebug(networking) << "Error loading " << _url; - auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError - : QNetworkReply::UnknownNetworkError; - emit failed(error); - finishedLoading(false); - break; - } - } + handleFailedRequest(result); } _request->disconnect(this); @@ -760,6 +733,37 @@ void Resource::handleReplyFinished() { _request = nullptr; } +void Resource::handleFailedRequest(ResourceRequest::Result result) { + switch (result) { + case ResourceRequest::Result::Timeout: { + qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal; + // Fall through to other cases + } + case ResourceRequest::Result::ServerUnavailable: { + // retry with increasing delays + const int BASE_DELAY_MS = 1000; + if (_attempts++ < MAX_ATTEMPTS) { + auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts); + + qCDebug(networking).noquote() << "Server unavailable for" << _url << "- may retry in" << waitTime << "ms" + << "if resource is still needed"; + + QTimer::singleShot(waitTime, this, &Resource::attemptRequest); + break; + } + // fall through to final failure + } + default: { + qCDebug(networking) << "Error loading " << _url; + auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError + : QNetworkReply::UnknownNetworkError; + emit failed(error); + finishedLoading(false); + break; + } + } +} + uint qHash(const QPointer& value, uint seed) { return qHash(value.data(), seed); } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 32364bf71c..e699c3d198 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -424,6 +424,8 @@ protected slots: protected: virtual void init(); + virtual void makeRequest(); + /// Checks whether the resource is cacheable. virtual bool isCacheable() const { return true; } @@ -440,6 +442,8 @@ protected: Q_INVOKABLE void allReferencesCleared(); + void handleFailedRequest(ResourceRequest::Result result); + QUrl _url; QUrl _activeUrl; ByteRange _requestByteRange; @@ -449,8 +453,15 @@ protected: QHash, float> _loadPriorities; QWeakPointer _self; QPointer _cache; + + qint64 _bytesReceived{ 0 }; + qint64 _bytesTotal{ 0 }; + qint64 _bytes{ 0 }; + + int _requestID; + ResourceRequest* _request{ nullptr }; -private slots: +public slots: void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal); void handleReplyFinished(); @@ -460,20 +471,14 @@ private: void setLRUKey(int lruKey) { _lruKey = lruKey; } - void makeRequest(); void retry(); void reinsert(); bool isInScript() const { return _isInScript; } void setInScript(bool isInScript) { _isInScript = isInScript; } - int _requestID; - ResourceRequest* _request{ nullptr }; int _lruKey{ 0 }; QTimer* _replyTimer{ nullptr }; - qint64 _bytesReceived{ 0 }; - qint64 _bytesTotal{ 0 }; - qint64 _bytes{ 0 }; int _attempts{ 0 }; bool _isInScript{ false }; }; diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 25db34ab0a..01ca62cf05 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -21,7 +21,7 @@ struct ByteRange { int64_t fromInclusive { 0 }; int64_t toExclusive { 0 }; - bool isSet() { return fromInclusive < -1 || fromInclusive < toExclusive; } + bool isSet() { return fromInclusive < 0 || fromInclusive < toExclusive; } }; class ResourceRequest : public QObject { From b20fcbfcdb7d0b19cff97295dc27d46f6542efec Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 12 Apr 2017 09:40:08 -0700 Subject: [PATCH 021/119] Add a way to write data to a storage object --- .../gpu-gl/src/gpu/gl/GLBackendPipeline.cpp | 4 +++- libraries/gpu/src/gpu/Texture.cpp | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 4 +++- libraries/ktx/src/ktx/KTX.cpp | 1 + libraries/ktx/src/ktx/KTX.h | 4 +++- libraries/ktx/src/ktx/Writer.cpp | 7 +++++++ .../src/model-networking/TextureCache.cpp | 17 +++++++++++++++-- .../networking/src/HTTPResourceRequest.cpp | 6 ++++++ libraries/networking/src/ResourceCache.cpp | 1 + libraries/shared/src/shared/Storage.cpp | 4 +++- libraries/shared/src/shared/Storage.h | 4 ++++ 11 files changed, 47 insertions(+), 6 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 1d1f92b297..008b658205 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -259,7 +259,9 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(target, to); - (void) CHECK_GL_ERROR(); + if (CHECK_GL_ERROR()) { + qDebug() << "slot: " << slot << ", target: " << target << ", to: " << to; + } _resource._textures[slot] = resourceTexture; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index ebde9d4d27..6a1e5ca699 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -411,6 +411,7 @@ const Element& Texture::getStoredMipFormat() const { } void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) { + // TODO Skip the extra allocation here storage::StoragePointer storage = std::make_shared(size, bytes); assignStoredMip(level, storage); } diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 487d32f91d..1e6fe1115e 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -45,6 +45,7 @@ std::string GPUKTXPayload::KEY { "hifi.gpu" }; KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { { + // We are doing a lot of work here just to get descriptor data ktx::StoragePointer storage { new storage::FileStorage(_filename.c_str()) }; auto ktxPointer = ktx::KTX::create(storage); _ktxDescriptor.reset(new ktx::KTXDescriptor(ktxPointer->toDescriptor())); @@ -74,8 +75,9 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { + return true; auto numLevels = _ktxDescriptor->header.numberOfMipmapLevels; - auto minLevel = 7 > numLevels ? 0 : numLevels - 7; + auto minLevel = 7 > numLevels ? 0 : numLevels - 10; auto avail = level >= minLevel; qDebug() << "isMipAvailable: " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; //return true; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 0580d9a8c3..1f22514226 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -211,6 +211,7 @@ Image ImageDescriptor::toImage(const ktx::StoragePointer& storage) const { FaceBytes faces; faces.resize(_faceOffsets.size()); for (size_t face = 0; face < _numFaces; ++face) { + // TODO Should we be storing pointers to unowned data? faces[face] = storage->data() + _faceOffsets[face]; } // Note, implicit cast of *this to const ImageHeader& diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index fb8927eca0..988c921ce9 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -384,7 +384,7 @@ namespace ktx { ImageDescriptors generateImageDescriptors() const; }; static const size_t KTX_HEADER_SIZE = 64; - static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static"); + static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static and should not change from the spec"); // Key Values struct KeyValue { @@ -497,6 +497,8 @@ namespace ktx { static size_t writeKeyValues(Byte* destBytes, size_t destByteSize, const KeyValues& keyValues); static Images writeImages(Byte* destBytes, size_t destByteSize, const Images& images); + void writeMipData(uint16_t level, const Byte* sourceBytes, size_t source_size); + // Parse a block of memory and create a KTX object from it static std::unique_ptr create(const StoragePointer& src); diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index e2803a2258..0d6cd737b6 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -235,4 +235,11 @@ namespace ktx { return destImages; } + void KTX::writeMipData(uint16_t level, const Byte* sourceBytes, size_t sourceSize) { + Q_ASSERT(level > 0); + Q_ASSERT(level < _images.size()); + Q_ASSERT(sourceSize == _images[level]._imageSize); + + //memcpy(reinterpret_cast(_images[level]._faceBytes[0]), sourceBytes, sourceSize); + } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f4fe192a4a..706c62c8a1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -545,7 +545,6 @@ void NetworkTexture::loadContent(const QByteArray& content) { texture = textureCache->cacheTextureByHash(filename, texture); } - setImage(texture, header->getPixelWidth(), header->getPixelHeight()); auto desc = memKtx->toDescriptor(); @@ -559,10 +558,24 @@ void NetworkTexture::loadContent(const QByteArray& content) { } _requestByteRange.fromInclusive = length - sizeOfTopMips; _requestByteRange.toExclusive = length; - attemptRequest(); + QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection); + + + //texture->setMinMip(desc.images.size() - 1); + setImage(texture, header->getPixelWidth(), header->getPixelHeight()); } else { qDebug() << "Got highest 6 mips"; + + ktx::StoragePointer storage { new storage::FileStorage(QString::fromStdString(_file->getFilepath())) }; + auto data = storage->mutableData(); + auto size = storage->getSize(); + //*data = 'H'; + memcpy(data + _requestByteRange.fromInclusive, content.data(), content.size()); + //getGPUTexture()->setMinMip(getGPUTexture()->getMinMip() - 6); + //auto ktxPointer = ktx::KTX::create(storage); + + //ktxPointer->writeMipData(level, data, size); } return; } diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 1bb292a3ef..499708b12d 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -22,6 +22,7 @@ #include "NetworkLogging.h" HTTPResourceRequest::~HTTPResourceRequest() { + qDebug() << "Cleaning up:" << _url << " " << _byteRange.fromInclusive << "-" << _byteRange.toExclusive; if (_reply) { _reply->disconnect(this); _reply->deleteLater(); @@ -76,9 +77,12 @@ void HTTPResourceRequest::doSend() { connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress); setupTimer(); + qDebug() << "Sent: " << _url; } void HTTPResourceRequest::onRequestFinished() { + qDebug() << "On request finished: " << _url; + Q_ASSERT(_state == InProgress); Q_ASSERT(_reply); @@ -181,6 +185,7 @@ void HTTPResourceRequest::onRequestFinished() { } void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { + qDebug() << "Progress: " << _url; Q_ASSERT(_state == InProgress); // We've received data, so reset the timer @@ -190,6 +195,7 @@ void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesT } void HTTPResourceRequest::onTimeout() { + qDebug() << "Timeout: " << _url << ":" << _reply->isFinished(); Q_ASSERT(_state == InProgress); _reply->disconnect(this); _reply->abort(); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 62ea85bc86..038ee7fb53 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -698,6 +698,7 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota } void Resource::handleReplyFinished() { + qDebug() << "Got response for " << _activeUrl; Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 8999caf1e8..0f2b696a66 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -69,7 +69,8 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u // Represents a memory mapped file FileStorage::FileStorage(const QString& filename) : _file(filename) { - if (_file.open(QFile::ReadOnly)) { + if (_file.open(QFile::ReadWrite)) { + qDebug() << ">>> Opening mmapped file: " << filename; _mapped = _file.map(0, _file.size()); if (_mapped) { _valid = true; @@ -82,6 +83,7 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } FileStorage::~FileStorage() { + qDebug() << ">>> Closing mmapped file: " << _file.fileName(); if (_mapped) { if (!_file.unmap(_mapped)) { throw std::runtime_error("Unable to unmap file"); diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 46f45cfdc5..3983387c15 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -25,6 +25,7 @@ namespace storage { public: virtual ~Storage() {} virtual const uint8_t* data() const = 0; + virtual uint8_t* mutableData() = 0; virtual size_t size() const = 0; virtual operator bool() const { return true; } @@ -42,6 +43,7 @@ namespace storage { MemoryStorage(size_t size, const uint8_t* data = nullptr); const uint8_t* data() const override { return _data.data(); } uint8_t* data() { return _data.data(); } + uint8_t* mutableData() override { return 0; } size_t size() const override { return _data.size(); } operator bool() const override { return true; } private: @@ -58,6 +60,7 @@ namespace storage { FileStorage& operator=(const FileStorage& other) = delete; const uint8_t* data() const override { return _mapped; } + uint8_t* mutableData() override { return _mapped; } size_t size() const override { return _file.size(); } operator bool() const override { return _valid; } private: @@ -70,6 +73,7 @@ namespace storage { public: ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data); const uint8_t* data() const override { return _data; } + uint8_t* mutableData() override { return 0; } size_t size() const override { return _size; } operator bool() const override { return *_owner; } private: From aca7ad27c661a7e11c59bd50cbddee9274ef7c1e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 12 Apr 2017 17:01:13 -0700 Subject: [PATCH 022/119] Fix ktx dependencies --- .../gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 1 + libraries/gpu/src/gpu/Texture.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 4 ++-- .../model-networking/src/model-networking/TextureCache.cpp | 6 ++++-- .../model-networking/src/model-networking/TextureCache.h | 3 +++ libraries/procedural/CMakeLists.txt | 2 +- libraries/script-engine/CMakeLists.txt | 2 +- plugins/openvr/CMakeLists.txt | 2 +- 8 files changed, 14 insertions(+), 7 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index a453d4207d..f87240f9d0 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -171,6 +171,7 @@ void GL45ResourceTexture::populateTransferQueue() { auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); for (uint8_t face = 0; face < maxFace; ++face) { + qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { continue; } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index e82566b852..832be610ba 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -321,6 +321,7 @@ public: protected: std::string _filename; + //storage::FileStorage _cacheFile; ktx::KTXDescriptorPointer _ktxDescriptor; friend class Texture; }; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 1e6fe1115e..306d72e58a 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -60,6 +60,7 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { } PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { + qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face; storage::StoragePointer result; auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); @@ -75,11 +76,10 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { - return true; auto numLevels = _ktxDescriptor->header.numberOfMipmapLevels; auto minLevel = 7 > numLevels ? 0 : numLevels - 10; auto avail = level >= minLevel; - qDebug() << "isMipAvailable: " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; + qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; //return true; return level > _ktxDescriptor->header.numberOfMipmapLevels - 7; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 706c62c8a1..404c44d454 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -30,7 +30,6 @@ #include -#include #include @@ -473,9 +472,11 @@ void NetworkTexture::maybeCreateKTX() { _file = file; } + _ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); + //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); gpu::TexturePointer texture; - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), memKtx->toDescriptor())); + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), *_ktxDescriptor)); texture->setKtxBacking(file->getFilepath()); // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different @@ -496,6 +497,7 @@ void NetworkTexture::downloadFinished(const QByteArray& data) { void NetworkTexture::loadContent(const QByteArray& content) { if (_sourceIsKTX) { + assert(false); if (_ktxLoadState == LOADING_HEADER) { // TODO Handle case where we already have the source hash texture on disk // TODO Handle case where data isn't as large as the ktx header diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 3998ba3408..a86c20c145 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "KTXCache.h" @@ -102,6 +103,8 @@ private: ResourceRequest* _ktxMipRequest { nullptr }; QByteArray _ktxHeaderData; QByteArray _ktxHighMipData; + ktx::KTXDescriptorPointer _ktxDescriptor; + int _originalWidth { 0 }; int _originalHeight { 0 }; diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt index 8c66442c59..3ebd0f3d14 100644 --- a/libraries/procedural/CMakeLists.txt +++ b/libraries/procedural/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME procedural) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared gpu gpu-gl networking model model-networking image) +link_hifi_libraries(shared gpu gpu-gl networking model model-networking ktx image) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 7b176a6973..39338fd767 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -16,6 +16,6 @@ if (NOT ANDROID) endif () -link_hifi_libraries(shared networking octree gpu ui procedural model model-networking recording avatars fbx entities controllers animation audio physics image) +link_hifi_libraries(shared networking octree gpu ui procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image) # ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit include_hifi_library_headers(gl) diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 2300a38e56..bc62117e70 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -13,7 +13,7 @@ if (WIN32) setup_hifi_plugin(OpenGL Script Qml Widgets) link_hifi_libraries(shared gl networking controllers ui plugins display-plugins ui-plugins input-plugins script-engine - render-utils model gpu gpu-gl render model-networking fbx image) + render-utils model gpu gpu-gl render model-networking fbx ktx image) include_hifi_library_headers(octree) From cf3dc12542452310b48bc48b43917cce1032f8a7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 13 Apr 2017 15:54:25 -0700 Subject: [PATCH 023/119] Add object labels to GL objects and add ktx min mip kv --- .../src/gpu/gl45/GL45BackendTexture.cpp | 2 ++ .../gpu/gl45/GL45BackendVariableTexture.cpp | 35 +++++++++++++++---- libraries/gpu/src/gpu/Texture.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 20 +++++++---- libraries/ktx/src/ktx/KTX.h | 3 ++ libraries/ktx/src/ktx/Writer.cpp | 16 +++++++-- .../src/model-networking/TextureCache.cpp | 4 +-- .../src/model-networking/TextureCache.h | 5 +++ .../networking/src/HTTPResourceRequest.cpp | 5 +-- 9 files changed, 72 insertions(+), 19 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index c6f1ef41ae..a539b76b6c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -109,6 +109,8 @@ GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& GLuint GL45Texture::allocate(const Texture& texture) { GLuint result; glCreateTextures(getGLTextureType(texture), 1, &result); + auto source = texture.source(); + glObjectLabel(GL_TEXTURE, result, source.length(), source.data()); return result; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index f87240f9d0..320d694473 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -41,16 +41,25 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { + if (texture.source().find_first_of("box.ktx") != std::string::npos) { + qDebug() << "In box.ktx ctor"; + } auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; + + _maxAllocatedMip = _populatedMip = mipLevels; + uvec3 mipDimensions; for (uint16_t mip = 0; mip < mipLevels; ++mip) { - if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) + && texture.isStoredMipFaceAvailable(mip)) { _maxAllocatedMip = _populatedMip = mip; break; } } + //_maxAllocatedMip = _populatedMip = mipLevels; + //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); allocateStorage(allocatedMip); copyMipsFromTexture(); @@ -87,6 +96,10 @@ void GL45ResourceTexture::copyMipsFromTexture() { void GL45ResourceTexture::syncSampler() const { Parent::syncSampler(); + qDebug() << "glTextureParameteri " << QString::fromStdString(_source) << _populatedMip << _populatedMip - _allocatedMip; + if (_source == "test" && _populatedMip == 0) { + qDebug() << "here"; + } glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); } @@ -97,6 +110,7 @@ void GL45ResourceTexture::promote() { auto oldSize = _size; // create new texture const_cast(_id) = allocate(_gpuObject); + //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); @@ -130,6 +144,7 @@ void GL45ResourceTexture::demote() { auto oldId = _id; auto oldSize = _size; const_cast(_id) = allocate(_gpuObject); + //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); uint16_t mips = _gpuObject.getNumMips(); @@ -166,20 +181,24 @@ void GL45ResourceTexture::populateTransferQueue() { const uint8_t maxFace = GLTexture::getFaceCount(_target); uint16_t sourceMip = _populatedMip; + qDebug() << "populateTransferQueue info : " << _populatedMip << " " << _maxAllocatedMip << " " << _allocatedMip; do { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); + bool transferQueued = false; + qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; for (uint8_t face = 0; face < maxFace; ++face) { - qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { continue; } // If the mip is less than the max transfer size, then just do it in one transfer if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { + qDebug() << "mip is less than max transfer size"; // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); + transferQueued = true; continue; } @@ -191,18 +210,22 @@ void GL45ResourceTexture::populateTransferQueue() { Q_ASSERT(0 == (mipSize % lines)); uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); uint32_t lineOffset = 0; + qDebug() << "queing up single line transfers " << linesPerTransfer << " " << lineOffset; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; + transferQueued = true; } } // queue up the sampler and populated mip change for after the transfer has completed - _pendingTransfers.emplace(new TransferJob(*this, [=] { - _populatedMip = sourceMip; - syncSampler(); - })); + if (transferQueued) { + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); + } } while (sourceMip != _allocatedMip); } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 832be610ba..83f1f154d3 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -321,6 +321,7 @@ public: protected: std::string _filename; + uint8_t _minMipLevelAvailable; //storage::FileStorage _cacheFile; ktx::KTXDescriptorPointer _ktxDescriptor; friend class Texture; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 306d72e58a..65dc48f634 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -30,7 +30,7 @@ struct GPUKTXPayload { } static bool findInKeyValues(const ktx::KeyValues& keyValues, GPUKTXPayload& payload) { - auto found = std::find_if(keyValues.begin(), keyValues.end(), isGPUKTX); + auto found = std::find_if(keyValues.begin(), keyValues.end(), isGPUKTX); if (found != keyValues.end()) { if ((*found)._value.size() == sizeof(GPUKTXPayload)) { memcpy(&payload, (*found)._value.data(), sizeof(GPUKTXPayload)); @@ -41,14 +41,23 @@ struct GPUKTXPayload { } }; -std::string GPUKTXPayload::KEY { "hifi.gpu" }; +std::string GPUKTXPayload::KEY{ "hifi.gpu" }; KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { { // We are doing a lot of work here just to get descriptor data - ktx::StoragePointer storage { new storage::FileStorage(_filename.c_str()) }; + ktx::StoragePointer storage{ new storage::FileStorage(_filename.c_str()) }; auto ktxPointer = ktx::KTX::create(storage); _ktxDescriptor.reset(new ktx::KTXDescriptor(ktxPointer->toDescriptor())); + auto& keyValues = _ktxDescriptor->keyValues; + auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { + return val._key.compare(ktx::HIFI_MIN_POPULATED_MIP_KEY) == 0; + }); + if (found != keyValues.end()) { + _minMipLevelAvailable = found->_value[0]; + } else { + _minMipLevelAvailable = 4;// _ktxDescriptor->header.numberOfMipmapLevels; + } } // now that we know the ktx, let's get the header info to configure this Texture::Storage: @@ -76,12 +85,11 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { - auto numLevels = _ktxDescriptor->header.numberOfMipmapLevels; - auto minLevel = 7 > numLevels ? 0 : numLevels - 10; + auto minLevel = _minMipLevelAvailable; auto avail = level >= minLevel; qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; //return true; - return level > _ktxDescriptor->header.numberOfMipmapLevels - 7; + return avail; } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 988c921ce9..8b5a62ebb3 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -70,6 +70,9 @@ end namespace ktx { + const std::string HIFI_MIN_POPULATED_MIP_KEY = "hifiMinMip"; + + const uint32_t PACKING_SIZE { sizeof(uint32_t) }; using Byte = uint8_t; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 0d6cd737b6..20d4d598a9 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -43,12 +43,19 @@ namespace ktx { std::unique_ptr KTX::createBare(const Header& header, const KeyValues& keyValues) { auto descriptors = header.generateImageDescriptors(); + auto newHeader = header; + + Byte minMip = header.numberOfMipmapLevels - 6; + auto newKeyValues = keyValues; + //newKeyValues.emplace_back(KeyValue(HIFI_MIN_POPULATED_MIP_KEY, sizeof(Byte), &minMip)); + //newHeader.bytesOfKeyValueData = KeyValue::serializedKeyValuesByteSize(newKeyValues); + StoragePointer storagePointer; { - auto storageSize = ktx::KTX::evalStorageSize(header, descriptors, keyValues); + auto storageSize = ktx::KTX::evalStorageSize(header, descriptors, newKeyValues); auto memoryStorage = new storage::MemoryStorage(storageSize); qDebug() << "Memory storage size is: " << storageSize; - ktx::KTX::writeWithoutImages(memoryStorage->data(), memoryStorage->size(), header, descriptors, keyValues); + ktx::KTX::writeWithoutImages(memoryStorage->data(), memoryStorage->size(), header, descriptors, newKeyValues); storagePointer.reset(memoryStorage); } return create(storagePointer); @@ -132,6 +139,7 @@ namespace ktx { memcpy(currentDestPtr, &header, sizeof(Header)); currentDestPtr += sizeof(Header); + // KeyValues if (!keyValues.empty()) { destHeader->bytesOfKeyValueData = (uint32_t) writeKeyValues(currentDestPtr, destByteSize - sizeof(Header), keyValues); @@ -145,9 +153,11 @@ namespace ktx { auto ptr = reinterpret_cast(currentDestPtr); *ptr = descriptors[i]._imageSize; ptr++; +#ifdef DEBUG for (size_t k = 0; k < descriptors[i]._imageSize/4; k++) { - *(ptr + k) = 0xFFFF0000; + *(ptr + k) = 0xFFFF00FF; } +#endif currentDestPtr += descriptors[i]._imageSize + sizeof(uint32_t); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 404c44d454..26691db083 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -425,7 +425,7 @@ void NetworkTexture::ktxMipRequestFinished() { _ktxHighMipData = _ktxMipRequest->getData(); maybeCreateKTX(); } else { - handleFailedRequest(_ktxHeaderRequest->getResult()); + handleFailedRequest(_ktxMipRequest->getResult()); } _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; @@ -433,7 +433,6 @@ void NetworkTexture::ktxMipRequestFinished() { // This is called when the header or top mips have been loaded void NetworkTexture::maybeCreateKTX() { - qDebug() << "Maybe create ktx..."; if (_ktxHeaderData.size() > 0 && _ktxHighMipData.size() > 0) { // create ktx... auto header = reinterpret_cast(_ktxHeaderData.data()); @@ -478,6 +477,7 @@ void NetworkTexture::maybeCreateKTX() { gpu::TexturePointer texture; texture.reset(gpu::Texture::unserialize(_file->getFilepath(), *_ktxDescriptor)); texture->setKtxBacking(file->getFilepath()); + texture->setSource(filename); // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index a86c20c145..a029b5b147 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -103,6 +103,11 @@ private: ResourceRequest* _ktxMipRequest { nullptr }; QByteArray _ktxHeaderData; QByteArray _ktxHighMipData; + + // This is a copy of the original KTX descriptor from the source url. + // We need this because the KTX that will be cached will likely include extra data + // in its key/value data, and so will not match up with the original, causing + // mip offsets to change. ktx::KTXDescriptorPointer _ktxDescriptor; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 499708b12d..f07ab4450b 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -63,10 +63,11 @@ void HTTPResourceRequest::doSend() { if (_byteRange.isSet()) { QString byteRange; if (_byteRange.fromInclusive < 0) { - auto byteRange = QString("bytes=%1").arg(_byteRange.fromInclusive); + byteRange = QString("bytes=%1").arg(_byteRange.fromInclusive); } else { - auto byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); } + qDebug() << "Setting http range to " << byteRange; networkRequest.setRawHeader("Range", byteRange.toLatin1()); } networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); From e9bb895bff8c122fa5f73dee722e92dfa835ff68 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 13 Apr 2017 17:03:59 -0700 Subject: [PATCH 024/119] Implement KTXStorage::assignMipData and add writing of mips to TextureCache --- libraries/gpu/src/gpu/Texture.cpp | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 30 +++++++++++++++++-- libraries/ktx/src/ktx/Writer.cpp | 6 ++-- .../src/model-networking/TextureCache.cpp | 18 +++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 6a1e5ca699..205cf3a65a 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -118,6 +118,7 @@ Texture::Size Texture::getAllowedGPUMemoryUsage() { return _allowedCPUMemoryUsage; } + void Texture::setAllowedGPUMemoryUsage(Size size) { qCDebug(gpulogging) << "New MAX texture memory " << BYTES_TO_MB(size) << " MB"; _allowedCPUMemoryUsage = size; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 65dc48f634..ede1f21c1f 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -56,7 +56,8 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { if (found != keyValues.end()) { _minMipLevelAvailable = found->_value[0]; } else { - _minMipLevelAvailable = 4;// _ktxDescriptor->header.numberOfMipmapLevels; + // Assume all mip levels are available + _minMipLevelAvailable = 0; } } @@ -93,7 +94,32 @@ bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { - throw std::runtime_error("Invalid call"); + if (level != _minMipLevelAvailable - 1) { + qWarning() << "Invalid level to be stored"; + return; + } + + if (level >= _ktxDescriptor->images.size()) { + throw std::runtime_error("Invalid level"); + } + + if (storage->size() != _ktxDescriptor->images[level]._imageSize) { + throw std::runtime_error("Invalid image size for level"); + } + + + ktx::StoragePointer file { new storage::FileStorage(_filename.c_str()) }; + auto data = file->mutableData(); + data += file->size(); + + // TODO Cache this data inside Image or ImageDescriptor? + for (auto i = _ktxDescriptor->header.numberOfMipmapLevels - 1; i >= level; --i) { + data -= _ktxDescriptor->images[i]._imageSize; + data -= 4; + } + data += 4; + memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); + _minMipLevelAvailable = level; } void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) { diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 20d4d598a9..d149d559f9 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -45,10 +45,10 @@ namespace ktx { auto newHeader = header; - Byte minMip = header.numberOfMipmapLevels - 6; + Byte minMip = header.numberOfMipmapLevels; auto newKeyValues = keyValues; - //newKeyValues.emplace_back(KeyValue(HIFI_MIN_POPULATED_MIP_KEY, sizeof(Byte), &minMip)); - //newHeader.bytesOfKeyValueData = KeyValue::serializedKeyValuesByteSize(newKeyValues); + newKeyValues.emplace_back(KeyValue(HIFI_MIN_POPULATED_MIP_KEY, sizeof(Byte), &minMip)); + newHeader.bytesOfKeyValueData = KeyValue::serializedKeyValuesByteSize(newKeyValues); StoragePointer storagePointer; { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 26691db083..93a25330b1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -387,6 +387,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); qDebug() << ">>> Making request to " << _url << " for " << low << " to " << high; + _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { // This is a special case where we load the high 7 mips ByteRange range; @@ -479,6 +480,23 @@ void NetworkTexture::maybeCreateKTX() { texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); + auto& images = _ktxDescriptor->images; + size_t imageSizeRemaining = _ktxHighMipData.size(); + uint8_t* ktxData = reinterpret_cast(_ktxHighMipData.data()); + ktxData += _ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + for (uint16_t i = images.size() - 1; i >= 0; --i) { + auto& image = images[i]; + if (image._imageSize > imageSizeRemaining) { + break; + } + qDebug() << "Transferring " << i; + ktxData -= image._imageSize; + texture->assignStoredMip(i, image._imageSize, ktxData); + ktxData -= 4; + imageSizeRemaining - image._imageSize - 4; + } + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner From 39c3fee838acde023a40a5dc80ab66816d57f42a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 13 Apr 2017 23:05:23 -0700 Subject: [PATCH 025/119] Add caching of imageOffset to ktx ImageDescriptor --- libraries/gpu/src/gpu/Texture_ktx.cpp | 9 +- libraries/ktx/src/ktx/KTX.cpp | 8 +- libraries/ktx/src/ktx/KTX.h | 15 +- libraries/ktx/src/ktx/Reader.cpp | 5 +- libraries/ktx/src/ktx/Writer.cpp | 5 +- .../src/model-networking/TextureCache.cpp | 135 ++++++------------ .../src/model-networking/TextureCache.h | 3 +- .../networking/src/HTTPResourceRequest.cpp | 3 +- 8 files changed, 79 insertions(+), 104 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index ede1f21c1f..d9a0348e54 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -108,7 +108,8 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor } - ktx::StoragePointer file { new storage::FileStorage(_filename.c_str()) }; + auto fileStorage = new storage::FileStorage(_filename.c_str()); + ktx::StoragePointer file { fileStorage }; auto data = file->mutableData(); data += file->size(); @@ -197,19 +198,21 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { header.numberOfMipmapLevels = texture.getNumMips(); ktx::Images images; + uint32_t imageOffset = 0; for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { auto mip = texture.accessStoredMipFace(level); if (mip) { if (numFaces == 1) { - images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData())); + images.emplace_back(ktx::Image(imageOffset, (uint32_t)mip->getSize(), 0, mip->readData())); } else { ktx::Image::FaceBytes cubeFaces(Texture::CUBE_FACE_COUNT); cubeFaces[0] = mip->readData(); for (uint32_t face = 1; face < Texture::CUBE_FACE_COUNT; face++) { cubeFaces[face] = texture.accessStoredMipFace(level, face)->readData(); } - images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, cubeFaces)); + images.emplace_back(ktx::Image(imageOffset, (uint32_t)mip->getSize(), 0, cubeFaces)); } + imageOffset += mip->getSize() + 4; } } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 1f22514226..73751bdef2 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -73,12 +73,18 @@ size_t Header::evalImageSize(uint32_t level) const { ImageDescriptors Header::generateImageDescriptors() const { ImageDescriptors descriptors; + uint32_t imageOffset = 0; for (auto level = 0; level < numberOfMipmapLevels; ++level) { + auto imageSize = static_cast(evalImageSize(level)); ImageHeader header { numberOfFaces == NUM_CUBEMAPFACES, - static_cast(evalImageSize(level)), + imageOffset, + imageSize, 0 }; + + imageOffset += imageSize + 4; + ImageHeader::FaceOffsets offsets; for (auto i = 0; i < numberOfFaces; ++i) { offsets.push_back(0); diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 8b5a62ebb3..7056f22ba8 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -414,12 +414,17 @@ namespace ktx { struct ImageHeader { using FaceOffsets = std::vector; using FaceBytes = std::vector; + // This is the byte offset from the _start_ of the image region. For example, level 0 + // will have a byte offset of 0. + const uint32_t _imageOffset; + const uint32_t _numFaces; const uint32_t _imageSize; const uint32_t _faceSize; const uint32_t _padding; - ImageHeader(bool cube, uint32_t imageSize, uint32_t padding) : + ImageHeader(bool cube, uint32_t imageOffset, uint32_t imageSize, uint32_t padding) : _numFaces(cube ? NUM_CUBEMAPFACES : 1), + _imageOffset(imageOffset), _imageSize(imageSize * _numFaces), _faceSize(imageSize), _padding(padding) { @@ -439,11 +444,11 @@ namespace ktx { struct Image : public ImageHeader { FaceBytes _faceBytes; Image(const ImageHeader& header, const FaceBytes& faces) : ImageHeader(header), _faceBytes(faces) {} - Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) : - ImageHeader(false, imageSize, padding), + Image(uint32_t imageOffset, uint32_t imageSize, uint32_t padding, const Byte* bytes) : + ImageHeader(false, imageOffset, imageSize, padding), _faceBytes(1, bytes) {} - Image(uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : - ImageHeader(true, pageSize, padding) + Image(uint32_t imageOffset, uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : + ImageHeader(true, imageOffset, pageSize, padding) { if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) { _faceBytes = cubeFaceBytes; diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index bf72faeba5..b22f262e85 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -144,6 +144,7 @@ namespace ktx { while ((currentPtr - srcBytes) + sizeof(uint32_t) <= (srcSize)) { // Grab the imageSize coming up + uint32_t imageOffset = currentPtr - srcBytes; size_t imageSize = *reinterpret_cast(currentPtr); currentPtr += sizeof(uint32_t); @@ -158,10 +159,10 @@ namespace ktx { faces[face] = currentPtr; currentPtr += faceSize; } - images.emplace_back(Image((uint32_t) faceSize, padding, faces)); + images.emplace_back(Image(imageOffset, (uint32_t) faceSize, padding, faces)); currentPtr += padding; } else { - images.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); + images.emplace_back(Image(imageOffset, (uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize + padding; } } else { diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index d149d559f9..4596bf00c0 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -211,6 +211,7 @@ namespace ktx { for (uint32_t l = 0; l < srcImages.size(); l++) { if (currentDataSize + sizeof(uint32_t) < allocatedImagesDataSize) { + uint32_t imageOffset = currentPtr - destBytes; size_t imageSize = srcImages[l]._imageSize; *(reinterpret_cast (currentPtr)) = (uint32_t) imageSize; currentPtr += sizeof(uint32_t); @@ -223,7 +224,7 @@ namespace ktx { // Single face vs cubes if (srcImages[l]._numFaces == 1) { memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize); - destImages.emplace_back(Image((uint32_t) imageSize, padding, currentPtr)); + destImages.emplace_back(Image(imageOffset, (uint32_t) imageSize, padding, currentPtr)); currentPtr += imageSize; } else { Image::FaceBytes faceBytes(NUM_CUBEMAPFACES); @@ -233,7 +234,7 @@ namespace ktx { faceBytes[face] = currentPtr; currentPtr += faceSize; } - destImages.emplace_back(Image(faceSize, padding, faceBytes)); + destImages.emplace_back(Image(imageOffset, faceSize, padding, faceBytes)); } currentPtr += padding; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 93a25330b1..592413e2bc 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -30,7 +30,6 @@ #include - #include #include @@ -395,6 +394,12 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { _ktxMipRequest->setByteRange(range); } else { // TODO: Discover range for other mips + ByteRange range; + range.fromInclusive = ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + + _ktxDescriptor->images[low]._imageOffset + 4; + range.toExclusive = ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + + _ktxDescriptor->images[high + 1]._imageOffset; + _ktxMipRequest->setByteRange(range); } connect(_ktxMipRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxMipRequestProgress); @@ -423,8 +428,17 @@ void NetworkTexture::ktxMipRequestFinished() { && _ktxMipLevelRangeInFlight.second == NULL_MIP_LEVEL; if (_ktxMipRequest->getResult() == ResourceRequest::Success) { - _ktxHighMipData = _ktxMipRequest->getData(); - maybeCreateKTX(); + if (_initialKtxLoaded) { + assert(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + + _textureSource->getGPUTexture()->assignStoredMip(_ktxMipLevelRangeInFlight.first, + _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); + //texture->assignStoredMip(level, image._imageSize, ktxData); + } else { + _ktxHighMipData = _ktxMipRequest->getData(); + maybeCreateKTX(); + } + } else { handleFailedRequest(_ktxMipRequest->getResult()); } @@ -457,7 +471,7 @@ void NetworkTexture::maybeCreateKTX() { auto memKtx = ktx::KTX::createBare(*header, keyValues); auto d = const_cast(memKtx->getStorage()->data()); - memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); + ///memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); auto textureCache = DependencyManager::get(); @@ -485,18 +499,21 @@ void NetworkTexture::maybeCreateKTX() { uint8_t* ktxData = reinterpret_cast(_ktxHighMipData.data()); ktxData += _ktxHighMipData.size(); // TODO Move image offset calculation to ktx ImageDescriptor - for (uint16_t i = images.size() - 1; i >= 0; --i) { - auto& image = images[i]; + uint16_t level; + for (level = images.size() - 1; level >= 0; --level) { + auto& image = images[level]; if (image._imageSize > imageSizeRemaining) { break; } - qDebug() << "Transferring " << i; + qDebug() << "Transferring " << level; ktxData -= image._imageSize; - texture->assignStoredMip(i, image._imageSize, ktxData); + texture->assignStoredMip(level, image._imageSize, ktxData); ktxData -= 4; imageSizeRemaining - image._imageSize - 4; } + _initialKtxLoaded = true; + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner @@ -506,6 +523,27 @@ void NetworkTexture::maybeCreateKTX() { setImage(texture, header->getPixelWidth(), header->getPixelHeight()); + + // Force load the next two levels + { + QTimer* timer = new QTimer(); + connect(timer, &QTimer::timeout, this, [=]() { + startMipRangeRequest(level, level); + }); + timer->setSingleShot(true); + timer->setInterval(4000); + timer->start(); + } + + { + QTimer* timer = new QTimer(); + connect(timer, &QTimer::timeout, this, [=]() { + startMipRangeRequest(level - 1, level - 1); + }); + timer->setSingleShot(true); + timer->setInterval(6000); + timer->start(); + } } } @@ -516,87 +554,6 @@ void NetworkTexture::downloadFinished(const QByteArray& data) { void NetworkTexture::loadContent(const QByteArray& content) { if (_sourceIsKTX) { assert(false); - if (_ktxLoadState == LOADING_HEADER) { - // TODO Handle case where we already have the source hash texture on disk - // TODO Handle case where data isn't as large as the ktx header - _ktxLoadState = LOADING_LOWEST_SIX; - auto header = reinterpret_cast(content.data()); - qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); - qDebug() << "Type:" << header->glType; - qDebug() << "TypeSize:" << header->glTypeSize; - qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; - qDebug() << "numberOfFaces:" << header->numberOfFaces; - qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; - auto kvSize = header->bytesOfKeyValueData; - if (kvSize > content.size() - ktx::KTX_HEADER_SIZE) { - qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; - return; - } - - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(content.data()) + ktx::KTX_HEADER_SIZE); - - // Create bare ktx in memory - std::string filename = "test"; - auto memKtx = ktx::KTX::createBare(*header, keyValues); - - - auto textureCache = DependencyManager::get(); - - // Move ktx to file - const char* data = reinterpret_cast(memKtx->_storage->data()); - size_t length = memKtx->_storage->size(); - KTXFilePointer file; - auto& ktxCache = textureCache->_ktxCache; - if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << "file cache failed"; - } else { - _file = file; - } - - //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); - gpu::TexturePointer texture; - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), memKtx->toDescriptor())); - texture->setKtxBacking(file->getFilepath()); - - // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different - // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will - // be the winner - if (textureCache) { - texture = textureCache->cacheTextureByHash(filename, texture); - } - - - - auto desc = memKtx->toDescriptor(); - int numMips = desc.images.size(); - auto numMipsToGet = glm::min(numMips, 6); - auto sizeOfTopMips = 0; - for (int i = 0; i < numMipsToGet; ++i) { - auto mipLevel = numMips - 1 - i; - auto& img = desc.images[mipLevel]; - sizeOfTopMips += img._imageSize; - } - _requestByteRange.fromInclusive = length - sizeOfTopMips; - _requestByteRange.toExclusive = length; - QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection); - - - //texture->setMinMip(desc.images.size() - 1); - setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - - } else { - qDebug() << "Got highest 6 mips"; - - ktx::StoragePointer storage { new storage::FileStorage(QString::fromStdString(_file->getFilepath())) }; - auto data = storage->mutableData(); - auto size = storage->getSize(); - //*data = 'H'; - memcpy(data + _requestByteRange.fromInclusive, content.data(), content.size()); - //getGPUTexture()->setMinMip(getGPUTexture()->getMinMip() - 6); - //auto ktxPointer = ktx::KTX::create(storage); - - //ktxPointer->writeMipData(level, data, size); - } return; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index a029b5b147..c032a9c29d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -93,7 +93,8 @@ private: }; - KTXLoadState _ktxLoadState { LOADING_HEADER }; + bool _initialKtxLoaded { false }; + //KTXLoadState _ktxLoadState; KTXFilePointer _file; static const uint16_t NULL_MIP_LEVEL; bool _sourceIsKTX { false }; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index f07ab4450b..8958eeaf3b 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -65,7 +65,8 @@ void HTTPResourceRequest::doSend() { if (_byteRange.fromInclusive < 0) { byteRange = QString("bytes=%1").arg(_byteRange.fromInclusive); } else { - byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive); + // HTTP byte ranges are inclusive on the `to` end: [from, to] + byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive - 1); } qDebug() << "Setting http range to " << byteRange; networkRequest.setRawHeader("Range", byteRange.toLatin1()); From ab7099b3eb28b95773ce650ab075c61a8e3cecc2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Sun, 16 Apr 2017 23:16:23 -0700 Subject: [PATCH 026/119] Add loading of lower mips to NetworkTexture --- .../gpu/gl45/GL45BackendVariableTexture.cpp | 1 + libraries/gpu/src/gpu/Texture.cpp | 26 +++++++++++++ libraries/gpu/src/gpu/Texture.h | 26 ++++++++++--- libraries/gpu/src/gpu/Texture_ktx.cpp | 36 ++++++++++++++---- libraries/ktx/src/ktx/KTX.h | 4 +- .../src/model-networking/TextureCache.cpp | 37 ++++++++++++++++++- .../src/model-networking/TextureCache.h | 11 ++++-- 7 files changed, 121 insertions(+), 20 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 320d694473..c37ee46bfd 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -190,6 +190,7 @@ void GL45ResourceTexture::populateTransferQueue() { qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { + const_cast(_gpuObject).requestInterestInMip(sourceMip); continue; } diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 205cf3a65a..1dbe9db2b4 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -476,6 +476,32 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin } } +void Texture::requestInterestInMip(uint16 level) { + if (!_storage->isMipAvailable(level, 0)) { + std::lock_guard lock(_mipInterestListenersMutex); + for (auto& callback : _mipInterestListeners) { + callback->handleMipInterestCallback(level); + } + } +} + +bool Texture::isStoredMipFaceAvailable(uint16 level, uint8 face) const { + return _storage->isMipAvailable(level, face); +} + +void Texture::registerMipInterestListener(MipInterestListener* listener) { + std::lock_guard lock(_mipInterestListenersMutex); + _mipInterestListeners.push_back(listener); +} + +void Texture::unregisterMipInterestListener(MipInterestListener* listener) { + std::lock_guard lock(_mipInterestListenersMutex); + auto it = find(_mipInterestListeners.begin(), _mipInterestListeners.end(), listener); + if (it != _mipInterestListeners.end()) { + _mipInterestListeners.erase(it); + } +} + void Texture::setAutoGenerateMips(bool enable) { bool changed = false; if (!_autoGenerateMips) { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 83f1f154d3..8c9d62e3b8 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -310,19 +310,22 @@ public: KtxStorage(const std::string& filename); PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override; Size getMipFaceSize(uint16 level, uint8 face = 0) const override; - // By convention, all mip levels and faces MUST be populated when using KTX backing bool isMipAvailable(uint16 level, uint8 face = 0) const override; - void assignMipData(uint16 level, const storage::StoragePointer& storage) override; - void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; void reset() override { } protected: + std::shared_ptr maybeOpenFile(); + + std::mutex _cacheFileCreateMutex; + std::mutex _cacheFileWriteMutex; + std::weak_ptr _cacheFile; + std::string _filename; - uint8_t _minMipLevelAvailable; - //storage::FileStorage _cacheFile; + std::atomic _minMipLevelAvailable; + ktx::KTXDescriptorPointer _ktxDescriptor; friend class Texture; }; @@ -470,7 +473,7 @@ public: // Access the stored mips and faces const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); } - bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } + bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const;// { return _storage->isMipAvailable(level, face); } Size getStoredMipFaceSize(uint16 level, uint8 face = 0) const { return _storage->getMipFaceSize(level, face); } Size getStoredMipSize(uint16 level) const; Size getStoredSize() const; @@ -478,6 +481,17 @@ public: void setStorage(std::unique_ptr& newStorage); void setKtxBacking(const std::string& filename); + class MipInterestListener { + public: + virtual void handleMipInterestCallback(uint16 level) = 0; + }; + void registerMipInterestListener(MipInterestListener* listener); + void unregisterMipInterestListener(MipInterestListener* listener); + std::vector _mipInterestListeners; + std::mutex _mipInterestListenersMutex; + + void requestInterestInMip(uint16 level); + // Usage is a a set of flags providing Semantic about the usage of the Texture. void setUsage(const Usage& usage) { _usage = usage; } Usage getUsage() const { return _usage; } diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d9a0348e54..0ea9f2ce4d 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -69,6 +69,19 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { } } +std::shared_ptr KtxStorage::maybeOpenFile() { + std::shared_ptr file = _cacheFile.lock(); + if (file) { + return file; + } + + std::lock_guard lock { _cacheFileCreateMutex }; + file = std::make_shared(_filename.c_str()); + _cacheFile = file; + + return file; +} + PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face; storage::StoragePointer result; @@ -86,9 +99,8 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { - auto minLevel = _minMipLevelAvailable; - auto avail = level >= minLevel; - qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels; + auto avail = level >= _minMipLevelAvailable; + qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << _minMipLevelAvailable << " " << _ktxDescriptor->header.numberOfMipmapLevels; //return true; return avail; } @@ -108,8 +120,9 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor } - auto fileStorage = new storage::FileStorage(_filename.c_str()); - ktx::StoragePointer file { fileStorage }; + //auto fileStorage = new storage::FileStorage(_filename.c_str()); + //ktx::StoragePointer file { fileStorage }; + auto file = maybeOpenFile(); auto data = file->mutableData(); data += file->size(); @@ -119,8 +132,17 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor data -= 4; } data += 4; - memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); - _minMipLevelAvailable = level; + { + std::lock_guard lock { _cacheFileWriteMutex }; + + if (level != _minMipLevelAvailable - 1) { + qWarning() << "Invalid level to be stored"; + return; + } + + memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); + _minMipLevelAvailable = level; + } } void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) { diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 7056f22ba8..e8ed7da8e3 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -70,7 +70,7 @@ end namespace ktx { - const std::string HIFI_MIN_POPULATED_MIP_KEY = "hifiMinMip"; + const std::string HIFI_MIN_POPULATED_MIP_KEY = "hifi.minMip"; const uint32_t PACKING_SIZE { sizeof(uint32_t) }; @@ -414,10 +414,10 @@ namespace ktx { struct ImageHeader { using FaceOffsets = std::vector; using FaceBytes = std::vector; + // This is the byte offset from the _start_ of the image region. For example, level 0 // will have a byte offset of 0. const uint32_t _imageOffset; - const uint32_t _numFaces; const uint32_t _imageSize; const uint32_t _faceSize; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 592413e2bc..e45c353aac 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -244,6 +244,10 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { return result; } +NetworkTexture::~NetworkTexture() { + _textureSource->getGPUTexture()->unregisterMipInterestListener(this); +} + /// Returns a texture version of an image file gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) { QImage image = QImage(path); @@ -371,6 +375,21 @@ void NetworkTexture::makeRequest() { startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); } +void NetworkTexture::handleMipInterestCallback(uint16_t level) { + QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(uint16_t, level)); +} + +void NetworkTexture::handleMipInterestLevel(uint16_t level) { + _lowestRequestedMipLevel = std::min(level, _lowestRequestedMipLevel); + if (!_ktxMipRequest) { + startRequestForNextMipLevel(); + } +} + +void NetworkTexture::startRequestForNextMipLevel() { + startMipRangeRequest(std::max(0, _lowestKnownPopulatedMip - 1), std::max(0, _lowestKnownPopulatedMip - 1)); +} + // Load mips in the range [low, high] (inclusive) void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { if (_ktxMipRequest) { @@ -430,6 +449,8 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxMipRequest->getResult() == ResourceRequest::Success) { if (_initialKtxLoaded) { assert(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + + _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; _textureSource->getGPUTexture()->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); @@ -493,6 +514,7 @@ void NetworkTexture::maybeCreateKTX() { texture.reset(gpu::Texture::unserialize(_file->getFilepath(), *_ktxDescriptor)); texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); + texture->registerMipInterestListener(this); auto& images = _ktxDescriptor->images; size_t imageSizeRemaining = _ktxHighMipData.size(); @@ -521,6 +543,15 @@ void NetworkTexture::maybeCreateKTX() { texture = textureCache->cacheTextureByHash(filename, texture); } + + _lowestKnownPopulatedMip = _ktxDescriptor->header.numberOfMipmapLevels; + for (uint16_t l = 0; l < 200; l++) { + if (texture->isStoredMipFaceAvailable(l)) { + _lowestKnownPopulatedMip = l; + break; + } + } + setImage(texture, header->getPixelWidth(), header->getPixelHeight()); @@ -528,7 +559,8 @@ void NetworkTexture::maybeCreateKTX() { { QTimer* timer = new QTimer(); connect(timer, &QTimer::timeout, this, [=]() { - startMipRangeRequest(level, level); + //startMipRangeRequest(level, level); + startRequestForNextMipLevel(); }); timer->setSingleShot(true); timer->setInterval(4000); @@ -538,7 +570,8 @@ void NetworkTexture::maybeCreateKTX() { { QTimer* timer = new QTimer(); connect(timer, &QTimer::timeout, this, [=]() { - startMipRangeRequest(level - 1, level - 1); + //startMipRangeRequest(level - 1, level - 1); + startRequestForNextMipLevel(); }); timer->setSingleShot(true); timer->setInterval(6000); diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index c032a9c29d..b11ae687a2 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -41,7 +41,7 @@ public: }; /// A texture loaded from the network. -class NetworkTexture : public Resource, public Texture { +class NetworkTexture : public Resource, public Texture, public gpu::Texture::MipInterestListener { Q_OBJECT public: @@ -57,6 +57,9 @@ public: gpu::TexturePointer getFallbackTexture() const; + void handleMipInterestCallback(uint16_t level) override; + Q_INVOKABLE void handleMipInterestLevel(uint16_t level); + signals: void networkTextureCreated(const QWeakPointer& self); @@ -77,6 +80,8 @@ protected: Q_INVOKABLE void loadContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); + void startRequestForNextMipLevel(); + void startMipRangeRequest(uint16_t low, uint16_t high); void maybeCreateKTX(); @@ -92,9 +97,7 @@ private: DONE_LOADING }; - bool _initialKtxLoaded { false }; - //KTXLoadState _ktxLoadState; KTXFilePointer _file; static const uint16_t NULL_MIP_LEVEL; bool _sourceIsKTX { false }; @@ -102,6 +105,8 @@ private: std::pair _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; + uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; + uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; QByteArray _ktxHeaderData; QByteArray _ktxHighMipData; From b21dc12cc6e0f112134c498c9c846eacba09c10f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 17 Apr 2017 10:37:21 -0700 Subject: [PATCH 027/119] Cleanup NetworkTexture ktx handling --- libraries/gpu/src/gpu/Texture_ktx.cpp | 21 ++++++++++++--- .../src/model-networking/TextureCache.cpp | 27 +++++++++++-------- .../src/model-networking/TextureCache.h | 4 +-- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 0ea9f2ce4d..1eb6d6b10f 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -75,9 +75,17 @@ std::shared_ptr KtxStorage::maybeOpenFile() { return file; } - std::lock_guard lock { _cacheFileCreateMutex }; - file = std::make_shared(_filename.c_str()); - _cacheFile = file; + { + std::lock_guard lock{ _cacheFileCreateMutex }; + + file = _cacheFile.lock(); + if (file) { + return file; + } + + file = std::make_shared(_filename.c_str()); + _cacheFile = file; + } return file; } @@ -127,11 +135,16 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor data += file->size(); // TODO Cache this data inside Image or ImageDescriptor? - for (auto i = _ktxDescriptor->header.numberOfMipmapLevels - 1; i >= level; --i) { + for (int i = _ktxDescriptor->header.numberOfMipmapLevels - 1; i >= level; --i) { data -= _ktxDescriptor->images[i]._imageSize; data -= 4; } data += 4; + + data = file->mutableData(); + data += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; + data += 4; + { std::lock_guard lock { _cacheFileWriteMutex }; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index e45c353aac..abc7ecfc41 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -376,11 +376,11 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::handleMipInterestCallback(uint16_t level) { - QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(uint16_t, level)); + QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(int, level)); } -void NetworkTexture::handleMipInterestLevel(uint16_t level) { - _lowestRequestedMipLevel = std::min(level, _lowestRequestedMipLevel); +void NetworkTexture::handleMipInterestLevel(int level) { + _lowestRequestedMipLevel = std::min(static_cast(level), _lowestRequestedMipLevel); if (!_ktxMipRequest) { startRequestForNextMipLevel(); } @@ -414,10 +414,10 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { } else { // TODO: Discover range for other mips ByteRange range; - range.fromInclusive = ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData - + _ktxDescriptor->images[low]._imageOffset + 4; - range.toExclusive = ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData - + _ktxDescriptor->images[high + 1]._imageOffset; + range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + + _originalKtxDescriptor->images[low]._imageOffset + 4; + range.toExclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + + _originalKtxDescriptor->images[high + 1]._imageOffset; _ktxMipRequest->setByteRange(range); } @@ -487,6 +487,9 @@ void NetworkTexture::maybeCreateKTX() { auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(_ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + auto imageDescriptors = header->generateImageDescriptors(); + _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); + // Create bare ktx in memory std::string filename = "test"; auto memKtx = ktx::KTX::createBare(*header, keyValues); @@ -507,16 +510,17 @@ void NetworkTexture::maybeCreateKTX() { _file = file; } - _ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); + //_ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); + auto newKtxDescriptor = memKtx->toDescriptor(); //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); gpu::TexturePointer texture; - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), *_ktxDescriptor)); + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); texture->registerMipInterestListener(this); - auto& images = _ktxDescriptor->images; + auto& images = _originalKtxDescriptor->images; size_t imageSizeRemaining = _ktxHighMipData.size(); uint8_t* ktxData = reinterpret_cast(_ktxHighMipData.data()); ktxData += _ktxHighMipData.size(); @@ -544,7 +548,7 @@ void NetworkTexture::maybeCreateKTX() { } - _lowestKnownPopulatedMip = _ktxDescriptor->header.numberOfMipmapLevels; + _lowestKnownPopulatedMip = _originalKtxDescriptor->header.numberOfMipmapLevels; for (uint16_t l = 0; l < 200; l++) { if (texture->isStoredMipFaceAvailable(l)) { _lowestKnownPopulatedMip = l; @@ -554,6 +558,7 @@ void NetworkTexture::maybeCreateKTX() { setImage(texture, header->getPixelWidth(), header->getPixelHeight()); + return; // Force load the next two levels { diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index b11ae687a2..50c8ecf352 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -58,7 +58,7 @@ public: gpu::TexturePointer getFallbackTexture() const; void handleMipInterestCallback(uint16_t level) override; - Q_INVOKABLE void handleMipInterestLevel(uint16_t level); + Q_INVOKABLE void handleMipInterestLevel(int level); signals: void networkTextureCreated(const QWeakPointer& self); @@ -114,7 +114,7 @@ private: // We need this because the KTX that will be cached will likely include extra data // in its key/value data, and so will not match up with the original, causing // mip offsets to change. - ktx::KTXDescriptorPointer _ktxDescriptor; + ktx::KTXDescriptorPointer _originalKtxDescriptor; int _originalWidth { 0 }; From 20f4d14e07eac0c4ba07e1c3256ebb87244d20cc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 17 Apr 2017 17:45:02 -0700 Subject: [PATCH 028/119] Add compressed KTX size evaluation --- .../src/RenderableWebEntityItem.cpp | 4 +- .../gpu/gl45/GL45BackendVariableTexture.cpp | 4 +- libraries/gpu/src/gpu/Texture.h | 2 + libraries/gpu/src/gpu/Texture_ktx.cpp | 14 +- libraries/ktx/src/ktx/KTX.cpp | 67 ++++- libraries/ktx/src/ktx/KTX.h | 8 +- libraries/ktx/src/ktx/Writer.cpp | 2 +- .../src/model-networking/TextureCache.cpp | 248 ++++++++++-------- .../src/model-networking/TextureCache.h | 10 +- libraries/shared/src/shared/Storage.cpp | 4 +- 10 files changed, 220 insertions(+), 143 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 0d286c46eb..20e4f4bc18 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -31,7 +31,7 @@ const float METERS_TO_INCHES = 39.3701f; static uint32_t _currentWebCount { 0 }; // Don't allow more than 100 concurrent web views -static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20; +static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 0; // If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND; @@ -71,7 +71,7 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { bool RenderableWebEntityItem::buildWebSurface(QSharedPointer renderer) { if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) { - qWarning() << "Too many concurrent web views to create new view"; + //qWarning() << "Too many concurrent web views to create new view"; return false; } QString javaScriptToInject; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index c37ee46bfd..09361689d8 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -181,13 +181,13 @@ void GL45ResourceTexture::populateTransferQueue() { const uint8_t maxFace = GLTexture::getFaceCount(_target); uint16_t sourceMip = _populatedMip; - qDebug() << "populateTransferQueue info : " << _populatedMip << " " << _maxAllocatedMip << " " << _allocatedMip; + //qDebug() << "populateTransferQueue info : " << _populatedMip << " " << _maxAllocatedMip << " " << _allocatedMip; do { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); bool transferQueued = false; - qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; + //qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { const_cast(_gpuObject).requestInterestInMip(sourceMip); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 8c9d62e3b8..e71cd63fbd 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -34,6 +34,8 @@ namespace ktx { namespace gpu { +extern const std::string SOURCE_HASH_KEY; + // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated // with the cube texture class Texture; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 1eb6d6b10f..eb6bb169ce 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -11,6 +11,7 @@ #include "Texture.h" +#include #include using namespace gpu; @@ -41,6 +42,7 @@ struct GPUKTXPayload { } }; +const std::string gpu::SOURCE_HASH_KEY { "hifi.sourceHash" }; std::string GPUKTXPayload::KEY{ "hifi.gpu" }; KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { @@ -91,7 +93,7 @@ std::shared_ptr KtxStorage::maybeOpenFile() { } PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { - qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face; + //qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face; storage::StoragePointer result; auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); @@ -108,14 +110,14 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { auto avail = level >= _minMipLevelAvailable; - qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << _minMipLevelAvailable << " " << _ktxDescriptor->header.numberOfMipmapLevels; + //qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << _minMipLevelAvailable << " " << _ktxDescriptor->header.numberOfMipmapLevels; //return true; return avail; } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { if (level != _minMipLevelAvailable - 1) { - qWarning() << "Invalid level to be stored"; + qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level; return; } @@ -124,7 +126,10 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor } if (storage->size() != _ktxDescriptor->images[level]._imageSize) { - throw std::runtime_error("Invalid image size for level"); + qDebug() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize + << ", filename: " << QString::fromStdString(_filename); + //throw std::runtime_error("Invalid image size for level"); + return; } @@ -258,7 +263,6 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { ktx::KeyValues keyValues; keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval)); - static const std::string SOURCE_HASH_KEY = "hifi.sourceHash"; auto hash = texture.sourceHash(); if (!hash.empty()) { keyValues.emplace_back(ktx::KeyValue(SOURCE_HASH_KEY, static_cast(hash.size()), (ktx::Byte*) hash.c_str())); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 73751bdef2..45820b9351 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -34,30 +34,75 @@ uint32_t Header::evalMaxDimension() const { return std::max(getPixelWidth(), std::max(getPixelHeight(), getPixelDepth())); } -uint32_t Header::evalPixelWidth(uint32_t level) const { - return std::max(getPixelWidth() >> level, 1U); +uint32_t Header::evalPixelOrBlockWidth(uint32_t level) const { + auto pixelWidth = std::max(getPixelWidth() >> level, 1U); + if (getGLType() == GLType::COMPRESSED_TYPE) { + return (pixelWidth + 3) / 4; + } else { + return pixelWidth; + } } -uint32_t Header::evalPixelHeight(uint32_t level) const { - return std::max(getPixelHeight() >> level, 1U); +uint32_t Header::evalPixelOrBlockHeight(uint32_t level) const { + auto pixelWidth = std::max(getPixelHeight() >> level, 1U); + if (getGLType() == GLType::COMPRESSED_TYPE) { + auto format = getGLInternaFormat_Compressed(); + switch (format) { + case GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT: // BC1 + case GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: // BC1A + case GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // BC3 + case GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1: // BC4 + case GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2: // BC5 + return (pixelWidth + 3) / 4; + default: + throw std::runtime_error("Unknown format"); + } + } else { + return pixelWidth; + } } -uint32_t Header::evalPixelDepth(uint32_t level) const { +uint32_t Header::evalPixelOrBlockDepth(uint32_t level) const { return std::max(getPixelDepth() >> level, 1U); } -size_t Header::evalPixelSize() const { - return 4;//glTypeSize; // Really we should generate the size from the FOrmat etc +size_t Header::evalPixelOrBlockSize() const { + if (getGLType() == GLType::COMPRESSED_TYPE) { + auto format = getGLInternaFormat_Compressed(); + if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT) { + return 8; + } else if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) { + return 8; + } else if (format == GLInternalFormat_Compressed::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) { + return 16; + } else if (format == GLInternalFormat_Compressed::COMPRESSED_RED_RGTC1) { + return 8; + } else if (format == GLInternalFormat_Compressed::COMPRESSED_RG_RGTC2) { + return 16; + } + } else { + auto baseFormat = getGLBaseInternalFormat(); + if (baseFormat == GLBaseInternalFormat::RED) { + return 1; + } else if (baseFormat == GLBaseInternalFormat::RG) { + return 2; + } else if (baseFormat == GLBaseInternalFormat::RGB) { + return 3; + } else if (baseFormat == GLBaseInternalFormat::RGBA) { + return 4; + } + } + throw std::runtime_error("Unknown format"); } size_t Header::evalRowSize(uint32_t level) const { - auto pixWidth = evalPixelWidth(level); - auto pixSize = evalPixelSize(); + auto pixWidth = evalPixelOrBlockWidth(level); + auto pixSize = evalPixelOrBlockSize(); auto netSize = pixWidth * pixSize; auto padding = evalPadding(netSize); return netSize + padding; } size_t Header::evalFaceSize(uint32_t level) const { - auto pixHeight = evalPixelHeight(level); - auto pixDepth = evalPixelDepth(level); + auto pixHeight = evalPixelOrBlockHeight(level); + auto pixDepth = evalPixelOrBlockDepth(level); auto rowSize = evalRowSize(level); return pixDepth * pixHeight * rowSize; } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index e8ed7da8e3..7f6f2da939 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -336,11 +336,11 @@ namespace ktx { uint32_t getNumberOfLevels() const { return (numberOfMipmapLevels ? numberOfMipmapLevels : 1); } uint32_t evalMaxDimension() const; - uint32_t evalPixelWidth(uint32_t level) const; - uint32_t evalPixelHeight(uint32_t level) const; - uint32_t evalPixelDepth(uint32_t level) const; + uint32_t evalPixelOrBlockWidth(uint32_t level) const; + uint32_t evalPixelOrBlockHeight(uint32_t level) const; + uint32_t evalPixelOrBlockDepth(uint32_t level) const; - size_t evalPixelSize() const; + size_t evalPixelOrBlockSize() const; size_t evalRowSize(uint32_t level) const; size_t evalFaceSize(uint32_t level) const; size_t evalImageSize(uint32_t level) const; diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 4596bf00c0..0d3d4e1d60 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -155,7 +155,7 @@ namespace ktx { ptr++; #ifdef DEBUG for (size_t k = 0; k < descriptors[i]._imageSize/4; k++) { - *(ptr + k) = 0xFFFF00FF; + *(ptr + k) = 0xFFFFFFFF; } #endif currentDestPtr += descriptors[i]._imageSize + sizeof(uint32_t); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index abc7ecfc41..3e06142d8e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -245,7 +245,10 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { } NetworkTexture::~NetworkTexture() { - _textureSource->getGPUTexture()->unregisterMipInterestListener(this); + auto texture = _textureSource->getGPUTexture(); + if (texture) { + texture->unregisterMipInterestListener(this); + } } /// Returns a texture version of an image file @@ -412,7 +415,6 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { range.fromInclusive = -15000; _ktxMipRequest->setByteRange(range); } else { - // TODO: Discover range for other mips ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[low]._imageOffset + 4; @@ -431,6 +433,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { assert(!_ktxHeaderLoaded); + _headerRequestFinished = true; if (_ktxHeaderRequest->getResult() == ResourceRequest::Success) { _ktxHeaderLoaded = true; _ktxHeaderData = _ktxHeaderRequest->getData(); @@ -447,7 +450,7 @@ void NetworkTexture::ktxMipRequestFinished() { && _ktxMipLevelRangeInFlight.second == NULL_MIP_LEVEL; if (_ktxMipRequest->getResult() == ResourceRequest::Success) { - if (_initialKtxLoaded) { + if (_highMipRequestFinished) { assert(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; @@ -456,6 +459,7 @@ void NetworkTexture::ktxMipRequestFinished() { _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); //texture->assignStoredMip(level, image._imageSize, ktxData); } else { + _highMipRequestFinished = true; _ktxHighMipData = _ktxMipRequest->getData(); maybeCreateKTX(); } @@ -469,118 +473,144 @@ void NetworkTexture::ktxMipRequestFinished() { // This is called when the header or top mips have been loaded void NetworkTexture::maybeCreateKTX() { - if (_ktxHeaderData.size() > 0 && _ktxHighMipData.size() > 0) { - // create ktx... - auto header = reinterpret_cast(_ktxHeaderData.data()); + if (_headerRequestFinished && _highMipRequestFinished) { + ResourceCache::requestCompleted(_self); + + if (_ktxHeaderData.size() > 0 && _ktxHighMipData.size() > 0) { + // create ktx... + auto header = reinterpret_cast(_ktxHeaderData.data()); + + qDebug() << "Creating KTX"; + qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); + qDebug() << "Type:" << header->glType; + qDebug() << "TypeSize:" << header->glTypeSize; + qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; + qDebug() << "numberOfFaces:" << header->numberOfFaces; + qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; + auto kvSize = header->bytesOfKeyValueData; + if (kvSize > _ktxHeaderData.size() - ktx::KTX_HEADER_SIZE) { + qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; + return; + } + + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(_ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + + auto imageDescriptors = header->generateImageDescriptors(); + _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); + + // Create bare ktx in memory + auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { + return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; + }); + std::string filename; + if (found == keyValues.end()) { + qWarning("Source hash key not found, bailing"); + filename = "test"; + //return; + + } + else { + if (found->_value.size() < 16) { + filename = _activeUrl.fileName().toStdString(); + } + else { + filename = std::string(reinterpret_cast(found->_value.data()), 32); + } + } + + auto memKtx = ktx::KTX::createBare(*header, keyValues); + + auto d = const_cast(memKtx->getStorage()->data()); + ///memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); + + auto textureCache = DependencyManager::get(); + + // Move ktx to file + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { + qCWarning(modelnetworking) << _url << "file cache failed"; + return; + } + else { + _file = file; + } + + //_ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); + auto newKtxDescriptor = memKtx->toDescriptor(); + + //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); + gpu::TexturePointer texture; + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); + texture->setKtxBacking(file->getFilepath()); + texture->setSource(filename); + texture->registerMipInterestListener(this); + + auto& images = _originalKtxDescriptor->images; + size_t imageSizeRemaining = _ktxHighMipData.size(); + uint8_t* ktxData = reinterpret_cast(_ktxHighMipData.data()); + ktxData += _ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + int level; + for (level = images.size() - 1; level >= 0; --level) { + auto& image = images[level]; + if (image._imageSize > imageSizeRemaining) { + break; + } + qDebug() << "Transferring " << level; + ktxData -= image._imageSize; + texture->assignStoredMip(level, image._imageSize, ktxData); + ktxData -= 4; + imageSizeRemaining - image._imageSize - 4; + } + + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner + if (textureCache) { + texture = textureCache->cacheTextureByHash(filename, texture); + } + + + _lowestKnownPopulatedMip = _originalKtxDescriptor->header.numberOfMipmapLevels; + for (uint16_t l = 0; l < 200; l++) { + if (texture->isStoredMipFaceAvailable(l)) { + _lowestKnownPopulatedMip = l; + break; + } + } + ResourceCache::requestCompleted(_self); + + setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); - qDebug() << "Type:" << header->glType; - qDebug() << "TypeSize:" << header->glTypeSize; - qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; - qDebug() << "numberOfFaces:" << header->numberOfFaces; - qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; - auto kvSize = header->bytesOfKeyValueData; - if (kvSize > _ktxHeaderData.size() - ktx::KTX_HEADER_SIZE) { - qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; return; - } - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(_ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); - - auto imageDescriptors = header->generateImageDescriptors(); - _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); - - // Create bare ktx in memory - std::string filename = "test"; - auto memKtx = ktx::KTX::createBare(*header, keyValues); - - auto d = const_cast(memKtx->getStorage()->data()); - ///memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); - - auto textureCache = DependencyManager::get(); - - // Move ktx to file - const char* data = reinterpret_cast(memKtx->_storage->data()); - size_t length = memKtx->_storage->size(); - KTXFilePointer file; - auto& ktxCache = textureCache->_ktxCache; - if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << "file cache failed"; - } else { - _file = file; - } - - //_ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); - auto newKtxDescriptor = memKtx->toDescriptor(); - - //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); - gpu::TexturePointer texture; - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); - texture->setKtxBacking(file->getFilepath()); - texture->setSource(filename); - texture->registerMipInterestListener(this); - - auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = _ktxHighMipData.size(); - uint8_t* ktxData = reinterpret_cast(_ktxHighMipData.data()); - ktxData += _ktxHighMipData.size(); - // TODO Move image offset calculation to ktx ImageDescriptor - uint16_t level; - for (level = images.size() - 1; level >= 0; --level) { - auto& image = images[level]; - if (image._imageSize > imageSizeRemaining) { - break; + /* + // Force load the next two levels + { + QTimer* timer = new QTimer(); + connect(timer, &QTimer::timeout, this, [=]() { + //startMipRangeRequest(level, level); + startRequestForNextMipLevel(); + }); + timer->setSingleShot(true); + timer->setInterval(4000); + timer->start(); } - qDebug() << "Transferring " << level; - ktxData -= image._imageSize; - texture->assignStoredMip(level, image._imageSize, ktxData); - ktxData -= 4; - imageSizeRemaining - image._imageSize - 4; - } - _initialKtxLoaded = true; - - // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different - // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will - // be the winner - if (textureCache) { - texture = textureCache->cacheTextureByHash(filename, texture); - } - - - _lowestKnownPopulatedMip = _originalKtxDescriptor->header.numberOfMipmapLevels; - for (uint16_t l = 0; l < 200; l++) { - if (texture->isStoredMipFaceAvailable(l)) { - _lowestKnownPopulatedMip = l; - break; + { + QTimer* timer = new QTimer(); + connect(timer, &QTimer::timeout, this, [=]() { + //startMipRangeRequest(level - 1, level - 1); + startRequestForNextMipLevel(); + }); + timer->setSingleShot(true); + timer->setInterval(6000); + timer->start(); } - } - - setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - - return; - - // Force load the next two levels - { - QTimer* timer = new QTimer(); - connect(timer, &QTimer::timeout, this, [=]() { - //startMipRangeRequest(level, level); - startRequestForNextMipLevel(); - }); - timer->setSingleShot(true); - timer->setInterval(4000); - timer->start(); - } - - { - QTimer* timer = new QTimer(); - connect(timer, &QTimer::timeout, this, [=]() { - //startMipRangeRequest(level - 1, level - 1); - startRequestForNextMipLevel(); - }); - timer->setSingleShot(true); - timer->setInterval(6000); - timer->start(); + */ } } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 50c8ecf352..30eb9d2d2e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -46,6 +46,7 @@ class NetworkTexture : public Resource, public Texture, public gpu::Texture::Mip public: NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); + NetworkTexture::~NetworkTexture() override; QString getType() const override { return "NetworkTexture"; } @@ -91,13 +92,6 @@ private: image::TextureUsage::Type _type; - enum KTXLoadState { - LOADING_HEADER, - LOADING_LOWEST_SIX, - DONE_LOADING - }; - - bool _initialKtxLoaded { false }; KTXFilePointer _file; static const uint16_t NULL_MIP_LEVEL; bool _sourceIsKTX { false }; @@ -105,6 +99,8 @@ private: std::pair _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; + bool _headerRequestFinished{ false }; + bool _highMipRequestFinished{ false }; uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; QByteArray _ktxHeaderData; diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 0f2b696a66..6eb311fa60 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -70,7 +70,7 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u // Represents a memory mapped file FileStorage::FileStorage(const QString& filename) : _file(filename) { if (_file.open(QFile::ReadWrite)) { - qDebug() << ">>> Opening mmapped file: " << filename; + //qDebug() << ">>> Opening mmapped file: " << filename; _mapped = _file.map(0, _file.size()); if (_mapped) { _valid = true; @@ -83,7 +83,7 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } FileStorage::~FileStorage() { - qDebug() << ">>> Closing mmapped file: " << _file.fileName(); + //qDebug() << ">>> Closing mmapped file: " << _file.fileName(); if (_mapped) { if (!_file.unmap(_mapped)) { throw std::runtime_error("Unable to unmap file"); From 11751611e1085e2c3a48b1b8da21b00291a6ad85 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 17 Apr 2017 22:10:21 -0700 Subject: [PATCH 029/119] Update NetworkTexture to track current KTX download state --- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- libraries/ktx/src/ktx/KTX.cpp | 6 +- .../src/model-networking/TextureCache.cpp | 102 +++++++++--------- .../src/model-networking/TextureCache.h | 21 +++- libraries/networking/src/ResourceCache.cpp | 15 ++- libraries/networking/src/ResourceCache.h | 5 + 6 files changed, 89 insertions(+), 62 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index eb6bb169ce..533797a657 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -252,7 +252,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } images.emplace_back(ktx::Image(imageOffset, (uint32_t)mip->getSize(), 0, cubeFaces)); } - imageOffset += mip->getSize() + 4; + imageOffset += static_cast(mip->getSize()) + 4; } } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 45820b9351..d9b5ea4a62 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -12,6 +12,7 @@ #include "KTX.h" #include //min max and more +#include using namespace ktx; @@ -90,6 +91,7 @@ size_t Header::evalPixelOrBlockSize() const { return 4; } } + qWarning() << "Unknown ktx format: " << glFormat << " " << glBaseInternalFormat << " " << glInternalFormat; throw std::runtime_error("Unknown format"); } @@ -119,7 +121,7 @@ ImageDescriptors Header::generateImageDescriptors() const { ImageDescriptors descriptors; uint32_t imageOffset = 0; - for (auto level = 0; level < numberOfMipmapLevels; ++level) { + for (uint32_t level = 0; level < numberOfMipmapLevels; ++level) { auto imageSize = static_cast(evalImageSize(level)); ImageHeader header { numberOfFaces == NUM_CUBEMAPFACES, @@ -131,7 +133,7 @@ ImageDescriptors Header::generateImageDescriptors() const { imageOffset += imageSize + 4; ImageHeader::FaceOffsets offsets; - for (auto i = 0; i < numberOfFaces; ++i) { + for (uint32_t i = 0; i < numberOfFaces; ++i) { offsets.push_back(0); } descriptors.push_back(ImageDescriptor(header, offsets)); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 3e06142d8e..3d81dd51e9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -38,6 +38,7 @@ #include #include +#include "NetworkLogging.h" #include "ModelNetworkingLogging.h" #include #include @@ -345,14 +346,12 @@ void NetworkTexture::makeRequest() { // We special-handle ktx requests to run 2 concurrent requests right off the bat PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); - if (!_ktxHeaderLoaded) { + if (!_ktxHeaderLoaded && !_highMipRequestFinished) { qDebug() << ">>> Making request to " << _url << " for header"; _ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl); if (!_ktxHeaderRequest) { //qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); - ResourceCache::requestCompleted(_self); - finishedLoading(false); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); return; } @@ -373,9 +372,14 @@ void NetworkTexture::makeRequest() { _bytesReceived = _bytesTotal = _bytes = 0; _ktxHeaderRequest->send(); + + startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); + } else { + if (_lowestKnownPopulatedMip > 0) { + startMipRangeRequest(_lowestKnownPopulatedMip - 1, _lowestKnownPopulatedMip - 1); + } } - startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); } void NetworkTexture::handleMipInterestCallback(uint16_t level) { @@ -384,13 +388,24 @@ void NetworkTexture::handleMipInterestCallback(uint16_t level) { void NetworkTexture::handleMipInterestLevel(int level) { _lowestRequestedMipLevel = std::min(static_cast(level), _lowestRequestedMipLevel); - if (!_ktxMipRequest) { - startRequestForNextMipLevel(); + if (!_ktxMipRequest && _lowestKnownPopulatedMip > 0) { + //startRequestForNextMipLevel(); + clearLoadPriority(this); + setLoadPriority(this, _lowestKnownPopulatedMip - 1); + ResourceCache::attemptRequest(_self); } } void NetworkTexture::startRequestForNextMipLevel() { - startMipRangeRequest(std::max(0, _lowestKnownPopulatedMip - 1), std::max(0, _lowestKnownPopulatedMip - 1)); + if (_lowestKnownPopulatedMip == 0) { + qWarning(networking) << "Requesting next mip level but all have been fulfilled"; + return; + } + if (_pending) { + return; + } + + //startMipRangeRequest(std::max(0, _lowestKnownPopulatedMip - 1), std::max(0, _lowestKnownPopulatedMip - 1)); } // Load mips in the range [low, high] (inclusive) @@ -406,7 +421,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { } _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); - qDebug() << ">>> Making request to " << _url << " for " << low << " to " << high; + qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { @@ -437,7 +452,7 @@ void NetworkTexture::ktxHeaderRequestFinished() { if (_ktxHeaderRequest->getResult() == ResourceRequest::Success) { _ktxHeaderLoaded = true; _ktxHeaderData = _ktxHeaderRequest->getData(); - maybeCreateKTX(); + maybeHandleFinishedInitialLoad(); } else { handleFailedRequest(_ktxHeaderRequest->getResult()); } @@ -455,15 +470,18 @@ void NetworkTexture::ktxMipRequestFinished() { _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; - _textureSource->getGPUTexture()->assignStoredMip(_ktxMipLevelRangeInFlight.first, - _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); - //texture->assignStoredMip(level, image._imageSize, ktxData); + auto texture = _textureSource->getGPUTexture(); + if (!texture) { + texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, + _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); + } else { + qWarning(networking) << "Trying to update mips but texture is null"; + } } else { _highMipRequestFinished = true; _ktxHighMipData = _ktxMipRequest->getData(); - maybeCreateKTX(); + maybeHandleFinishedInitialLoad(); } - } else { handleFailedRequest(_ktxMipRequest->getResult()); } @@ -472,11 +490,13 @@ void NetworkTexture::ktxMipRequestFinished() { } // This is called when the header or top mips have been loaded -void NetworkTexture::maybeCreateKTX() { +void NetworkTexture::maybeHandleFinishedInitialLoad() { if (_headerRequestFinished && _highMipRequestFinished) { ResourceCache::requestCompleted(_self); - if (_ktxHeaderData.size() > 0 && _ktxHighMipData.size() > 0) { + if (_ktxHeaderData.size() == 0 || _ktxHighMipData.size() == 0) { + finishedLoading(false); + } else { // create ktx... auto header = reinterpret_cast(_ktxHeaderData.data()); @@ -490,6 +510,7 @@ void NetworkTexture::maybeCreateKTX() { auto kvSize = header->bytesOfKeyValueData; if (kvSize > _ktxHeaderData.size() - ktx::KTX_HEADER_SIZE) { qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; + finishedLoading(false); return; } @@ -505,23 +526,23 @@ void NetworkTexture::maybeCreateKTX() { std::string filename; if (found == keyValues.end()) { qWarning("Source hash key not found, bailing"); - filename = "test"; - //return; - + finishedLoading(false); + return; } else { - if (found->_value.size() < 16) { - filename = _activeUrl.fileName().toStdString(); - } - else { + if (found->_value.size() < 32) { + qWarning("Invalid source hash key found, bailing"); + finishedLoading(false); + return; + } else { filename = std::string(reinterpret_cast(found->_value.data()), 32); } } auto memKtx = ktx::KTX::createBare(*header, keyValues); - auto d = const_cast(memKtx->getStorage()->data()); - ///memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); + //auto d = const_cast(memKtx->getStorage()->data()); + //memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); auto textureCache = DependencyManager::get(); @@ -531,7 +552,8 @@ void NetworkTexture::maybeCreateKTX() { KTXFilePointer file; auto& ktxCache = textureCache->_ktxCache; if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << "file cache failed"; + qCWarning(modelnetworking) << _url << " failed to write cache file"; + finishedLoading(false); return; } else { @@ -581,36 +603,8 @@ void NetworkTexture::maybeCreateKTX() { break; } } - ResourceCache::requestCompleted(_self); setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - - return; - - /* - // Force load the next two levels - { - QTimer* timer = new QTimer(); - connect(timer, &QTimer::timeout, this, [=]() { - //startMipRangeRequest(level, level); - startRequestForNextMipLevel(); - }); - timer->setSingleShot(true); - timer->setInterval(4000); - timer->start(); - } - - { - QTimer* timer = new QTimer(); - connect(timer, &QTimer::timeout, this, [=]() { - //startMipRangeRequest(level - 1, level - 1); - startRequestForNextMipLevel(); - }); - timer->setSingleShot(true); - timer->setInterval(6000); - timer->start(); - } - */ } } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 30eb9d2d2e..3048f16eb8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -84,7 +84,7 @@ protected: void startRequestForNextMipLevel(); void startMipRangeRequest(uint16_t low, uint16_t high); - void maybeCreateKTX(); + void maybeHandleFinishedInitialLoad(); private: friend class KTXReader; @@ -92,15 +92,28 @@ private: image::TextureUsage::Type _type; - KTXFilePointer _file; static const uint16_t NULL_MIP_LEVEL; + struct KTXResourceState { + NOT_LOADED = 0, + LOADING_INITIAL_DATA, // Loading KTX Header + Low Resolution Mips + WAITING_FOR_MIP_REQUEST, // Waiting for the gpu layer to report that it needs higher resolution mips + PENDING_MIP_REQUEST, // We have added ourselves to the ResourceCache queue + REQUESTING_MIP // We have a mip in flight + }; + + KTXResourceState _ktxResourceState{ NOT_LOADED }; + + KTXFilePointer _file; + bool _sourceIsKTX { false }; bool _ktxHeaderLoaded { false }; + bool _highMipRequestFinished { false }; + std::pair _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; + ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; - bool _headerRequestFinished{ false }; - bool _highMipRequestFinished{ false }; + bool _headerRequestFinished { false }; uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; QByteArray _ktxHeaderData; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 038ee7fb53..6716b7473f 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -474,8 +474,15 @@ int ResourceCache::getLoadingRequestCount() { bool ResourceCache::attemptRequest(QSharedPointer resource) { Q_ASSERT(!resource.isNull()); - auto sharedItems = DependencyManager::get(); + if (resource->_pending) { + qWarning(networking) << "Attempted to request " << resource->getURL() << " but it was already pending"; + return false; + } + + resource->_pending = true; + + auto sharedItems = DependencyManager::get(); if (_requestsActive >= _requestLimit) { // wait until a slot becomes available sharedItems->appendPendingRequest(resource); @@ -490,6 +497,12 @@ bool ResourceCache::attemptRequest(QSharedPointer resource) { void ResourceCache::requestCompleted(QWeakPointer resource) { auto sharedItems = DependencyManager::get(); + + auto sharedResource = resource.lock(); + if (sharedResource) { + sharedResource->_pending = true; + } + sharedItems->removeRequest(resource); --_requestsActive; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index e699c3d198..13162aac9d 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -424,6 +424,9 @@ protected slots: protected: virtual void init(); + /// Called by ResourceCache to begin loading this Resource. + /// This method can be overriden to provide custom request functionality. If this is done, + /// downloadFinished and ResourceCache::requestCompleted must be called. virtual void makeRequest(); /// Checks whether the resource is cacheable. @@ -460,6 +463,8 @@ protected: int _requestID; ResourceRequest* _request{ nullptr }; + + bool _pending{ false }; public slots: void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal); From 70b816827eb56ded04abbc36f9c3ec117e2c8634 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 18 Apr 2017 00:14:04 -0700 Subject: [PATCH 030/119] Improve handling of KTX downloads in NetworkTexture --- .../src/model-networking/TextureCache.cpp | 135 ++++++++++-------- .../src/model-networking/TextureCache.h | 22 +-- libraries/networking/src/ResourceCache.cpp | 17 +-- libraries/networking/src/ResourceCache.h | 5 +- 4 files changed, 95 insertions(+), 84 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 3d81dd51e9..25d7018a1a 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -281,10 +281,10 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _loaded = true; } - if (_sourceIsKTX) { - _requestByteRange.fromInclusive = 0; - _requestByteRange.toExclusive = 1000; - } + //if (_sourceIsKTX) { + //_requestByteRange.fromInclusive = 0; + //_requestByteRange.toExclusive = 1000; + //} // if we have content, load it after we have our self pointer if (!content.isEmpty()) { @@ -346,7 +346,9 @@ void NetworkTexture::makeRequest() { // We special-handle ktx requests to run 2 concurrent requests right off the bat PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); - if (!_ktxHeaderLoaded && !_highMipRequestFinished) { + if (_ktxResourceState == PENDING_INITIAL_LOAD) { + _ktxResourceState = LOADING_INITIAL_DATA; + qDebug() << ">>> Making request to " << _url << " for header"; _ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl); @@ -374,10 +376,13 @@ void NetworkTexture::makeRequest() { _ktxHeaderRequest->send(); startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); - } else { + } else if (_ktxResourceState == PENDING_MIP_REQUEST) { + _ktxResourceState = REQUESTING_MIP; if (_lowestKnownPopulatedMip > 0) { startMipRangeRequest(_lowestKnownPopulatedMip - 1, _lowestKnownPopulatedMip - 1); } + } else { + qWarning(networking) << "NetworkTexture::makeRequest() called while not in a valid state: " << _ktxResourceState; } } @@ -387,13 +392,7 @@ void NetworkTexture::handleMipInterestCallback(uint16_t level) { } void NetworkTexture::handleMipInterestLevel(int level) { - _lowestRequestedMipLevel = std::min(static_cast(level), _lowestRequestedMipLevel); - if (!_ktxMipRequest && _lowestKnownPopulatedMip > 0) { - //startRequestForNextMipLevel(); - clearLoadPriority(this); - setLoadPriority(this, _lowestKnownPopulatedMip - 1); - ResourceCache::attemptRequest(_self); - } + startRequestForNextMipLevel(); } void NetworkTexture::startRequestForNextMipLevel() { @@ -401,11 +400,15 @@ void NetworkTexture::startRequestForNextMipLevel() { qWarning(networking) << "Requesting next mip level but all have been fulfilled"; return; } - if (_pending) { - return; - } - //startMipRangeRequest(std::max(0, _lowestKnownPopulatedMip - 1), std::max(0, _lowestKnownPopulatedMip - 1)); + if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { + _ktxResourceState = PENDING_MIP_REQUEST; + + setLoadPriority(this, _lowestKnownPopulatedMip); + + init(); + ResourceCache::attemptRequest(_self); + } } // Load mips in the range [low, high] (inclusive) @@ -415,10 +418,6 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { } bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; - - if (!isHighMipRequest && !_ktxHeaderLoaded) { - return; - } _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; @@ -446,59 +445,75 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { - assert(!_ktxHeaderLoaded); + Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); - _headerRequestFinished = true; - if (_ktxHeaderRequest->getResult() == ResourceRequest::Success) { - _ktxHeaderLoaded = true; - _ktxHeaderData = _ktxHeaderRequest->getData(); - maybeHandleFinishedInitialLoad(); - } else { - handleFailedRequest(_ktxHeaderRequest->getResult()); - } - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; + _ktxHeaderRequestFinished = true; + maybeHandleFinishedInitialLoad(); } void NetworkTexture::ktxMipRequestFinished() { - bool isHighMipRequest = _ktxMipLevelRangeInFlight.first == NULL_MIP_LEVEL - && _ktxMipLevelRangeInFlight.second == NULL_MIP_LEVEL; + Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP); - if (_ktxMipRequest->getResult() == ResourceRequest::Success) { - if (_highMipRequestFinished) { - assert(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); + if (_ktxResourceState == LOADING_INITIAL_DATA) { + _ktxHighMipRequestFinished = true; + maybeHandleFinishedInitialLoad(); + } else if (_ktxResourceState == REQUESTING_MIP) { + ResourceCache::requestCompleted(_self); + + if (_ktxMipRequest->getResult() == ResourceRequest::Success) { + Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); - _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; - auto texture = _textureSource->getGPUTexture(); - if (!texture) { + if (texture) { + _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); } else { qWarning(networking) << "Trying to update mips but texture is null"; } + _ktxResourceState = WAITING_FOR_MIP_REQUEST; } else { - _highMipRequestFinished = true; - _ktxHighMipData = _ktxMipRequest->getData(); - maybeHandleFinishedInitialLoad(); + _ktxResourceState = PENDING_MIP_REQUEST; + handleFailedRequest(_ktxMipRequest->getResult()); } + + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } else { - handleFailedRequest(_ktxMipRequest->getResult()); + qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; } - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; } // This is called when the header or top mips have been loaded void NetworkTexture::maybeHandleFinishedInitialLoad() { - if (_headerRequestFinished && _highMipRequestFinished) { + Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + + if (_ktxHeaderRequestFinished && _ktxHighMipRequestFinished) { + ResourceCache::requestCompleted(_self); - if (_ktxHeaderData.size() == 0 || _ktxHighMipData.size() == 0) { - finishedLoading(false); + if (_ktxHeaderRequest->getResult() != ResourceRequest::Success || _ktxMipRequest->getResult() != ResourceRequest::Success) { + if (handleFailedRequest(_ktxMipRequest->getResult())) { + _ktxResourceState = PENDING_INITIAL_LOAD; + } else { + _ktxResourceState = FAILED_TO_LOAD; + } + + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } else { // create ktx... - auto header = reinterpret_cast(_ktxHeaderData.data()); + auto ktxHeaderData = _ktxHeaderRequest->getData(); + auto ktxHighMipData = _ktxMipRequest->getData(); + + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; + + auto header = reinterpret_cast(ktxHeaderData.data()); qDebug() << "Creating KTX"; qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); @@ -507,14 +522,16 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; qDebug() << "numberOfFaces:" << header->numberOfFaces; qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; + auto kvSize = header->bytesOfKeyValueData; - if (kvSize > _ktxHeaderData.size() - ktx::KTX_HEADER_SIZE) { + if (kvSize > ktxHeaderData.size() - ktx::KTX_HEADER_SIZE) { qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; + _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); return; } - auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(_ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); + auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); auto imageDescriptors = header->generateImageDescriptors(); _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); @@ -532,6 +549,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { else { if (found->_value.size() < 32) { qWarning("Invalid source hash key found, bailing"); + _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); return; } else { @@ -553,10 +571,10 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto& ktxCache = textureCache->_ktxCache; if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { qCWarning(modelnetworking) << _url << " failed to write cache file"; + _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); return; - } - else { + } else { _file = file; } @@ -571,9 +589,9 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { texture->registerMipInterestListener(this); auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = _ktxHighMipData.size(); - uint8_t* ktxData = reinterpret_cast(_ktxHighMipData.data()); - ktxData += _ktxHighMipData.size(); + size_t imageSizeRemaining = ktxHighMipData.size(); + uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); + ktxData += ktxHighMipData.size(); // TODO Move image offset calculation to ktx ImageDescriptor int level; for (level = images.size() - 1; level >= 0; --level) { @@ -585,7 +603,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { ktxData -= image._imageSize; texture->assignStoredMip(level, image._imageSize, ktxData); ktxData -= 4; - imageSizeRemaining - image._imageSize - 4; + imageSizeRemaining -= (image._imageSize + 4); } // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different @@ -604,6 +622,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { } } + _ktxResourceState = WAITING_FOR_MIP_REQUEST; setImage(texture, header->getPixelWidth(), header->getPixelHeight()); } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 3048f16eb8..1ea266a115 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -93,31 +93,31 @@ private: image::TextureUsage::Type _type; static const uint16_t NULL_MIP_LEVEL; - struct KTXResourceState { - NOT_LOADED = 0, + enum KTXResourceState { + PENDING_INITIAL_LOAD = 0, LOADING_INITIAL_DATA, // Loading KTX Header + Low Resolution Mips WAITING_FOR_MIP_REQUEST, // Waiting for the gpu layer to report that it needs higher resolution mips PENDING_MIP_REQUEST, // We have added ourselves to the ResourceCache queue - REQUESTING_MIP // We have a mip in flight + REQUESTING_MIP, // We have a mip in flight + FAILED_TO_LOAD }; - KTXResourceState _ktxResourceState{ NOT_LOADED }; + bool _sourceIsKTX { false }; + KTXResourceState _ktxResourceState { PENDING_INITIAL_LOAD }; + // TODO Can this be removed? KTXFilePointer _file; - bool _sourceIsKTX { false }; - bool _ktxHeaderLoaded { false }; - bool _highMipRequestFinished { false }; - + // The current mips that are currently being requested w/ _ktxMipRequest std::pair _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL }; ResourceRequest* _ktxHeaderRequest { nullptr }; ResourceRequest* _ktxMipRequest { nullptr }; - bool _headerRequestFinished { false }; + bool _ktxHeaderRequestFinished{ false }; + bool _ktxHighMipRequestFinished{ false }; + uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL }; uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL }; - QByteArray _ktxHeaderData; - QByteArray _ktxHighMipData; // This is a copy of the original KTX descriptor from the source url. // We need this because the KTX that will be cached will likely include extra data diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 6716b7473f..95bfd0e24d 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -475,12 +475,6 @@ int ResourceCache::getLoadingRequestCount() { bool ResourceCache::attemptRequest(QSharedPointer resource) { Q_ASSERT(!resource.isNull()); - if (resource->_pending) { - qWarning(networking) << "Attempted to request " << resource->getURL() << " but it was already pending"; - return false; - } - - resource->_pending = true; auto sharedItems = DependencyManager::get(); if (_requestsActive >= _requestLimit) { @@ -498,11 +492,6 @@ bool ResourceCache::attemptRequest(QSharedPointer resource) { void ResourceCache::requestCompleted(QWeakPointer resource) { auto sharedItems = DependencyManager::get(); - auto sharedResource = resource.lock(); - if (sharedResource) { - sharedResource->_pending = true; - } - sharedItems->removeRequest(resource); --_requestsActive; @@ -747,7 +736,8 @@ void Resource::handleReplyFinished() { _request = nullptr; } -void Resource::handleFailedRequest(ResourceRequest::Result result) { +bool Resource::handleFailedRequest(ResourceRequest::Result result) { + bool willRetry = false; switch (result) { case ResourceRequest::Result::Timeout: { qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal; @@ -763,6 +753,7 @@ void Resource::handleFailedRequest(ResourceRequest::Result result) { << "if resource is still needed"; QTimer::singleShot(waitTime, this, &Resource::attemptRequest); + willRetry = true; break; } // fall through to final failure @@ -772,10 +763,12 @@ void Resource::handleFailedRequest(ResourceRequest::Result result) { auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError : QNetworkReply::UnknownNetworkError; emit failed(error); + willRetry = false; finishedLoading(false); break; } } + return willRetry; } uint qHash(const QPointer& value, uint seed) { diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 13162aac9d..3a28c6c313 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -445,7 +445,8 @@ protected: Q_INVOKABLE void allReferencesCleared(); - void handleFailedRequest(ResourceRequest::Result result); + /// Return true if the resource will be retried + bool handleFailedRequest(ResourceRequest::Result result); QUrl _url; QUrl _activeUrl; @@ -464,8 +465,6 @@ protected: int _requestID; ResourceRequest* _request{ nullptr }; - bool _pending{ false }; - public slots: void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal); void handleReplyFinished(); From 5594e81fe40779c5c20c1c3646d9e19464904dab Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 18 Apr 2017 17:03:54 -0700 Subject: [PATCH 031/119] Improve gl backend handling of unavailable mips --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 1 + libraries/gpu-gl/src/gpu/gl/GLTexture.h | 1 + libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 1 + .../gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 14 ++++++++++---- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 1 + .../gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 10 ++++++++++ .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 10 +++++++++- libraries/gpu/src/gpu/Texture.h | 4 ++++ libraries/gpu/src/gpu/Texture_ktx.cpp | 7 ++++++- libraries/ktx/src/ktx/KTX.cpp | 6 ++++-- .../src/model-networking/TextureCache.cpp | 14 +++++++++++--- 11 files changed, 58 insertions(+), 11 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index b7d2ee0b0f..3853d0a9cc 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -520,6 +520,7 @@ void GLVariableAllocationSupport::processWorkQueues() { _memoryPressureStateStale = true; } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { if (!vartexture->canPromote()) { + vartexture->populateTransferQueue(); continue; } vartexture->promote(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 8b4b545b7d..c8ec4c5fe6 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -116,6 +116,7 @@ protected: bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } void executeNextTransfer(const TexturePointer& currentTexture); + virtual bool canPopulate() const = 0; virtual void populateTransferQueue() = 0; virtual void promote() = 0; virtual void demote() = 0; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 19979a1778..c0b9ea0e45 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -100,6 +100,7 @@ public: GL41VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); ~GL41VariableAllocationTexture(); + bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } void allocateStorage(uint16 allocatedMip); void syncSampler() const override; void promote() override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index bff5bf3f2c..04c2201c75 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -392,8 +392,10 @@ void GL41VariableAllocationTexture::populateTransferQueue() { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); + bool didQueueTransfer = false; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { + const_cast(_gpuObject).requestInterestInMip(sourceMip); continue; } @@ -401,6 +403,7 @@ void GL41VariableAllocationTexture::populateTransferQueue() { if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); + didQueueTransfer = true; continue; } @@ -416,14 +419,17 @@ void GL41VariableAllocationTexture::populateTransferQueue() { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; + didQueueTransfer = true; } } // queue up the sampler and populated mip change for after the transfer has completed - _pendingTransfers.emplace(new TransferJob(*this, [=] { - _populatedMip = sourceMip; - syncSampler(); - })); + if (didQueueTransfer) { + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); + } } while (sourceMip != _allocatedMip); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index dbedd81c76..15e98c3af7 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -100,6 +100,7 @@ public: GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); ~GL45VariableAllocationTexture(); Size size() const override { return _size; } + bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } Size _size { 0 }; }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index a539b76b6c..54b2411a14 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -80,6 +80,16 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { default: Q_UNREACHABLE(); } + } else { + + if (texture.getUsageType() == TextureUsageType::RESOURCE) { + auto varTex = static_cast (object); + + if (varTex->canPromoteAndPopulate()) { + GL45VariableAllocationTexture::_memoryPressureStateStale = true; + } + + } } return object; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 09361689d8..ac2b5f607f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -62,6 +62,7 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); allocateStorage(allocatedMip); + _memoryPressureStateStale = true; copyMipsFromTexture(); syncSampler(); @@ -105,7 +106,12 @@ void GL45ResourceTexture::syncSampler() const { void GL45ResourceTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - Q_ASSERT(_allocatedMip > 0); + //Q_ASSERT(_allocatedMip > 0); + uint16_t sourceMip = _populatedMip; + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, 0)) { + const_cast(_gpuObject).requestInterestInMip(sourceMip); + return; + } GLuint oldId = _id; auto oldSize = _size; // create new texture @@ -191,6 +197,7 @@ void GL45ResourceTexture::populateTransferQueue() { for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { const_cast(_gpuObject).requestInterestInMip(sourceMip); + _minRequestedMip = sourceMip; continue; } @@ -218,6 +225,7 @@ void GL45ResourceTexture::populateTransferQueue() { lineOffset += linesToCopy; transferQueued = true; } + _minRequestedMip = std::min(_minRequestedMip, sourceMip); } // queue up the sampler and populated mip change for after the transfer has completed diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index e71cd63fbd..6e13598caa 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -275,6 +275,7 @@ public: virtual void assignMipData(uint16 level, const storage::StoragePointer& storage) = 0; virtual void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) = 0; virtual bool isMipAvailable(uint16 level, uint8 face = 0) const = 0; + virtual uint16 minAvailableMipLevel() const { return 0; } Texture::Type getType() const { return _type; } Stamp getStamp() const { return _stamp; } @@ -315,6 +316,7 @@ public: bool isMipAvailable(uint16 level, uint8 face = 0) const override; void assignMipData(uint16 level, const storage::StoragePointer& storage) override; void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override; + uint16 minAvailableMipLevel() const override; void reset() override { } @@ -332,6 +334,8 @@ public: friend class Texture; }; + uint16 minAvailableMipLevel() const { return _storage->minAvailableMipLevel(); }; + static const uint16 MAX_NUM_MIPS = 0; static const uint16 SINGLE_MIP = 1; static TexturePointer create1D(const Element& texelFormat, uint16 width, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 533797a657..e2b9b8d9ae 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -115,9 +115,14 @@ bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { return avail; } +uint16 KtxStorage::minAvailableMipLevel() const { + return _minMipLevelAvailable; +} + void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { if (level != _minMipLevelAvailable - 1) { qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level; + //throw std::runtime_error("Invalid image size for level"); return; } @@ -128,7 +133,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor if (storage->size() != _ktxDescriptor->images[level]._imageSize) { qDebug() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize << ", filename: " << QString::fromStdString(_filename); - //throw std::runtime_error("Invalid image size for level"); + throw std::runtime_error("Invalid image size for level"); return; } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index d9b5ea4a62..c9dd18d665 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -92,7 +92,8 @@ size_t Header::evalPixelOrBlockSize() const { } } qWarning() << "Unknown ktx format: " << glFormat << " " << glBaseInternalFormat << " " << glInternalFormat; - throw std::runtime_error("Unknown format"); + return 1; + //throw std::runtime_error("Unknown format"); } size_t Header::evalRowSize(uint32_t level) const { @@ -130,9 +131,10 @@ ImageDescriptors Header::generateImageDescriptors() const { 0 }; - imageOffset += imageSize + 4; + imageOffset += (imageSize * numberOfFaces) + 4; ImageHeader::FaceOffsets offsets; + // TODO Add correct face offsets for (uint32_t i = 0; i < numberOfFaces; ++i) { offsets.push_back(0); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 25d7018a1a..11121570c1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -388,6 +388,7 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::handleMipInterestCallback(uint16_t level) { + //qDebug(networking) << "++++ Got request for mip level: " << _url << " " << level; QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(int, level)); } @@ -466,6 +467,7 @@ void NetworkTexture::ktxMipRequestFinished() { auto texture = _textureSource->getGPUTexture(); if (texture) { _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; + qDebug() << "Writing mip for " << _url; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); } else { @@ -542,9 +544,10 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { }); std::string filename; if (found == keyValues.end()) { - qWarning("Source hash key not found, bailing"); - finishedLoading(false); - return; + //qWarning("Source hash key not found, bailing"); + //finishedLoading(false); + //return; + filename = _url.fileName().toStdString(); } else { if (found->_value.size() < 32) { @@ -558,6 +561,11 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { } auto memKtx = ktx::KTX::createBare(*header, keyValues); + if (!memKtx) { + qWarning() << " Ktx could not be created, bailing"; + finishedLoading(false); + return; + } //auto d = const_cast(memKtx->getStorage()->data()); //memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); From 70eaac8d6c5b509d3e395f2595e41011ee129f71 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 18 Apr 2017 21:16:17 -0700 Subject: [PATCH 032/119] Add persisting of ktx min mips available to ktx cache file --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 6 +++--- libraries/gpu/src/gpu/Texture_ktx.cpp | 4 ++++ libraries/ktx/src/ktx/KTX.cpp | 12 ++++++++++++ libraries/ktx/src/ktx/KTX.h | 1 + .../src/model-networking/TextureCache.cpp | 3 ++- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 3853d0a9cc..c82fa4c744 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -458,12 +458,12 @@ void GLVariableAllocationSupport::updateMemoryPressure() { float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; auto newState = MemoryPressureState::Idle; - if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { + if (hasTransfers) { + newState = MemoryPressureState::Transfer; + } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { newState = MemoryPressureState::Oversubscribed; } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { newState = MemoryPressureState::Undersubscribed; - } else if (hasTransfers) { - newState = MemoryPressureState::Transfer; } if (newState != _memoryPressureState) { diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index e2b9b8d9ae..c0fe3f5c9c 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -155,6 +155,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor data += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; data += 4; + auto offset = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); { std::lock_guard lock { _cacheFileWriteMutex }; @@ -165,6 +166,9 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; + if (offset > 0) { + memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + offset, (uint8_t)_minMipLevelAvailable, 1); + } } } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index c9dd18d665..68b83b1682 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -118,6 +118,18 @@ size_t Header::evalImageSize(uint32_t level) const { } } + +size_t KTXDescriptor::getValueOffsetForKey(const std::string& key) const { + size_t offset { 0 }; + for (auto& kv : keyValues) { + if (kv._key == key) { + return offset + kv._key.size() + 1; + } + offset += kv.serializedByteSize(); + } + return 0; +} + ImageDescriptors Header::generateImageDescriptors() const { ImageDescriptors descriptors; diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 7f6f2da939..65ecc430b9 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -471,6 +471,7 @@ namespace ktx { const ImageDescriptors images; size_t getMipFaceTexelsSize(uint16_t mip = 0, uint8_t face = 0) const; size_t getMipFaceTexelsOffset(uint16_t mip = 0, uint8_t face = 0) const; + size_t getValueOffsetForKey(const std::string& key) const; }; class KTX { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 11121570c1..8b6530b5b9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -377,8 +377,8 @@ void NetworkTexture::makeRequest() { startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL); } else if (_ktxResourceState == PENDING_MIP_REQUEST) { - _ktxResourceState = REQUESTING_MIP; if (_lowestKnownPopulatedMip > 0) { + _ktxResourceState = REQUESTING_MIP; startMipRangeRequest(_lowestKnownPopulatedMip - 1, _lowestKnownPopulatedMip - 1); } } else { @@ -393,6 +393,7 @@ void NetworkTexture::handleMipInterestCallback(uint16_t level) { } void NetworkTexture::handleMipInterestLevel(int level) { + _lowestRequestedMipLevel = std::min((uint16_t)level, _lowestRequestedMipLevel); startRequestForNextMipLevel(); } From ce12a216c07bc654381eb42e4816f2f8e3812360 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Apr 2017 01:20:03 -0700 Subject: [PATCH 033/119] I think it might be working. --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 3 + libraries/gpu-gl/src/gpu/gl/GLTexture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 5 +- .../src/model-networking/ModelCache.cpp | 1 + .../src/model-networking/TextureCache.cpp | 147 ++++++++++-------- .../networking/src/HTTPResourceRequest.cpp | 3 +- 6 files changed, 90 insertions(+), 71 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index c82fa4c744..652dc3c46f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -459,10 +459,13 @@ void GLVariableAllocationSupport::updateMemoryPressure() { auto newState = MemoryPressureState::Idle; if (hasTransfers) { + qDebug() << "Transferring"; newState = MemoryPressureState::Transfer; } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { + qDebug() << "Demoting"; newState = MemoryPressureState::Oversubscribed; } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { + qDebug() << "Promoting"; newState = MemoryPressureState::Undersubscribed; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index c8ec4c5fe6..7f3431b7eb 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -112,7 +112,7 @@ protected: static void manageMemory(); //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - bool canPromote() const { return _allocatedMip > 0; } + bool canPromote() const { return _allocatedMip > 0 || _populatedMip > 0; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } void executeNextTransfer(const TexturePointer& currentTexture); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index c0fe3f5c9c..a7af329505 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -51,6 +51,9 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { ktx::StoragePointer storage{ new storage::FileStorage(_filename.c_str()) }; auto ktxPointer = ktx::KTX::create(storage); _ktxDescriptor.reset(new ktx::KTXDescriptor(ktxPointer->toDescriptor())); + if (_ktxDescriptor->images.size() < _ktxDescriptor->header.numberOfMipmapLevels) { + qDebug() << "bad images found"; + } auto& keyValues = _ktxDescriptor->keyValues; auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { return val._key.compare(ktx::HIFI_MIN_POPULATED_MIP_KEY) == 0; @@ -167,7 +170,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; if (offset > 0) { - memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + offset, (uint8_t)_minMipLevelAvailable, 1); + memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + offset, (void*)&_minMipLevelAvailable, 1); } } } diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 623832aaa8..defe868abc 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -364,6 +364,7 @@ void Geometry::setTextures(const QVariantMap& textureMap) { bool Geometry::areTexturesLoaded() const { if (!_areTexturesLoaded) { + qDebug() << "Textures not loaded for " << _fbxGeometry->originalURL; for (auto& material : _materials) { // Check if material textures are loaded bool materialMissingTexture = std::any_of(material->_textures.cbegin(), material->_textures.cend(), diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 8b6530b5b9..4c40075daa 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -422,7 +422,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); - qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; + //qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { @@ -460,6 +460,7 @@ void NetworkTexture::ktxMipRequestFinished() { _ktxHighMipRequestFinished = true; maybeHandleFinishedInitialLoad(); } else if (_ktxResourceState == REQUESTING_MIP) { + Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); ResourceCache::requestCompleted(_self); if (_ktxMipRequest->getResult() == ResourceRequest::Success) { @@ -468,20 +469,26 @@ void NetworkTexture::ktxMipRequestFinished() { auto texture = _textureSource->getGPUTexture(); if (texture) { _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; - qDebug() << "Writing mip for " << _url; + //qDebug() << "Writing mip for " << _url; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); } else { qWarning(networking) << "Trying to update mips but texture is null"; } + finishedLoading(true); _ktxResourceState = WAITING_FOR_MIP_REQUEST; } else { _ktxResourceState = PENDING_MIP_REQUEST; + finishedLoading(false); handleFailedRequest(_ktxMipRequest->getResult()); } _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; + + if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { + startRequestForNextMipLevel(); + } } else { qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; } @@ -544,85 +551,88 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { return val._key.compare(gpu::SOURCE_HASH_KEY) == 0; }); std::string filename; - if (found == keyValues.end()) { - //qWarning("Source hash key not found, bailing"); - //finishedLoading(false); - //return; - filename = _url.fileName().toStdString(); - } - else { - if (found->_value.size() < 32) { - qWarning("Invalid source hash key found, bailing"); - _ktxResourceState = FAILED_TO_LOAD; - finishedLoading(false); - return; - } else { - filename = std::string(reinterpret_cast(found->_value.data()), 32); - } - } - - auto memKtx = ktx::KTX::createBare(*header, keyValues); - if (!memKtx) { - qWarning() << " Ktx could not be created, bailing"; - finishedLoading(false); - return; - } - - //auto d = const_cast(memKtx->getStorage()->data()); - //memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size()); - - auto textureCache = DependencyManager::get(); - - // Move ktx to file - const char* data = reinterpret_cast(memKtx->_storage->data()); - size_t length = memKtx->_storage->size(); - KTXFilePointer file; - auto& ktxCache = textureCache->_ktxCache; - if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { - qCWarning(modelnetworking) << _url << " failed to write cache file"; + std::string hash; + if (found == keyValues.end() || found->_value.size() != 32) { + qWarning("Invalid source hash key found, bailing"); _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); return; } else { - _file = file; + hash = filename = std::string(reinterpret_cast(found->_value.data()), 32); + //hash = filename = _url.path().replace("/", "_").toStdString(); } - //_ktxDescriptor.reset(new ktx::KTXDescriptor(memKtx->toDescriptor())); - auto newKtxDescriptor = memKtx->toDescriptor(); + auto textureCache = DependencyManager::get(); - //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); - gpu::TexturePointer texture; - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); - texture->setKtxBacking(file->getFilepath()); - texture->setSource(filename); - texture->registerMipInterestListener(this); + gpu::TexturePointer texture = textureCache->getTextureByHash(hash); - auto& images = _originalKtxDescriptor->images; - size_t imageSizeRemaining = ktxHighMipData.size(); - uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); - ktxData += ktxHighMipData.size(); - // TODO Move image offset calculation to ktx ImageDescriptor - int level; - for (level = images.size() - 1; level >= 0; --level) { - auto& image = images[level]; - if (image._imageSize > imageSizeRemaining) { - break; + if (!texture) { + KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); + if (ktxFile) { + texture.reset(gpu::Texture::unserialize(ktxFile->getFilepath())); + if (texture) { + texture = textureCache->cacheTextureByHash(hash, texture); + } } - qDebug() << "Transferring " << level; - ktxData -= image._imageSize; - texture->assignStoredMip(level, image._imageSize, ktxData); - ktxData -= 4; - imageSizeRemaining -= (image._imageSize + 4); } - // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different - // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will - // be the winner - if (textureCache) { + if (!texture) { + + auto memKtx = ktx::KTX::createBare(*header, keyValues); + if (!memKtx) { + qWarning() << " Ktx could not be created, bailing"; + finishedLoading(false); + return; + } + + // Move ktx to file + const char* data = reinterpret_cast(memKtx->_storage->data()); + size_t length = memKtx->_storage->size(); + KTXFilePointer file; + auto& ktxCache = textureCache->_ktxCache; + if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { + qCWarning(modelnetworking) << _url << " failed to write cache file"; + _ktxResourceState = FAILED_TO_LOAD; + finishedLoading(false); + return; + } + else { + _file = file; + } + + auto newKtxDescriptor = memKtx->toDescriptor(); + + //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); + texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); + texture->setKtxBacking(file->getFilepath()); + texture->setSource(filename); + + auto& images = _originalKtxDescriptor->images; + size_t imageSizeRemaining = ktxHighMipData.size(); + uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); + ktxData += ktxHighMipData.size(); + // TODO Move image offset calculation to ktx ImageDescriptor + int level; + for (level = images.size() - 1; level >= 0; --level) { + auto& image = images[level]; + if (image._imageSize > imageSizeRemaining) { + break; + } + //qDebug() << "Transferring " << level; + ktxData -= image._imageSize; + texture->assignStoredMip(level, image._imageSize, ktxData); + ktxData -= 4; + imageSizeRemaining -= (image._imageSize + 4); + } + + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will + // be the winner texture = textureCache->cacheTextureByHash(filename, texture); - } + } + _lowestKnownPopulatedMip = _originalKtxDescriptor->header.numberOfMipmapLevels; for (uint16_t l = 0; l < 200; l++) { if (texture->isStoredMipFaceAvailable(l)) { @@ -631,8 +641,11 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { } } + + texture->registerMipInterestListener(this); _ktxResourceState = WAITING_FOR_MIP_REQUEST; setImage(texture, header->getPixelWidth(), header->getPixelHeight()); + qDebug() << "Loaded KTX: " << QString::fromStdString(hash) << " : " << _url; } } } diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 8958eeaf3b..0023ecb09c 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -132,7 +132,7 @@ void HTTPResourceRequest::onRequestFinished() { uint64_t size; std::tie(success, size) = parseContentRangeHeader(contentRangeHeader); if (success) { - qWarning(networking) << "Total http resource size is: " << size; + //qWarning(networking) << "Total http resource size is: " << size; _totalSizeOfResource = size; } else { qWarning(networking) << "Error parsing content-range header: " << contentRangeHeader; @@ -187,7 +187,6 @@ void HTTPResourceRequest::onRequestFinished() { } void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - qDebug() << "Progress: " << _url; Q_ASSERT(_state == InProgress); // We've received data, so reset the timer From cc7169b754e4656a87f5a134a06c244d53240117 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Apr 2017 11:08:37 -0700 Subject: [PATCH 034/119] Add debug logging for ktx downloads --- libraries/gpu/src/gpu/Texture_ktx.cpp | 4 ++-- .../model-networking/src/model-networking/ModelCache.cpp | 2 +- .../model-networking/src/model-networking/TextureCache.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index a7af329505..5d85a9f01e 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -135,8 +135,8 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor if (storage->size() != _ktxDescriptor->images[level]._imageSize) { qDebug() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize - << ", filename: " << QString::fromStdString(_filename); - throw std::runtime_error("Invalid image size for level"); + << ", level: " << level << ", filename: " << QString::fromStdString(_filename); + //throw std::runtime_error("Invalid image size for level"); return; } diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index defe868abc..fc662e32a3 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -364,7 +364,7 @@ void Geometry::setTextures(const QVariantMap& textureMap) { bool Geometry::areTexturesLoaded() const { if (!_areTexturesLoaded) { - qDebug() << "Textures not loaded for " << _fbxGeometry->originalURL; + //qDebug() << "Textures not loaded for " << _fbxGeometry->originalURL; for (auto& material : _materials) { // Check if material textures are loaded bool materialMissingTexture = std::any_of(material->_textures.cbegin(), material->_textures.cend(), diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 4c40075daa..1a80280546 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -399,7 +399,7 @@ void NetworkTexture::handleMipInterestLevel(int level) { void NetworkTexture::startRequestForNextMipLevel() { if (_lowestKnownPopulatedMip == 0) { - qWarning(networking) << "Requesting next mip level but all have been fulfilled"; + qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _url; return; } @@ -422,7 +422,6 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); - //qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { @@ -431,6 +430,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { range.fromInclusive = -15000; _ktxMipRequest->setByteRange(range); } else { + qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[low]._imageOffset + 4; From 841d301decaf3cc093bc9684c0f4451ef92287e3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Apr 2017 11:25:20 -0700 Subject: [PATCH 035/119] Fix build errors due to TexturePointer change --- libraries/gpu/src/gpu/Texture.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- .../model-networking/src/model-networking/TextureCache.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 6e13598caa..adf6a464f7 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -525,7 +525,7 @@ public: ExternalUpdates getUpdates() const; // Serialize ktx header and keyvalues directly to a file, and return a Texture representing that file - static Texture* serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues); + static TexturePointer serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues); // Serialize a texture into a KTX file static ktx::KTXUniquePointer serialize(const Texture& texture); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 5d85a9f01e..9a1c633be5 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -370,7 +370,7 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe return tex; } -Texture* Texture::serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues) { +TexturePointer Texture::serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues) { // Create a memory-backed KTX object auto ktxBuffer = ktx::KTX::createBare(header, keyValues); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1a80280546..cf94192442 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -569,7 +569,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (!texture) { KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash); if (ktxFile) { - texture.reset(gpu::Texture::unserialize(ktxFile->getFilepath())); + texture = gpu::Texture::unserialize(ktxFile->getFilepath()); if (texture) { texture = textureCache->cacheTextureByHash(hash, texture); } @@ -603,7 +603,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto newKtxDescriptor = memKtx->toDescriptor(); //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); - texture.reset(gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor)); + texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); From 472c888529c96d752cc75e91c5ba405349aa2580 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Apr 2017 13:16:53 -0700 Subject: [PATCH 036/119] Fix handling of failed ktx downloads --- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 2 +- libraries/ktx/src/ktx/KTX.cpp | 8 +++- libraries/ktx/src/ktx/KTX.h | 2 + .../src/model-networking/TextureCache.cpp | 44 ++++++++++++++----- .../render-utils/src/MeshPartPayload.cpp | 3 +- tests/gpu-test/CMakeLists.txt | 2 +- 6 files changed, 46 insertions(+), 15 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 7f3431b7eb..bc8467b808 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -114,7 +114,7 @@ protected: //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } bool canPromote() const { return _allocatedMip > 0 || _populatedMip > 0; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } + bool hasPendingTransfers() const { return _pendingTransfers.size() > 0; } void executeNextTransfer(const TexturePointer& currentTexture); virtual bool canPopulate() const = 0; virtual void populateTransferQueue() = 0; diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 68b83b1682..e00937a67a 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -92,13 +92,16 @@ size_t Header::evalPixelOrBlockSize() const { } } qWarning() << "Unknown ktx format: " << glFormat << " " << glBaseInternalFormat << " " << glInternalFormat; - return 1; + return 0; //throw std::runtime_error("Unknown format"); } size_t Header::evalRowSize(uint32_t level) const { auto pixWidth = evalPixelOrBlockWidth(level); auto pixSize = evalPixelOrBlockSize(); + if (pixSize == 0) { + return 0; + } auto netSize = pixWidth * pixSize; auto padding = evalPadding(netSize); return netSize + padding; @@ -136,6 +139,9 @@ ImageDescriptors Header::generateImageDescriptors() const { uint32_t imageOffset = 0; for (uint32_t level = 0; level < numberOfMipmapLevels; ++level) { auto imageSize = static_cast(evalImageSize(level)); + if (imageSize == 0) { + return ImageDescriptors(); + } ImageHeader header { numberOfFaces == NUM_CUBEMAPFACES, imageOffset, diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 65ecc430b9..713733b1dd 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -298,6 +298,8 @@ namespace ktx { struct ImageDescriptor; using ImageDescriptors = std::vector; + bool checkIdentifier(const Byte* identifier); + // Header struct Header { static const size_t IDENTIFIER_LENGTH = 12; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index cf94192442..17f1d62b7b 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -399,14 +399,15 @@ void NetworkTexture::handleMipInterestLevel(int level) { void NetworkTexture::startRequestForNextMipLevel() { if (_lowestKnownPopulatedMip == 0) { - qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _url; + qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _lowestKnownPopulatedMip + << " " << _textureSource->getGPUTexture()->minAvailableMipLevel() << " " << _url; return; } if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { _ktxResourceState = PENDING_MIP_REQUEST; - setLoadPriority(this, _lowestKnownPopulatedMip); + setLoadPriority(this, -_originalKtxDescriptor->header.numberOfMipmapLevels + _lowestKnownPopulatedMip); init(); ResourceCache::attemptRequest(_self); @@ -449,8 +450,8 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); - _ktxHeaderRequestFinished = true; - maybeHandleFinishedInitialLoad(); +_ktxHeaderRequestFinished = true; +maybeHandleFinishedInitialLoad(); } void NetworkTexture::ktxMipRequestFinished() { @@ -459,7 +460,8 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxResourceState == LOADING_INITIAL_DATA) { _ktxHighMipRequestFinished = true; maybeHandleFinishedInitialLoad(); - } else if (_ktxResourceState == REQUESTING_MIP) { + } + else if (_ktxResourceState == REQUESTING_MIP) { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); ResourceCache::requestCompleted(_self); @@ -472,15 +474,22 @@ void NetworkTexture::ktxMipRequestFinished() { //qDebug() << "Writing mip for " << _url; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); - } else { + } + else { qWarning(networking) << "Trying to update mips but texture is null"; } finishedLoading(true); _ktxResourceState = WAITING_FOR_MIP_REQUEST; - } else { - _ktxResourceState = PENDING_MIP_REQUEST; + } + else { finishedLoading(false); - handleFailedRequest(_ktxMipRequest->getResult()); + if (handleFailedRequest(_ktxMipRequest->getResult())) { + _ktxResourceState = PENDING_MIP_REQUEST; + } + else { + qWarning() << "Failed to load mip: " << _url; + _ktxResourceState = FAILED_TO_LOAD; + } } _ktxMipRequest->deleteLater(); @@ -489,7 +498,8 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { startRequestForNextMipLevel(); } - } else { + } + else { qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; } } @@ -505,7 +515,8 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (_ktxHeaderRequest->getResult() != ResourceRequest::Success || _ktxMipRequest->getResult() != ResourceRequest::Success) { if (handleFailedRequest(_ktxMipRequest->getResult())) { _ktxResourceState = PENDING_INITIAL_LOAD; - } else { + } + else { _ktxResourceState = FAILED_TO_LOAD; } @@ -533,6 +544,12 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { qDebug() << "numberOfFaces:" << header->numberOfFaces; qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; + if (!ktx::checkIdentifier(header->identifier)) { + qWarning() << "Cannot load " << _url << ", invalid header identifier"; + _ktxResourceState = FAILED_TO_LOAD; + finishedLoading(false); + } + auto kvSize = header->bytesOfKeyValueData; if (kvSize > ktxHeaderData.size() - ktx::KTX_HEADER_SIZE) { qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; @@ -544,6 +561,11 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE); auto imageDescriptors = header->generateImageDescriptors(); + if (imageDescriptors.size() == 0) { + qWarning(networking) << "Failed to process ktx file " << _url; + _ktxResourceState = FAILED_TO_LOAD; + finishedLoading(false); + } _originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); // Create bare ktx in memory diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 51ce0fffa7..e6ae6d21a1 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -544,7 +544,8 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } if (_fadeState == FADE_WAITING_TO_START) { - if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { + //if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { + if (_model->isLoaded()) { if (EntityItem::getEntitiesShouldFadeFunction()()) { _fadeStartTime = usecTimestampNow(); _fadeState = FADE_IN_PROGRESS; diff --git a/tests/gpu-test/CMakeLists.txt b/tests/gpu-test/CMakeLists.txt index 1712a5a3e1..c37e36b53b 100644 --- a/tests/gpu-test/CMakeLists.txt +++ b/tests/gpu-test/CMakeLists.txt @@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render-utils) # This is not a testcase -- just set it up as a regular hifi project setup_hifi_project(Quick Gui OpenGL Script Widgets) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") -link_hifi_libraries(networking gl gpu gpu-gl procedural shared fbx model model-networking animation script-engine render render-utils octree image) +link_hifi_libraries(networking gl gpu gpu-gl procedural shared fbx model model-networking animation script-engine render render-utils octree image ktx) package_libraries_for_deployment() target_nsight() From d5f1e6fb373e7c004353fd0d771da8996f3aeb08 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 19 Apr 2017 17:01:36 -0700 Subject: [PATCH 037/119] Adjust handling of requested mips in gl backend --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 2 +- .../gpu/gl45/GL45BackendVariableTexture.cpp | 8 ++--- libraries/gpu/src/gpu/Texture_ktx.cpp | 3 +- libraries/ktx/src/ktx/Reader.cpp | 2 +- .../src/model-networking/TextureCache.cpp | 32 +++++++++++++------ 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 652dc3c46f..1179403bff 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -450,7 +450,7 @@ void GLVariableAllocationSupport::updateMemoryPressure() { // Track how much we're actually using totalVariableMemoryAllocation += gltexture->size(); canDemote |= vartexture->canDemote(); - canPromote |= vartexture->canPromote(); + canPromote |= vartexture->canPromote() || (texture->minAvailableMipLevel() < vartexture->_allocatedMip); hasTransfers |= vartexture->hasPendingTransfers(); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index ac2b5f607f..f99209a7c2 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -98,18 +98,14 @@ void GL45ResourceTexture::copyMipsFromTexture() { void GL45ResourceTexture::syncSampler() const { Parent::syncSampler(); qDebug() << "glTextureParameteri " << QString::fromStdString(_source) << _populatedMip << _populatedMip - _allocatedMip; - if (_source == "test" && _populatedMip == 0) { - qDebug() << "here"; - } glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); } void GL45ResourceTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); //Q_ASSERT(_allocatedMip > 0); - uint16_t sourceMip = _populatedMip; - if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, 0)) { - const_cast(_gpuObject).requestInterestInMip(sourceMip); + if (!_gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0)) { + const_cast(_gpuObject).requestInterestInMip(_populatedMip - 1); return; } GLuint oldId = _id; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 9a1c633be5..620fd56277 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -123,8 +123,9 @@ uint16 KtxStorage::minAvailableMipLevel() const { } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { + qDebug() << "Populating " << level << " " << _filename.c_str(); if (level != _minMipLevelAvailable - 1) { - qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level; + qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level << " " << _filename.c_str(); //throw std::runtime_error("Invalid image size for level"); return; } diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index b22f262e85..27aee8771c 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -50,7 +50,7 @@ namespace ktx { bool checkIdentifier(const Byte* identifier) { if (!(0 == memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH))) { - throw ReaderException("identifier field invalid"); + //throw ReaderException("identifier field invalid"); return false; } return true; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 17f1d62b7b..84e9c82946 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -353,7 +353,8 @@ void NetworkTexture::makeRequest() { _ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl); if (!_ktxHeaderRequest) { - //qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); + qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); return; } @@ -424,6 +425,13 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); + if (!_ktxMipRequest) { + qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); + + PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); + return; + } + _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { // This is a special case where we load the high 7 mips @@ -450,8 +458,8 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); -_ktxHeaderRequestFinished = true; -maybeHandleFinishedInitialLoad(); + _ktxHeaderRequestFinished = true; + maybeHandleFinishedInitialLoad(); } void NetworkTexture::ktxMipRequestFinished() { @@ -470,10 +478,11 @@ void NetworkTexture::ktxMipRequestFinished() { auto texture = _textureSource->getGPUTexture(); if (texture) { - _lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; + //_lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; //qDebug() << "Writing mip for " << _url; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); + _lowestKnownPopulatedMip = _textureSource->getGPUTexture()->minAvailableMipLevel(); } else { qWarning(networking) << "Trying to update mips but texture is null"; @@ -529,11 +538,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto ktxHeaderData = _ktxHeaderRequest->getData(); auto ktxHighMipData = _ktxMipRequest->getData(); - _ktxHeaderRequest->deleteLater(); - _ktxHeaderRequest = nullptr; - _ktxMipRequest->deleteLater(); - _ktxMipRequest = nullptr; - auto header = reinterpret_cast(ktxHeaderData.data()); qDebug() << "Creating KTX"; @@ -548,10 +552,11 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { qWarning() << "Cannot load " << _url << ", invalid header identifier"; _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); + return; } auto kvSize = header->bytesOfKeyValueData; - if (kvSize > ktxHeaderData.size() - ktx::KTX_HEADER_SIZE) { + if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); @@ -663,11 +668,18 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { } } + _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + texture->registerMipInterestListener(this); _ktxResourceState = WAITING_FOR_MIP_REQUEST; setImage(texture, header->getPixelWidth(), header->getPixelHeight()); qDebug() << "Loaded KTX: " << QString::fromStdString(hash) << " : " << _url; + + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; } } } From 18fb695614c007af22f73414af13b0d12d194230 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 20 Apr 2017 15:26:05 -0700 Subject: [PATCH 038/119] Disable pipelining in HTTPResourceRequest --- libraries/networking/src/HTTPResourceRequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 0023ecb09c..7045894633 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -71,7 +71,7 @@ void HTTPResourceRequest::doSend() { qDebug() << "Setting http range to " << byteRange; networkRequest.setRawHeader("Range", byteRange.toLatin1()); } - networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, false); _reply = NetworkAccessManager::getInstance().get(networkRequest); From 4e583416312e4fa6be22158ad63347d4958b1f90 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 20 Apr 2017 16:28:57 -0700 Subject: [PATCH 039/119] Remove http proxy --- interface/src/Application.cpp | 9 --------- libraries/networking/src/NetworkAccessManager.cpp | 9 +-------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 886487603e..f51c3d2b46 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -188,7 +188,6 @@ #include #include #include -#include // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. @@ -605,7 +604,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo { const QString TEST_SCRIPT = "--testScript"; const QString TRACE_FILE = "--traceFile"; - const QString HTTP_PROXY = "--httpProxy"; const QStringList args = arguments(); for (int i = 0; i < args.size() - 1; ++i) { if (args.at(i) == TEST_SCRIPT) { @@ -617,17 +615,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QString traceFilePath = args.at(i + 1); setProperty(hifi::properties::TRACING, traceFilePath); DependencyManager::get()->startTracing(); - } else if (args.at(i) == HTTP_PROXY) { } } } - QNetworkProxy proxy; - proxy.setType(QNetworkProxy::HttpProxy); - proxy.setHostName("127.0.0.1"); - proxy.setPort(8888); - QNetworkProxy::setApplicationProxy(proxy); - // make sure the debug draw singleton is initialized on the main thread. DebugDraw::getInstance().removeMarker(""); diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index 6895118be5..fd356c3e94 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -19,14 +19,7 @@ QThreadStorage networkAccessManagers; QNetworkAccessManager& NetworkAccessManager::getInstance() { if (!networkAccessManagers.hasLocalData()) { - auto nm = new QNetworkAccessManager(); - networkAccessManagers.setLocalData(nm); - - QNetworkProxy proxy; - proxy.setType(QNetworkProxy::HttpProxy); - proxy.setHostName("127.0.0.1"); - proxy.setPort(8888); - nm->setProxy(proxy); + networkAccessManagers.setLocalData(new QNetworkAccessManager()); } return *networkAccessManagers.localData(); From 7cab70debac07e44a443ed01ff2ae558e0c0ba9d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 20 Apr 2017 23:17:42 -0700 Subject: [PATCH 040/119] Remove gl error logging --- libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 008b658205..1d1f92b297 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -259,9 +259,7 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(target, to); - if (CHECK_GL_ERROR()) { - qDebug() << "slot: " << slot << ", target: " << target << ", to: " << to; - } + (void) CHECK_GL_ERROR(); _resource._textures[slot] = resourceTexture; From b2ff0a711e4e9319b937363c7c06c81237312efc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 20 Apr 2017 23:30:42 -0700 Subject: [PATCH 041/119] Replace canPromoteAndPopulate with multiple function calls --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 54b2411a14..761cd305bb 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -85,7 +85,7 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { if (texture.getUsageType() == TextureUsageType::RESOURCE) { auto varTex = static_cast (object); - if (varTex->canPromoteAndPopulate()) { + if (varTex->canPromote() && varTex->canPopulate()) { GL45VariableAllocationTexture::_memoryPressureStateStale = true; } From 790290f40ca7faa829aeb7d9a60cc8b759317b78 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 20 Apr 2017 23:31:10 -0700 Subject: [PATCH 042/119] Fix NetworkTexture build errors on osx/linux --- .../model-networking/src/model-networking/TextureCache.cpp | 6 +++--- .../model-networking/src/model-networking/TextureCache.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 84e9c82946..0bfca1f1fb 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -411,7 +411,7 @@ void NetworkTexture::startRequestForNextMipLevel() { setLoadPriority(this, -_originalKtxDescriptor->header.numberOfMipmapLevels + _lowestKnownPopulatedMip); init(); - ResourceCache::attemptRequest(_self); + TextureCache::attemptRequest(_self); } } @@ -471,7 +471,7 @@ void NetworkTexture::ktxMipRequestFinished() { } else if (_ktxResourceState == REQUESTING_MIP) { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); if (_ktxMipRequest->getResult() == ResourceRequest::Success) { Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); @@ -519,7 +519,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (_ktxHeaderRequestFinished && _ktxHighMipRequestFinished) { - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); if (_ktxHeaderRequest->getResult() != ResourceRequest::Success || _ktxMipRequest->getResult() != ResourceRequest::Success) { if (handleFailedRequest(_ktxMipRequest->getResult())) { diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 1ea266a115..5fb23c0d2d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -46,7 +46,7 @@ class NetworkTexture : public Resource, public Texture, public gpu::Texture::Mip public: NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); - NetworkTexture::~NetworkTexture() override; + ~NetworkTexture() override; QString getType() const override { return "NetworkTexture"; } From 5901ab63812eb136c7f58d98a0625064aca86073 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 20 Apr 2017 23:38:22 -0700 Subject: [PATCH 043/119] Fix initalization order warnings in KTX.h and TextureCache.cpp --- libraries/ktx/src/ktx/KTX.h | 2 +- .../model-networking/src/model-networking/TextureCache.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 713733b1dd..e84cba085d 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -419,8 +419,8 @@ namespace ktx { // This is the byte offset from the _start_ of the image region. For example, level 0 // will have a byte offset of 0. - const uint32_t _imageOffset; const uint32_t _numFaces; + const uint32_t _imageOffset; const uint32_t _imageSize; const uint32_t _faceSize; const uint32_t _padding; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 0bfca1f1fb..05cfc7c06e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -272,8 +272,8 @@ QSharedPointer TextureCache::createResource(const QUrl& url, const QSh NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) : Resource(url), _type(type), - _maxNumPixels(maxNumPixels), - _sourceIsKTX(url.path().endsWith(".ktx")) + _sourceIsKTX(url.path().endsWith(".ktx")), + _maxNumPixels(maxNumPixels) { _textureSource = std::make_shared(); From bc6d476a4d2c2cb7aab644f815f332974cf18d9b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 21 Apr 2017 00:21:25 -0700 Subject: [PATCH 044/119] Cleanup Texture_ktx.cpp --- libraries/gpu/src/gpu/Texture_ktx.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 620fd56277..250b9ea8f3 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -52,7 +52,7 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { auto ktxPointer = ktx::KTX::create(storage); _ktxDescriptor.reset(new ktx::KTXDescriptor(ktxPointer->toDescriptor())); if (_ktxDescriptor->images.size() < _ktxDescriptor->header.numberOfMipmapLevels) { - qDebug() << "bad images found"; + qWarning() << "Bad images found in ktx"; } auto& keyValues = _ktxDescriptor->keyValues; auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { @@ -96,7 +96,6 @@ std::shared_ptr KtxStorage::maybeOpenFile() { } PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { - //qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face; storage::StoragePointer result; auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); @@ -112,10 +111,7 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const { bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const { - auto avail = level >= _minMipLevelAvailable; - //qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << _minMipLevelAvailable << " " << _ktxDescriptor->header.numberOfMipmapLevels; - //return true; - return avail; + return level >= _minMipLevelAvailable; } uint16 KtxStorage::minAvailableMipLevel() const { @@ -123,10 +119,8 @@ uint16 KtxStorage::minAvailableMipLevel() const { } void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& storage) { - qDebug() << "Populating " << level << " " << _filename.c_str(); if (level != _minMipLevelAvailable - 1) { qWarning() << "Invalid level to be stored, expected: " << (_minMipLevelAvailable - 1) << ", got: " << level << " " << _filename.c_str(); - //throw std::runtime_error("Invalid image size for level"); return; } @@ -135,9 +129,8 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor } if (storage->size() != _ktxDescriptor->images[level]._imageSize) { - qDebug() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize + qWarning() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize << ", level: " << level << ", filename: " << QString::fromStdString(_filename); - //throw std::runtime_error("Invalid image size for level"); return; } From 8d03d50d35843656264842cdb64240eb5b946fc0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 21 Apr 2017 00:33:05 -0700 Subject: [PATCH 045/119] Cleanup progressive ktx debugging --- .../gpu/gl45/GL45BackendVariableTexture.cpp | 9 +------ libraries/gpu/src/gpu/Texture.cpp | 1 + libraries/gpu/src/gpu/Texture.h | 3 --- libraries/gpu/src/gpu/Texture_ktx.cpp | 22 +-------------- libraries/ktx/src/ktx/KTX.cpp | 3 +-- libraries/ktx/src/ktx/Reader.cpp | 2 +- .../src/model-networking/ModelCache.cpp | 1 - .../src/model-networking/TextureCache.cpp | 27 ++----------------- .../networking/src/HTTPResourceRequest.cpp | 1 - libraries/networking/src/ResourceCache.cpp | 2 -- .../render-utils/src/MeshPartPayload.cpp | 1 - libraries/shared/src/shared/Storage.cpp | 3 --- 12 files changed, 7 insertions(+), 68 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index f99209a7c2..77e42e7fdb 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -41,9 +41,6 @@ GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { - if (texture.source().find_first_of("box.ktx") != std::string::npos) { - qDebug() << "In box.ktx ctor"; - } auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; @@ -53,13 +50,11 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend for (uint16_t mip = 0; mip < mipLevels; ++mip) { if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) && texture.isStoredMipFaceAvailable(mip)) { - _maxAllocatedMip = _populatedMip = mip; + _lowestRequestedMip = _maxAllocatedMip = _populatedMip = mip; break; } } - //_maxAllocatedMip = _populatedMip = mipLevels; - //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); allocateStorage(allocatedMip); _memoryPressureStateStale = true; @@ -112,7 +107,6 @@ void GL45ResourceTexture::promote() { auto oldSize = _size; // create new texture const_cast(_id) = allocate(_gpuObject); - //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); @@ -146,7 +140,6 @@ void GL45ResourceTexture::demote() { auto oldId = _id; auto oldSize = _size; const_cast(_id) = allocate(_gpuObject); - //glObjectLabel(GL_TEXTURE, _id, _source.length(), _source.data()); allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); uint16_t mips = _gpuObject.getNumMips(); diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 1dbe9db2b4..bed01a805d 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -397,6 +397,7 @@ Size Texture::evalTotalSize(uint16 startingMip) const { Size size = 0; uint16 minMipLevel = std::max(getMinMip(), startingMip); uint16 maxMipLevel = getMaxMip(); + qDebug() << " min: " << minMipLevel << " " << maxMipLevel; for (uint16 level = minMipLevel; level <= maxMipLevel; level++) { size += evalMipSize(level); } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index adf6a464f7..146615d631 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -524,9 +524,6 @@ public: ExternalUpdates getUpdates() const; - // Serialize ktx header and keyvalues directly to a file, and return a Texture representing that file - static TexturePointer serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues); - // Serialize a texture into a KTX file static ktx::KTXUniquePointer serialize(const Texture& texture); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 250b9ea8f3..8e30645837 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -134,21 +134,9 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor return; } - - //auto fileStorage = new storage::FileStorage(_filename.c_str()); - //ktx::StoragePointer file { fileStorage }; auto file = maybeOpenFile(); + auto data = file->mutableData(); - data += file->size(); - - // TODO Cache this data inside Image or ImageDescriptor? - for (int i = _ktxDescriptor->header.numberOfMipmapLevels - 1; i >= level; --i) { - data -= _ktxDescriptor->images[i]._imageSize; - data -= 4; - } - data += 4; - - data = file->mutableData(); data += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; data += 4; @@ -173,7 +161,6 @@ void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::Stor throw std::runtime_error("Invalid call"); } - void Texture::setKtxBacking(const std::string& filename) { // Check the KTX file for validity before using it as backing storage { @@ -364,13 +351,6 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe return tex; } -TexturePointer Texture::serializeHeader(const std::string& ktxfile, const ktx::Header& header, const ktx::KeyValues& keyValues) { - // Create a memory-backed KTX object - auto ktxBuffer = ktx::KTX::createBare(header, keyValues); - - return unserialize(ktxfile, ktxBuffer->toDescriptor()); -} - bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat_Uncompressed::RGBA8, ktx::GLBaseInternalFormat::RGBA); diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index e00937a67a..0dbc2e720f 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -91,9 +91,9 @@ size_t Header::evalPixelOrBlockSize() const { return 4; } } + qWarning() << "Unknown ktx format: " << glFormat << " " << glBaseInternalFormat << " " << glInternalFormat; return 0; - //throw std::runtime_error("Unknown format"); } size_t Header::evalRowSize(uint32_t level) const { @@ -284,7 +284,6 @@ Image ImageDescriptor::toImage(const ktx::StoragePointer& storage) const { FaceBytes faces; faces.resize(_faceOffsets.size()); for (size_t face = 0; face < _numFaces; ++face) { - // TODO Should we be storing pointers to unowned data? faces[face] = storage->data() + _faceOffsets[face]; } // Note, implicit cast of *this to const ImageHeader& diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index 27aee8771c..b22f262e85 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -50,7 +50,7 @@ namespace ktx { bool checkIdentifier(const Byte* identifier) { if (!(0 == memcmp(identifier, Header::IDENTIFIER.data(), Header::IDENTIFIER_LENGTH))) { - //throw ReaderException("identifier field invalid"); + throw ReaderException("identifier field invalid"); return false; } return true; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index fc662e32a3..623832aaa8 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -364,7 +364,6 @@ void Geometry::setTextures(const QVariantMap& textureMap) { bool Geometry::areTexturesLoaded() const { if (!_areTexturesLoaded) { - //qDebug() << "Textures not loaded for " << _fbxGeometry->originalURL; for (auto& material : _materials) { // Check if material textures are loaded bool materialMissingTexture = std::any_of(material->_textures.cbegin(), material->_textures.cend(), diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 05cfc7c06e..761f55068b 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -281,11 +281,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _loaded = true; } - //if (_sourceIsKTX) { - //_requestByteRange.fromInclusive = 0; - //_requestByteRange.toExclusive = 1000; - //} - // if we have content, load it after we have our self pointer if (!content.isEmpty()) { _startedLoading = true; @@ -364,12 +359,9 @@ void NetworkTexture::makeRequest() { range.toExclusive = 1000; _ktxHeaderRequest->setByteRange(range); - //qCDebug(resourceLog).noquote() << "Starting request for:" << _url.toDisplayString(); emit loading(); connect(_ktxHeaderRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxHeaderRequestProgress); - //connect(this, &Resource::onProgress, this, &NetworkTexture::ktxHeaderRequestFinished); - connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxHeaderRequestFinished); _bytesReceived = _bytesTotal = _bytes = 0; @@ -389,7 +381,6 @@ void NetworkTexture::makeRequest() { } void NetworkTexture::handleMipInterestCallback(uint16_t level) { - //qDebug(networking) << "++++ Got request for mip level: " << _url << " " << level; QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(int, level)); } @@ -426,7 +417,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { _ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl); if (!_ktxMipRequest) { - qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); + qCWarning(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); return; @@ -439,7 +430,6 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { range.fromInclusive = -15000; _ktxMipRequest->setByteRange(range); } else { - qDebug(networking) << ">>> Making request to " << _url << " for " << low << " to " << high; ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[low]._imageOffset + 4; @@ -478,8 +468,6 @@ void NetworkTexture::ktxMipRequestFinished() { auto texture = _textureSource->getGPUTexture(); if (texture) { - //_lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first; - //qDebug() << "Writing mip for " << _url; texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); _lowestKnownPopulatedMip = _textureSource->getGPUTexture()->minAvailableMipLevel(); @@ -496,7 +484,7 @@ void NetworkTexture::ktxMipRequestFinished() { _ktxResourceState = PENDING_MIP_REQUEST; } else { - qWarning() << "Failed to load mip: " << _url; + qWarning(networking) << "Failed to load mip: " << _url; _ktxResourceState = FAILED_TO_LOAD; } } @@ -540,14 +528,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto header = reinterpret_cast(ktxHeaderData.data()); - qDebug() << "Creating KTX"; - qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12)); - qDebug() << "Type:" << header->glType; - qDebug() << "TypeSize:" << header->glTypeSize; - qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements; - qDebug() << "numberOfFaces:" << header->numberOfFaces; - qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels; - if (!ktx::checkIdentifier(header->identifier)) { qWarning() << "Cannot load " << _url << ", invalid header identifier"; _ktxResourceState = FAILED_TO_LOAD; @@ -586,7 +566,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { return; } else { hash = filename = std::string(reinterpret_cast(found->_value.data()), 32); - //hash = filename = _url.path().replace("/", "_").toStdString(); } auto textureCache = DependencyManager::get(); @@ -629,7 +608,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { auto newKtxDescriptor = memKtx->toDescriptor(); - //auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues); texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor); texture->setKtxBacking(file->getFilepath()); texture->setSource(filename); @@ -645,7 +623,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { if (image._imageSize > imageSizeRemaining) { break; } - //qDebug() << "Transferring " << level; ktxData -= image._imageSize; texture->assignStoredMip(level, image._imageSize, ktxData); ktxData -= 4; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 7045894633..52f5ee43f5 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -132,7 +132,6 @@ void HTTPResourceRequest::onRequestFinished() { uint64_t size; std::tie(success, size) = parseContentRangeHeader(contentRangeHeader); if (success) { - //qWarning(networking) << "Total http resource size is: " << size; _totalSizeOfResource = size; } else { qWarning(networking) << "Error parsing content-range header: " << contentRangeHeader; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 95bfd0e24d..56897ca4cd 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -667,7 +667,6 @@ void Resource::makeRequest() { } PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); - qDebug() << "Making request to " << _url << " for byte range " << _requestByteRange.fromInclusive << "-" << _requestByteRange.toExclusive; _request = ResourceManager::createResourceRequest(this, _activeUrl); @@ -700,7 +699,6 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota } void Resource::handleReplyFinished() { - qDebug() << "Got response for " << _activeUrl; Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished"); PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), { diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index e6ae6d21a1..9232564514 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -544,7 +544,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } if (_fadeState == FADE_WAITING_TO_START) { - //if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { if (_model->isLoaded()) { if (EntityItem::getEntitiesShouldFadeFunction()()) { _fadeStartTime = usecTimestampNow(); diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 6eb311fa60..aae1f8455f 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -67,10 +67,8 @@ StoragePointer FileStorage::create(const QString& filename, size_t size, const u return std::make_shared(filename); } -// Represents a memory mapped file FileStorage::FileStorage(const QString& filename) : _file(filename) { if (_file.open(QFile::ReadWrite)) { - //qDebug() << ">>> Opening mmapped file: " << filename; _mapped = _file.map(0, _file.size()); if (_mapped) { _valid = true; @@ -83,7 +81,6 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { } FileStorage::~FileStorage() { - //qDebug() << ">>> Closing mmapped file: " << _file.fileName(); if (_mapped) { if (!_file.unmap(_mapped)) { throw std::runtime_error("Unable to unmap file"); From 970be9d2c567a904f0527c304ae893d2aa5ca053 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 21 Apr 2017 01:10:54 -0700 Subject: [PATCH 046/119] Add check for canPopulate to gpu backend --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 6 ++++-- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 4 ++-- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 1179403bff..f1bf76b39d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -437,6 +437,7 @@ void GLVariableAllocationSupport::updateMemoryPressure() { size_t idealMemoryAllocation = 0; bool canDemote = false; bool canPromote = false; + bool canPopulate = false; bool hasTransfers = false; for (const auto& texture : strongTextures) { // Race conditions can still leave nulls in the list, so we need to check @@ -450,7 +451,8 @@ void GLVariableAllocationSupport::updateMemoryPressure() { // Track how much we're actually using totalVariableMemoryAllocation += gltexture->size(); canDemote |= vartexture->canDemote(); - canPromote |= vartexture->canPromote() || (texture->minAvailableMipLevel() < vartexture->_allocatedMip); + canPromote |= vartexture->canPromote(); + canPopulate |= vartexture->canPopulate(); hasTransfers |= vartexture->hasPendingTransfers(); } @@ -464,7 +466,7 @@ void GLVariableAllocationSupport::updateMemoryPressure() { } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { qDebug() << "Demoting"; newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { + } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && ((unallocated != 0 && canPromote) || canPopulate)) { qDebug() << "Promoting"; newState = MemoryPressureState::Undersubscribed; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index bc8467b808..9aad49546e 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -112,11 +112,11 @@ protected: static void manageMemory(); //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - bool canPromote() const { return _allocatedMip > 0 || _populatedMip > 0; } + virtual bool canPopulate() const = 0; + bool canPromote() const { return _allocatedMip > 0; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return _pendingTransfers.size() > 0; } void executeNextTransfer(const TexturePointer& currentTexture); - virtual bool canPopulate() const = 0; virtual void populateTransferQueue() = 0; virtual void promote() = 0; virtual void demote() = 0; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index c0b9ea0e45..dc6d2b3aa7 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -100,7 +100,7 @@ public: GL41VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); ~GL41VariableAllocationTexture(); - bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } + bool canPopulate() const override { return _populatedMip > _allocatedMip && _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } void allocateStorage(uint16 allocatedMip); void syncSampler() const override; void promote() override; From 14f8c91e23442b48bd108dfa5aab4f6113cbe1a1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 21 Apr 2017 01:11:30 -0700 Subject: [PATCH 047/119] Adjust gl45 backend to request interest in mips and keep track of min requested --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 ++ .../src/gpu/gl45/GL45BackendTexture.cpp | 2 ++ .../gpu/gl45/GL45BackendVariableTexture.cpp | 23 +++++++++++-------- libraries/gpu/src/gpu/Texture.cpp | 1 - 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 15e98c3af7..21c429c577 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -117,6 +117,8 @@ public: void allocateStorage(uint16 mip); void copyMipsFromTexture(); + + uint16 _lowestRequestedMip { 0 }; }; #if 0 diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 761cd305bb..299a52eddd 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -119,8 +119,10 @@ GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& GLuint GL45Texture::allocate(const Texture& texture) { GLuint result; glCreateTextures(getGLTextureType(texture), 1, &result); +#ifdef DEBUG auto source = texture.source(); glObjectLabel(GL_TEXTURE, result, source.length(), source.data()); +#endif return result; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 77e42e7fdb..5dfbd6e727 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -75,6 +75,11 @@ void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) { for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) { _size += _gpuObject.evalMipSize(mip); } + + if (!_gpuObject.isStoredMipFaceAvailable(allocatedMip, 0)) { + const_cast(_gpuObject).requestInterestInMip(allocatedMip); + } + Backend::updateTextureGPUMemoryUsage(0, _size); } @@ -98,9 +103,11 @@ void GL45ResourceTexture::syncSampler() const { void GL45ResourceTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - //Q_ASSERT(_allocatedMip > 0); - if (!_gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0)) { - const_cast(_gpuObject).requestInterestInMip(_populatedMip - 1); + Q_ASSERT(_allocatedMip > 0); + uint16_t targetAllocatedMip = _allocatedMip - std::min(_allocatedMip, 2); + if (!_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { + _lowestRequestedMip = targetAllocatedMip; + const_cast(_gpuObject).requestInterestInMip(targetAllocatedMip); return; } GLuint oldId = _id; @@ -109,7 +116,7 @@ void GL45ResourceTexture::promote() { const_cast(_id) = allocate(_gpuObject); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level - allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); + allocateStorage(targetAllocatedMip); uint16_t mips = _gpuObject.getNumMips(); // copy pre-existing mips for (uint16_t mip = _populatedMip; mip < mips; ++mip) { @@ -176,23 +183,21 @@ void GL45ResourceTexture::populateTransferQueue() { const uint8_t maxFace = GLTexture::getFaceCount(_target); uint16_t sourceMip = _populatedMip; - //qDebug() << "populateTransferQueue info : " << _populatedMip << " " << _maxAllocatedMip << " " << _allocatedMip; do { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); bool transferQueued = false; - //qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { const_cast(_gpuObject).requestInterestInMip(sourceMip); - _minRequestedMip = sourceMip; + _lowestRequestedMip = sourceMip; continue; } + _lowestRequestedMip = sourceMip; // If the mip is less than the max transfer size, then just do it in one transfer if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { - qDebug() << "mip is less than max transfer size"; // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); transferQueued = true; @@ -207,14 +212,12 @@ void GL45ResourceTexture::populateTransferQueue() { Q_ASSERT(0 == (mipSize % lines)); uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); uint32_t lineOffset = 0; - qDebug() << "queing up single line transfers " << linesPerTransfer << " " << lineOffset; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; transferQueued = true; } - _minRequestedMip = std::min(_minRequestedMip, sourceMip); } // queue up the sampler and populated mip change for after the transfer has completed diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index bed01a805d..1dbe9db2b4 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -397,7 +397,6 @@ Size Texture::evalTotalSize(uint16 startingMip) const { Size size = 0; uint16 minMipLevel = std::max(getMinMip(), startingMip); uint16 maxMipLevel = getMaxMip(); - qDebug() << " min: " << minMipLevel << " " << maxMipLevel; for (uint16 level = minMipLevel; level <= maxMipLevel; level++) { size += evalMipSize(level); } From 6e307dd6ebac8a82fe30d3e05306725d24c8e551 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 21 Apr 2017 01:17:59 -0700 Subject: [PATCH 048/119] Remove debug logging --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index f1bf76b39d..ef938ecd6d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -461,13 +461,10 @@ void GLVariableAllocationSupport::updateMemoryPressure() { auto newState = MemoryPressureState::Idle; if (hasTransfers) { - qDebug() << "Transferring"; newState = MemoryPressureState::Transfer; } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { - qDebug() << "Demoting"; newState = MemoryPressureState::Oversubscribed; } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && ((unallocated != 0 && canPromote) || canPopulate)) { - qDebug() << "Promoting"; newState = MemoryPressureState::Undersubscribed; } From 927efc88f0de332e36c39a96d61ab02f2b854f09 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 20 Apr 2017 16:41:35 -0700 Subject: [PATCH 049/119] Fix GPUKTXPayload conflict across platforms --- libraries/gpu/src/gpu/Texture.h | 6 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 116 +++++++++++++++++++------- 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 146615d631..5dc8871e9d 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -154,7 +154,7 @@ protected: Desc _desc; }; -enum class TextureUsageType { +enum class TextureUsageType : uint8 { RENDERBUFFER, // Used as attachments to a framebuffer RESOURCE, // Resource textures, like materials... subject to memory manipulation STRICT_RESOURCE, // Resource textures not subject to manipulation, like the normal fitting texture @@ -527,8 +527,8 @@ public: // Serialize a texture into a KTX file static ktx::KTXUniquePointer serialize(const Texture& texture); - static TexturePointer unserialize(const std::string& ktxFile, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); - static TexturePointer unserialize(const std::string& ktxFile, const ktx::KTXDescriptor& descriptor, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); + static TexturePointer unserialize(const std::string& ktxFile); + static TexturePointer unserialize(const std::string& ktxFile, const ktx::KTXDescriptor& descriptor); static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 8e30645837..89b7b86768 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -20,12 +20,66 @@ using PixelsPointer = Texture::PixelsPointer; using KtxStorage = Texture::KtxStorage; struct GPUKTXPayload { + using Version = uint8; + + static const std::string KEY; + static const Version CURRENT_VERSION { 1 }; + static const size_t PADDING { 2 }; + static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + PADDING }; + static_assert(GPUKTXPayload::SIZE == 36, "Packing size may differ between platforms"); + static_assert(GPUKTXPayload::SIZE % 4 == 0, "GPUKTXPayload is not 4 bytes aligned"); + Sampler::Desc _samplerDesc; Texture::Usage _usage; TextureUsageType _usageType; + Byte* serialize(Byte* data) const { + *(Version*)data = CURRENT_VERSION; + data += sizeof(Version); + + memcpy(data, &_samplerDesc, sizeof(Sampler::Desc)); + data += sizeof(Sampler::Desc); + + // We can't copy the bitset in Texture::Usage in a crossplateform manner + // So serialize it manually + *(uint32*)data = _usage._flags.to_ulong(); + data += sizeof(uint32); + + *(TextureUsageType*)data = _usageType; + data += sizeof(TextureUsageType); + + return data + PADDING; + } + + bool unserialize(const Byte* data, size_t size) { + if (size != SIZE) { + return false; + } + + Version version = *(const Version*)data; + if (version != CURRENT_VERSION) { + glm::vec4 borderColor(1.0f); + if (memcmp(&borderColor, data, sizeof(glm::vec4)) == 0) { + memcpy(this, data, sizeof(GPUKTXPayload)); + return true; + } else { + return false; + } + } + data += sizeof(Version); + + memcpy(&_samplerDesc, data, sizeof(Sampler::Desc)); + data += sizeof(Sampler::Desc); + + // We can't copy the bitset in Texture::Usage in a crossplateform manner + // So unserialize it manually + _usage = Texture::Usage(*(const uint32*)data); + data += sizeof(uint32); + + _usageType = *(const TextureUsageType*)data; + return true; + } - static std::string KEY; static bool isGPUKTX(const ktx::KeyValue& val) { return (val._key.compare(KEY) == 0); } @@ -33,17 +87,14 @@ struct GPUKTXPayload { static bool findInKeyValues(const ktx::KeyValues& keyValues, GPUKTXPayload& payload) { auto found = std::find_if(keyValues.begin(), keyValues.end(), isGPUKTX); if (found != keyValues.end()) { - if ((*found)._value.size() == sizeof(GPUKTXPayload)) { - memcpy(&payload, (*found)._value.data(), sizeof(GPUKTXPayload)); - return true; - } + auto value = found->_value; + return payload.unserialize(value.data(), value.size()); } return false; } }; - const std::string gpu::SOURCE_HASH_KEY { "hifi.sourceHash" }; -std::string GPUKTXPayload::KEY{ "hifi.gpu" }; +const std::string GPUKTXPayload::KEY { "hifi.gpu" }; KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { { @@ -253,12 +304,15 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { keyval._samplerDesc = texture.getSampler().getDesc(); keyval._usage = texture.getUsage(); keyval._usageType = texture.getUsageType(); + Byte keyvalPayload[GPUKTXPayload::SIZE]; + keyval.serialize(keyvalPayload); + ktx::KeyValues keyValues; - keyValues.emplace_back(ktx::KeyValue(GPUKTXPayload::KEY, sizeof(GPUKTXPayload), (ktx::Byte*) &keyval)); + keyValues.emplace_back(GPUKTXPayload::KEY, (uint32)GPUKTXPayload::SIZE, (ktx::Byte*) &keyvalPayload); auto hash = texture.sourceHash(); if (!hash.empty()) { - keyValues.emplace_back(ktx::KeyValue(SOURCE_HASH_KEY, static_cast(hash.size()), (ktx::Byte*) hash.c_str())); + keyValues.emplace_back(SOURCE_HASH_KEY, static_cast(hash.size()), (ktx::Byte*) hash.c_str()); } auto ktxBuffer = ktx::KTX::create(header, images, keyValues); @@ -291,17 +345,17 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -TexturePointer Texture::unserialize(const std::string& ktxfile, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) { - std::unique_ptr ktxPointer = ktx::KTX::create(ktx::StoragePointer { new storage::FileStorage(ktxfile.c_str()) }); +TexturePointer Texture::unserialize(const std::string& ktxfile) { + std::unique_ptr ktxPointer = ktx::KTX::create(std::make_shared(ktxfile.c_str())); if (!ktxPointer) { return nullptr; } ktx::KTXDescriptor descriptor { ktxPointer->toDescriptor() }; - return unserialize(ktxfile, ktxPointer->toDescriptor(), usageType, usage, sampler); + return unserialize(ktxfile, ktxPointer->toDescriptor()); } -TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDescriptor& descriptor, TextureUsageType usageType, Usage usage, const Sampler::Desc& sampler) { +TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDescriptor& descriptor) { const auto& header = descriptor.header; Format mipFormat = Format::COLOR_BGRA_32; @@ -327,28 +381,28 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe type = TEX_3D; } - - // If found, use the GPUKTXPayload gpuktxKeyValue; - bool isGPUKTXPayload = GPUKTXPayload::findInKeyValues(descriptor.keyValues, gpuktxKeyValue); + if (!GPUKTXPayload::findInKeyValues(descriptor.keyValues, gpuktxKeyValue)) { + qCWarning(gpulogging) << "Could not find GPUKTX key values."; + return TexturePointer(); + } - auto tex = Texture::create( (isGPUKTXPayload ? gpuktxKeyValue._usageType : usageType), - type, - texelFormat, - header.getPixelWidth(), - header.getPixelHeight(), - header.getPixelDepth(), - 1, // num Samples - header.getNumberOfSlices(), - header.getNumberOfLevels(), - (isGPUKTXPayload ? gpuktxKeyValue._samplerDesc : sampler)); - - tex->setUsage((isGPUKTXPayload ? gpuktxKeyValue._usage : usage)); + auto texture = create(gpuktxKeyValue._usageType, + type, + texelFormat, + header.getPixelWidth(), + header.getPixelHeight(), + header.getPixelDepth(), + 1, // num Samples + header.getNumberOfSlices(), + header.getNumberOfLevels(), + gpuktxKeyValue._samplerDesc); + texture->setUsage(gpuktxKeyValue._usage); // Assing the mips availables - tex->setStoredMipFormat(mipFormat); - tex->setKtxBacking(ktxfile); - return tex; + texture->setStoredMipFormat(mipFormat); + texture->setKtxBacking(ktxfile); + return texture; } bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { From 3c5754282f99d467d2f7c688f53f3c4d94838462 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Apr 2017 16:18:51 -0700 Subject: [PATCH 050/119] write the source hash in ktx header in binary --- libraries/gpu/src/gpu/Texture.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 5dc8871e9d..3c82f67c86 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -35,6 +35,7 @@ namespace ktx { namespace gpu { extern const std::string SOURCE_HASH_KEY; +const uint8 SOURCE_HASH_BYTES = 16; // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated // with the cube texture diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 89b7b86768..6828af4cc4 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -13,7 +13,10 @@ #include "Texture.h" #include +#include + #include + using namespace gpu; using PixelsPointer = Texture::PixelsPointer; @@ -312,7 +315,10 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { auto hash = texture.sourceHash(); if (!hash.empty()) { - keyValues.emplace_back(SOURCE_HASH_KEY, static_cast(hash.size()), (ktx::Byte*) hash.c_str()); + // the sourceHash is an std::string in hex + // we use QByteArray to take the hex and turn it into the smaller binary representation (16 bytes) + auto binaryHash = QByteArray::fromHex(QByteArray::fromStdString(hash)); + keyValues.emplace_back(SOURCE_HASH_KEY, static_cast(binaryHash.size()), (ktx::Byte*) binaryHash.data()); } auto ktxBuffer = ktx::KTX::create(header, images, keyValues); From e708a71b6482b4baddfa96e3b1ade51f8450e471 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Apr 2017 18:19:29 -0700 Subject: [PATCH 051/119] handle 16 byte hash while reading in TextureCache --- .../model-networking/src/model-networking/TextureCache.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 761f55068b..c8765e0a52 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -559,13 +559,16 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { }); std::string filename; std::string hash; - if (found == keyValues.end() || found->_value.size() != 32) { + if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { qWarning("Invalid source hash key found, bailing"); _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); return; } else { - hash = filename = std::string(reinterpret_cast(found->_value.data()), 32); + // at this point the source hash is in binary 16-byte form + // and we need it in a hexadecimal string + auto binaryHash = QByteArray(reinterpret_cast(found->_value.data()), gpu::SOURCE_HASH_BYTES); + hash = filename = binaryHash.toHex().toStdString(); } auto textureCache = DependencyManager::get(); From 397a29039effa0768fba2220488144d58d05d524 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 17 Apr 2017 19:08:06 -0700 Subject: [PATCH 052/119] add support for byte range requests to ATP --- .../src/assets/SendAssetTask.cpp | 62 ++++++-- libraries/networking/src/AssetClient.cpp | 5 +- libraries/networking/src/AssetClient.h | 3 +- libraries/networking/src/AssetRequest.cpp | 133 +++++++----------- libraries/networking/src/AssetRequest.h | 10 +- libraries/networking/src/ByteRange.h | 24 ++++ libraries/networking/src/ResourceRequest.h | 7 +- 7 files changed, 132 insertions(+), 112 deletions(-) create mode 100644 libraries/networking/src/ByteRange.h diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index ca8733d660..4a49deabde 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -11,6 +11,8 @@ #include "SendAssetTask.h" +#include + #include #include @@ -21,6 +23,7 @@ #include #include "AssetUtils.h" +#include "ByteRange.h" #include "ClientServerUtils.h" SendAssetTask::SendAssetTask(QSharedPointer message, const SharedNodePointer& sendToNode, const QDir& resourcesDir) : @@ -34,20 +37,21 @@ SendAssetTask::SendAssetTask(QSharedPointer message, const Shar void SendAssetTask::run() { MessageID messageID; - DataOffset start, end; - + ByteRange byteRange; + _message->readPrimitive(&messageID); QByteArray assetHash = _message->read(SHA256_HASH_LENGTH); // `start` and `end` indicate the range of data to retrieve for the asset identified by `assetHash`. // `start` is inclusive, `end` is exclusive. Requesting `start` = 1, `end` = 10 will retrieve 9 bytes of data, // starting at index 1. - _message->readPrimitive(&start); - _message->readPrimitive(&end); + _message->readPrimitive(&byteRange.fromInclusive); + _message->readPrimitive(&byteRange.toExclusive); QString hexHash = assetHash.toHex(); - qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from " << start << " to " << end; + qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from " + << byteRange.fromInclusive << " to " << byteRange.toExclusive; qDebug() << "Starting task to send asset: " << hexHash << " for messageID " << messageID; auto replyPacketList = NLPacketList::create(PacketType::AssetGetReply, QByteArray(), true, true); @@ -56,7 +60,7 @@ void SendAssetTask::run() { replyPacketList->writePrimitive(messageID); - if (end <= start) { + if (byteRange.toExclusive < byteRange.fromInclusive) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); } else { QString filePath = _resourcesDir.filePath(QString(hexHash)); @@ -64,15 +68,47 @@ void SendAssetTask::run() { QFile file { filePath }; if (file.open(QIODevice::ReadOnly)) { - if (file.size() < end) { + if (byteRange.isSet()) { + // if the byte range is not set, force it to be from 0 to the end of the file + byteRange.fromInclusive = 0; + byteRange.toExclusive = file.size(); + } + + if (file.size() < std::abs(byteRange.toExclusive)) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); - qCDebug(networking) << "Bad byte range: " << hexHash << " " << start << ":" << end; + qCDebug(networking) << "Bad byte range: " << hexHash << " " + << byteRange.fromInclusive << ":" << byteRange.toExclusive; } else { - auto size = end - start; - file.seek(start); - replyPacketList->writePrimitive(AssetServerError::NoError); - replyPacketList->writePrimitive(size); - replyPacketList->write(file.read(size)); + // we have a valid byte range, handle it and send the asset + auto size = byteRange.size(); + + if (byteRange.fromInclusive > 0) { + // this range is positive, meaning we just need to seek into the file and then read from there + file.seek(byteRange.fromInclusive); + replyPacketList->writePrimitive(AssetServerError::NoError); + replyPacketList->writePrimitive(size); + replyPacketList->write(file.read(size)); + } else { + // this range is negative, at least the first part of the read will be back into the end of the file + + // seek to the part of the file where the negative range begins + file.seek(file.size() + byteRange.fromInclusive); + + replyPacketList->writePrimitive(AssetServerError::NoError); + replyPacketList->writePrimitive(size); + + // first write everything from the negative range to the end of the file + replyPacketList->write(file.read(-byteRange.fromInclusive)); + + if (byteRange.toExclusive != 0) { + // this range has a portion that is at the front of the file + + // seek to the beginning and read what is left over + file.seek(0); + replyPacketList->write(file.read(byteRange.toExclusive)); + } + } + qCDebug(networking) << "Sending asset: " << hexHash; } file.close(); diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 37b1af0996..48f8bb87f9 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -67,7 +67,6 @@ void AssetClient::init() { } } - void AssetClient::cacheInfoRequest(QObject* reciever, QString slot) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "cacheInfoRequest", Qt::QueuedConnection, @@ -182,8 +181,8 @@ RenameMappingRequest* AssetClient::createRenameMappingRequest(const AssetPath& o return request; } -AssetRequest* AssetClient::createRequest(const AssetHash& hash) { - auto request = new AssetRequest(hash); +AssetRequest* AssetClient::createRequest(const AssetHash& hash, ByteRange byteRange) { + auto request = new AssetRequest(hash, byteRange); // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) request->moveToThread(thread()); diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index c0d58cd8e6..b204fab47e 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -21,6 +21,7 @@ #include #include "AssetUtils.h" +#include "ByteRange.h" #include "ClientServerUtils.h" #include "LimitedNodeList.h" #include "Node.h" @@ -55,7 +56,7 @@ public: Q_INVOKABLE DeleteMappingsRequest* createDeleteMappingsRequest(const AssetPathList& paths); Q_INVOKABLE SetMappingRequest* createSetMappingRequest(const AssetPath& path, const AssetHash& hash); Q_INVOKABLE RenameMappingRequest* createRenameMappingRequest(const AssetPath& oldPath, const AssetPath& newPath); - Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash); + Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash, ByteRange byteRange = ByteRange()); Q_INVOKABLE AssetUpload* createUpload(const QString& filename); Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data); diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 8d663933ca..e54a058ac2 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -23,10 +23,12 @@ static int requestID = 0; -AssetRequest::AssetRequest(const QString& hash) : +AssetRequest::AssetRequest(const QString& hash, ByteRange byteRange) : _requestID(++requestID), - _hash(hash) + _hash(hash), + _byteRange(byteRange) { + } AssetRequest::~AssetRequest() { @@ -34,9 +36,6 @@ AssetRequest::~AssetRequest() { if (_assetRequestID) { assetClient->cancelGetAssetRequest(_assetRequestID); } - if (_assetInfoRequestID) { - assetClient->cancelGetAssetInfoRequest(_assetInfoRequestID); - } } void AssetRequest::start() { @@ -62,108 +61,74 @@ void AssetRequest::start() { // Try to load from cache _data = loadFromCache(getUrl()); if (!_data.isNull()) { - _info.hash = _hash; - _info.size = _data.size(); _error = NoError; _state = Finished; emit finished(this); return; } - - _state = WaitingForInfo; - + + _state = WaitingForData; + auto assetClient = DependencyManager::get(); - _assetInfoRequestID = assetClient->getAssetInfo(_hash, - [this](bool responseReceived, AssetServerError serverError, AssetInfo info) { + auto that = QPointer(this); // Used to track the request's lifetime + auto hash = _hash; - _assetInfoRequestID = INVALID_MESSAGE_ID; + _assetRequestID = assetClient->getAsset(_hash, _byteRange.fromInclusive, _byteRange.toExclusive, + [this, that, hash](bool responseReceived, AssetServerError serverError, const QByteArray& data) { - _info = info; + if (!that) { + qCWarning(asset_client) << "Got reply for dead asset request " << hash << "- error code" << _error; + // If the request is dead, return + return; + } + _assetRequestID = INVALID_MESSAGE_ID; if (!responseReceived) { _error = NetworkError; } else if (serverError != AssetServerError::NoError) { - switch(serverError) { + switch (serverError) { case AssetServerError::AssetNotFound: _error = NotFound; break; + case AssetServerError::InvalidByteRange: + _error = InvalidByteRange; + break; default: _error = UnknownError; break; } - } + } else { + if (_byteRange.isSet()) { + // we had a byte range, the size of the data does not match what we expect, so we return an error + if (data.size() != _byteRange.size()) { + _error = SizeVerificationFailed; + } + } else if (hashData(data).toHex() != _hash) { + // the hash of the received data does not match what we expect, so we return an error + _error = HashVerificationFailed; + } + if (_error == NoError) { + _data = data; + _totalReceived += data.size(); + emit progress(_totalReceived, data.size()); + + saveToCache(getUrl(), data); + } + } + if (_error != NoError) { - qCWarning(asset_client) << "Got error retrieving asset info for" << _hash; - _state = Finished; - emit finished(this); - + qCWarning(asset_client) << "Got error retrieving asset" << _hash << "- error code" << _error; + } + + _state = Finished; + emit finished(this); + }, [this, that](qint64 totalReceived, qint64 total) { + if (!that) { + // If the request is dead, return return; } - - _state = WaitingForData; - _data.resize(info.size); - - qCDebug(asset_client) << "Got size of " << _hash << " : " << info.size << " bytes"; - - int start = 0, end = _info.size; - - auto assetClient = DependencyManager::get(); - auto that = QPointer(this); // Used to track the request's lifetime - auto hash = _hash; - _assetRequestID = assetClient->getAsset(_hash, start, end, - [this, that, hash, start, end](bool responseReceived, AssetServerError serverError, const QByteArray& data) { - if (!that) { - qCWarning(asset_client) << "Got reply for dead asset request " << hash << "- error code" << _error; - // If the request is dead, return - return; - } - _assetRequestID = INVALID_MESSAGE_ID; - - if (!responseReceived) { - _error = NetworkError; - } else if (serverError != AssetServerError::NoError) { - switch (serverError) { - case AssetServerError::AssetNotFound: - _error = NotFound; - break; - case AssetServerError::InvalidByteRange: - _error = InvalidByteRange; - break; - default: - _error = UnknownError; - break; - } - } else { - Q_ASSERT(data.size() == (end - start)); - - // we need to check the hash of the received data to make sure it matches what we expect - if (hashData(data).toHex() == _hash) { - memcpy(_data.data() + start, data.constData(), data.size()); - _totalReceived += data.size(); - emit progress(_totalReceived, _info.size); - - saveToCache(getUrl(), data); - } else { - // hash doesn't match - we have an error - _error = HashVerificationFailed; - } - - } - - if (_error != NoError) { - qCWarning(asset_client) << "Got error retrieving asset" << _hash << "- error code" << _error; - } - - _state = Finished; - emit finished(this); - }, [this, that](qint64 totalReceived, qint64 total) { - if (!that) { - // If the request is dead, return - return; - } - emit progress(totalReceived, total); - }); + emit progress(totalReceived, total); }); } diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index 1632a55336..5120b8066e 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -17,15 +17,15 @@ #include #include "AssetClient.h" - #include "AssetUtils.h" +#include "ByteRange.h" + class AssetRequest : public QObject { Q_OBJECT public: enum State { NotStarted = 0, - WaitingForInfo, WaitingForData, Finished }; @@ -36,11 +36,12 @@ public: InvalidByteRange, InvalidHash, HashVerificationFailed, + SizeVerificationFailed, NetworkError, UnknownError }; - AssetRequest(const QString& hash); + AssetRequest(const QString& hash, ByteRange byteRange); virtual ~AssetRequest() override; Q_INVOKABLE void start(); @@ -59,13 +60,12 @@ private: int _requestID; State _state = NotStarted; Error _error = NoError; - AssetInfo _info; uint64_t _totalReceived { 0 }; QString _hash; QByteArray _data; int _numPendingRequests { 0 }; MessageID _assetRequestID { INVALID_MESSAGE_ID }; - MessageID _assetInfoRequestID { INVALID_MESSAGE_ID }; + ByteRange _byteRange; }; #endif diff --git a/libraries/networking/src/ByteRange.h b/libraries/networking/src/ByteRange.h new file mode 100644 index 0000000000..2fe5264a49 --- /dev/null +++ b/libraries/networking/src/ByteRange.h @@ -0,0 +1,24 @@ +// +// ByteRange.h +// libraries/networking/src +// +// Created by Stephen Birarda on 4/17/17. +// Copyright 2017 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_ByteRange_h +#define hifi_ByteRange_h + +struct ByteRange { + int64_t fromInclusive { 0 }; + int64_t toExclusive { 0 }; + + bool isSet() { return fromInclusive < 0 || fromInclusive < toExclusive; } + int64_t size() { return toExclusive - fromInclusive; } +}; + + +#endif // hifi_ByteRange_h diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 01ca62cf05..77fc8d2591 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -17,12 +17,7 @@ #include -struct ByteRange { - int64_t fromInclusive { 0 }; - int64_t toExclusive { 0 }; - - bool isSet() { return fromInclusive < 0 || fromInclusive < toExclusive; } -}; +#include "ByteRange.h" class ResourceRequest : public QObject { Q_OBJECT From 8145e416f9edcd428374c4da29731f838ebc631a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 17 Apr 2017 19:14:24 -0700 Subject: [PATCH 053/119] check both sides of range for invalid byte range --- assignment-client/src/assets/SendAssetTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index 4a49deabde..3196c12d97 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -74,7 +74,7 @@ void SendAssetTask::run() { byteRange.toExclusive = file.size(); } - if (file.size() < std::abs(byteRange.toExclusive)) { + if (file.size() < std::abs(byteRange.fromInclusive) || file.size() < byteRange.toExclusive) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); qCDebug(networking) << "Bad byte range: " << hexHash << " " << byteRange.fromInclusive << ":" << byteRange.toExclusive; From 229a481232d85877fd9f8b91e6d30be5b7586893 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 17 Apr 2017 19:17:45 -0700 Subject: [PATCH 054/119] add byte range handling to FileResourceRequest --- .../networking/src/FileResourceRequest.cpp | 32 ++++++++++++++++++- libraries/networking/src/ResourceRequest.h | 1 + 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/FileResourceRequest.cpp b/libraries/networking/src/FileResourceRequest.cpp index 58a2074103..3895fbc34d 100644 --- a/libraries/networking/src/FileResourceRequest.cpp +++ b/libraries/networking/src/FileResourceRequest.cpp @@ -11,6 +11,8 @@ #include "FileResourceRequest.h" +#include + #include void FileResourceRequest::doSend() { @@ -25,7 +27,35 @@ void FileResourceRequest::doSend() { QFile file(filename); if (file.exists()) { if (file.open(QFile::ReadOnly)) { - _data = file.readAll(); + + if (!_byteRange.isSet()) { + // no byte range, read the whole file + _data = file.readAll(); + } else { + if (file.size() < std::abs(_byteRange.fromInclusive) || file.size() < _byteRange.toExclusive) { + _result = ResourceRequest::InvalidByteRange; + } else { + // we have a byte range to handle + if (_byteRange.fromInclusive > 0) { + // this is a positive byte range, simply skip to that part of the file and read from there + file.seek(_byteRange.fromInclusive); + _data = file.read(_byteRange.size()); + } else { + // this is a negative byte range, we'll need to grab data from the end of the file first + file.seek(file.size() + _byteRange.fromInclusive); + _data = file.read(-_byteRange.fromInclusive); + + if (_byteRange.toExclusive > 0) { + // there is additional data to read from the front of the file + // handle that now + file.seek(0); + _data.append(file.read(_byteRange.toExclusive)); + } + } + } + + } + _result = ResourceRequest::Success; } else { _result = ResourceRequest::AccessDenied; diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 77fc8d2591..ef40cb3455 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -37,6 +37,7 @@ public: Timeout, ServerUnavailable, AccessDenied, + InvalidByteRange, InvalidURL, NotFound }; From 7ae8c741c5793366e946f001332fdbc65d159d1c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 17 Apr 2017 19:30:04 -0700 Subject: [PATCH 055/119] pass the byte range to asset client from AssetResourceRequest --- libraries/networking/src/AssetResourceRequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 540fb4767f..092e0ccb3d 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -114,7 +114,7 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { void AssetResourceRequest::requestHash(const AssetHash& hash) { // Make request to atp auto assetClient = DependencyManager::get(); - _assetRequest = assetClient->createRequest(hash); + _assetRequest = assetClient->createRequest(hash, _byteRange); connect(_assetRequest, &AssetRequest::progress, this, &AssetResourceRequest::onDownloadProgress); connect(_assetRequest, &AssetRequest::finished, this, [this](AssetRequest* req) { From fab1bdaeb7a148f79181a41b0b460e4eb8d27d8d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 17 Apr 2017 19:44:52 -0700 Subject: [PATCH 056/119] push asset server packet versions for range requests --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 3ad4dbf28d..863f1bfda6 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -64,7 +64,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AssetGetInfo: case PacketType::AssetGet: case PacketType::AssetUpload: - return static_cast(AssetServerPacketVersion::VegasCongestionControl); + return static_cast(AssetServerPacketVersion::RangeRequestSupport); case PacketType::NodeIgnoreRequest: return 18; // Introduction of node ignore request (which replaced an unused packet tpye) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 074876862f..87af3513b5 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -214,7 +214,8 @@ enum class EntityQueryPacketVersion: PacketVersion { }; enum class AssetServerPacketVersion: PacketVersion { - VegasCongestionControl = 19 + VegasCongestionControl = 19, + RangeRequestSupport }; enum class AvatarMixerPacketVersion : PacketVersion { From 06ce63f421221d06a71e15de623f9b6c9a5d8d6f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 18 Apr 2017 10:34:40 -0700 Subject: [PATCH 057/119] fix byte range set check in SendAssetTask --- assignment-client/src/assets/SendAssetTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index 3196c12d97..89d59360b9 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -68,7 +68,7 @@ void SendAssetTask::run() { QFile file { filePath }; if (file.open(QIODevice::ReadOnly)) { - if (byteRange.isSet()) { + if (!byteRange.isSet()) { // if the byte range is not set, force it to be from 0 to the end of the file byteRange.fromInclusive = 0; byteRange.toExclusive = file.size(); From 7a3219d8f99f7bf352c3ddcde2f36e0be8d0c1c3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 18 Apr 2017 10:37:15 -0700 Subject: [PATCH 058/119] force a negative to in ATP byte range to be invalid --- assignment-client/src/assets/SendAssetTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index 89d59360b9..0222849915 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -60,7 +60,7 @@ void SendAssetTask::run() { replyPacketList->writePrimitive(messageID); - if (byteRange.toExclusive < byteRange.fromInclusive) { + if (byteRange.toExclusive < byteRange.fromInclusive || byteRange.toExclusive < 0) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); } else { QString filePath = _resourcesDir.filePath(QString(hexHash)); From 814970c4e277c9e577f92c7e75321d2a4a5be901 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 18 Apr 2017 12:36:38 -0700 Subject: [PATCH 059/119] cleanup invalid byte range handling --- .../src/assets/SendAssetTask.cpp | 21 ++++--- libraries/networking/src/ByteRange.h | 23 ++++++++ .../networking/src/FileResourceRequest.cpp | 55 ++++++++++--------- 3 files changed, 61 insertions(+), 38 deletions(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index 0222849915..cb99636e83 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -60,7 +60,7 @@ void SendAssetTask::run() { replyPacketList->writePrimitive(messageID); - if (byteRange.toExclusive < byteRange.fromInclusive || byteRange.toExclusive < 0) { + if (!byteRange.isValid()) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); } else { QString filePath = _resourcesDir.filePath(QString(hexHash)); @@ -74,15 +74,22 @@ void SendAssetTask::run() { byteRange.toExclusive = file.size(); } - if (file.size() < std::abs(byteRange.fromInclusive) || file.size() < byteRange.toExclusive) { + // check if we're being asked to read data that we just don't have + // because of the file size + if (file.size() < byteRange.fromInclusive || file.size() < byteRange.toExclusive) { replyPacketList->writePrimitive(AssetServerError::InvalidByteRange); qCDebug(networking) << "Bad byte range: " << hexHash << " " << byteRange.fromInclusive << ":" << byteRange.toExclusive; } else { // we have a valid byte range, handle it and send the asset + + // first fixup the range based on the now known file size + byteRange.fixupRange(file.size()); + auto size = byteRange.size(); if (byteRange.fromInclusive > 0) { + // this range is positive, meaning we just need to seek into the file and then read from there file.seek(byteRange.fromInclusive); replyPacketList->writePrimitive(AssetServerError::NoError); @@ -98,15 +105,7 @@ void SendAssetTask::run() { replyPacketList->writePrimitive(size); // first write everything from the negative range to the end of the file - replyPacketList->write(file.read(-byteRange.fromInclusive)); - - if (byteRange.toExclusive != 0) { - // this range has a portion that is at the front of the file - - // seek to the beginning and read what is left over - file.seek(0); - replyPacketList->write(file.read(byteRange.toExclusive)); - } + replyPacketList->write(file.read(size)); } qCDebug(networking) << "Sending asset: " << hexHash; diff --git a/libraries/networking/src/ByteRange.h b/libraries/networking/src/ByteRange.h index 2fe5264a49..3993bac324 100644 --- a/libraries/networking/src/ByteRange.h +++ b/libraries/networking/src/ByteRange.h @@ -18,6 +18,29 @@ struct ByteRange { bool isSet() { return fromInclusive < 0 || fromInclusive < toExclusive; } int64_t size() { return toExclusive - fromInclusive; } + + // byte ranges are invalid if: + // (1) the toExclusive of the range is negative + // (2) the toExclusive of the range is less than the fromInclusive, and isn't zero + // (3) the fromExclusive of the range is negative, and the toExclusive isn't zero + bool isValid() { + return toExclusive < 0 + || (toExclusive < fromInclusive && toExclusive != 0) + || (fromInclusive < 0 && toExclusive != 0); + } + + void fixupRange(int64_t fileSize) { + if (fromInclusive > 0 && toExclusive == 0) { + // we have a left side of the range that is non-zero + // if the RHS of the range is zero, set it to the end of the file now + toExclusive = fileSize; + } else if (-fromInclusive >= fileSize) { + // we have a negative range that is equal or greater than the full size of the file + // so we just set this to be a range across the entire file, from 0 + fromInclusive = 0; + toExclusive = fileSize; + } + } }; diff --git a/libraries/networking/src/FileResourceRequest.cpp b/libraries/networking/src/FileResourceRequest.cpp index 3895fbc34d..9e2b8f4ab1 100644 --- a/libraries/networking/src/FileResourceRequest.cpp +++ b/libraries/networking/src/FileResourceRequest.cpp @@ -23,45 +23,46 @@ void FileResourceRequest::doSend() { if (filename.isEmpty()) { filename = _url.toString(); } - - QFile file(filename); - if (file.exists()) { - if (file.open(QFile::ReadOnly)) { - if (!_byteRange.isSet()) { - // no byte range, read the whole file - _data = file.readAll(); - } else { - if (file.size() < std::abs(_byteRange.fromInclusive) || file.size() < _byteRange.toExclusive) { + if (!_byteRange.isValid()) { + _result = ResourceRequest::InvalidByteRange; + } else { + QFile file(filename); + if (file.exists()) { + if (file.open(QFile::ReadOnly)) { + + if (file.size() < _byteRange.fromInclusive || file.size() < _byteRange.toExclusive) { _result = ResourceRequest::InvalidByteRange; } else { - // we have a byte range to handle - if (_byteRange.fromInclusive > 0) { - // this is a positive byte range, simply skip to that part of the file and read from there - file.seek(_byteRange.fromInclusive); - _data = file.read(_byteRange.size()); + if (!_byteRange.isSet()) { + // no byte range, read the whole file + _data = file.readAll(); } else { - // this is a negative byte range, we'll need to grab data from the end of the file first - file.seek(file.size() + _byteRange.fromInclusive); - _data = file.read(-_byteRange.fromInclusive); + // we have a byte range to handle - if (_byteRange.toExclusive > 0) { - // there is additional data to read from the front of the file - // handle that now - file.seek(0); - _data.append(file.read(_byteRange.toExclusive)); + // fix it up based on the known size of the file + _byteRange.fixupRange(file.size()); + + if (_byteRange.fromInclusive > 0) { + // this is a positive byte range, simply skip to that part of the file and read from there + file.seek(_byteRange.fromInclusive); + _data = file.read(_byteRange.size()); + } else { + // this is a negative byte range, we'll need to grab data from the end of the file first + file.seek(file.size() + _byteRange.fromInclusive); + _data = file.read(_byteRange.size()); } } + + _result = ResourceRequest::Success; } + } else { + _result = ResourceRequest::AccessDenied; } - - _result = ResourceRequest::Success; } else { - _result = ResourceRequest::AccessDenied; + _result = ResourceRequest::NotFound; } - } else { - _result = ResourceRequest::NotFound; } _state = Finished; From d9c5997b638ba80b27cfa67f5453664e07d9d2ed Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 18 Apr 2017 13:10:45 -0700 Subject: [PATCH 060/119] fix references to TextureCache in NetworkTexture --- .../model-networking/src/model-networking/TextureCache.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c8765e0a52..0eda44019b 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -458,8 +458,7 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxResourceState == LOADING_INITIAL_DATA) { _ktxHighMipRequestFinished = true; maybeHandleFinishedInitialLoad(); - } - else if (_ktxResourceState == REQUESTING_MIP) { + } else if (_ktxResourceState == REQUESTING_MIP) { Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); TextureCache::requestCompleted(_self); From 3928e116117606fac9d677d0ad8f54ec5038f3ec Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 18 Apr 2017 13:51:08 -0700 Subject: [PATCH 061/119] fix valid byte range check and send asset from 0 --- assignment-client/src/assets/SendAssetTask.cpp | 2 +- libraries/networking/src/ByteRange.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index cb99636e83..5a394eaa07 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -88,7 +88,7 @@ void SendAssetTask::run() { auto size = byteRange.size(); - if (byteRange.fromInclusive > 0) { + if (byteRange.fromInclusive >= 0) { // this range is positive, meaning we just need to seek into the file and then read from there file.seek(byteRange.fromInclusive); diff --git a/libraries/networking/src/ByteRange.h b/libraries/networking/src/ByteRange.h index 3993bac324..b5e214605d 100644 --- a/libraries/networking/src/ByteRange.h +++ b/libraries/networking/src/ByteRange.h @@ -24,9 +24,9 @@ struct ByteRange { // (2) the toExclusive of the range is less than the fromInclusive, and isn't zero // (3) the fromExclusive of the range is negative, and the toExclusive isn't zero bool isValid() { - return toExclusive < 0 - || (toExclusive < fromInclusive && toExclusive != 0) - || (fromInclusive < 0 && toExclusive != 0); + return toExclusive >= 0 + && (toExclusive >= fromInclusive || toExclusive == 0) + && (fromInclusive >= 0 || toExclusive == 0); } void fixupRange(int64_t fileSize) { From 6fb074715e575a530548ed106341989542be9525 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 21 Apr 2017 15:28:29 -0700 Subject: [PATCH 062/119] Fix alpha rendering bug --- libraries/gpu/src/gpu/Texture_ktx.cpp | 3 ++- libraries/render-utils/src/MeshPartPayload.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 6828af4cc4..b2b08ca170 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -11,12 +11,13 @@ #include "Texture.h" -#include #include #include +#include "GPULogging.h" + using namespace gpu; using PixelsPointer = Texture::PixelsPointer; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 9232564514..51ce0fffa7 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -544,7 +544,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } if (_fadeState == FADE_WAITING_TO_START) { - if (_model->isLoaded()) { + if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { if (EntityItem::getEntitiesShouldFadeFunction()()) { _fadeStartTime = usecTimestampNow(); _fadeState = FADE_IN_PROGRESS; From 8269e01474da975b48a35db8afaca3a261644b5d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 21 Apr 2017 17:08:50 -0700 Subject: [PATCH 063/119] Fix atp-get compile error --- libraries/networking/src/AssetRequest.h | 2 +- tests/render-texture-load/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index 5120b8066e..e617d75157 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -41,7 +41,7 @@ public: UnknownError }; - AssetRequest(const QString& hash, ByteRange byteRange); + AssetRequest(const QString& hash, ByteRange byteRange = ByteRange()); virtual ~AssetRequest() override; Q_INVOKABLE void start(); diff --git a/tests/render-texture-load/CMakeLists.txt b/tests/render-texture-load/CMakeLists.txt index b73b67f56c..1f0c0a069a 100644 --- a/tests/render-texture-load/CMakeLists.txt +++ b/tests/render-texture-load/CMakeLists.txt @@ -10,7 +10,7 @@ setup_hifi_project(Quick Gui OpenGL) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image) +link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics ktx image) package_libraries_for_deployment() From 194541b2d0758e419e1b2408689925f708219be1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 21 Apr 2017 17:30:59 -0700 Subject: [PATCH 064/119] remove temp code --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 20e4f4bc18..0d286c46eb 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -31,7 +31,7 @@ const float METERS_TO_INCHES = 39.3701f; static uint32_t _currentWebCount { 0 }; // Don't allow more than 100 concurrent web views -static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 0; +static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20; // If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND; @@ -71,7 +71,7 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { bool RenderableWebEntityItem::buildWebSurface(QSharedPointer renderer) { if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) { - //qWarning() << "Too many concurrent web views to create new view"; + qWarning() << "Too many concurrent web views to create new view"; return false; } QString javaScriptToInject; From 043c587395cf0672d7a66fc97984c64cd6d049a1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 21 Apr 2017 19:16:56 -0700 Subject: [PATCH 065/119] Fix byte range for file resource requests --- .../src/assets/SendAssetTask.cpp | 12 +++------ libraries/networking/src/ByteRange.h | 6 +++++ .../networking/src/FileResourceRequest.cpp | 27 +++++++------------ 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index 5a394eaa07..eab88e0d46 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -68,11 +68,9 @@ void SendAssetTask::run() { QFile file { filePath }; if (file.open(QIODevice::ReadOnly)) { - if (!byteRange.isSet()) { - // if the byte range is not set, force it to be from 0 to the end of the file - byteRange.fromInclusive = 0; - byteRange.toExclusive = file.size(); - } + + // first fixup the range based on the now known file size + byteRange.fixupRange(file.size()); // check if we're being asked to read data that we just don't have // because of the file size @@ -82,10 +80,6 @@ void SendAssetTask::run() { << byteRange.fromInclusive << ":" << byteRange.toExclusive; } else { // we have a valid byte range, handle it and send the asset - - // first fixup the range based on the now known file size - byteRange.fixupRange(file.size()); - auto size = byteRange.size(); if (byteRange.fromInclusive >= 0) { diff --git a/libraries/networking/src/ByteRange.h b/libraries/networking/src/ByteRange.h index b5e214605d..77932a8c28 100644 --- a/libraries/networking/src/ByteRange.h +++ b/libraries/networking/src/ByteRange.h @@ -30,6 +30,12 @@ struct ByteRange { } void fixupRange(int64_t fileSize) { + if (!isSet()) { + // if the byte range is not set, force it to be from 0 to the end of the file + fromInclusive = 0; + toExclusive = fileSize; + } + if (fromInclusive > 0 && toExclusive == 0) { // we have a left side of the range that is non-zero // if the RHS of the range is zero, set it to the end of the file now diff --git a/libraries/networking/src/FileResourceRequest.cpp b/libraries/networking/src/FileResourceRequest.cpp index 9e2b8f4ab1..1e549e5fa3 100644 --- a/libraries/networking/src/FileResourceRequest.cpp +++ b/libraries/networking/src/FileResourceRequest.cpp @@ -34,24 +34,17 @@ void FileResourceRequest::doSend() { if (file.size() < _byteRange.fromInclusive || file.size() < _byteRange.toExclusive) { _result = ResourceRequest::InvalidByteRange; } else { - if (!_byteRange.isSet()) { - // no byte range, read the whole file - _data = file.readAll(); + // fix it up based on the known size of the file + _byteRange.fixupRange(file.size()); + + if (_byteRange.fromInclusive >= 0) { + // this is a positive byte range, simply skip to that part of the file and read from there + file.seek(_byteRange.fromInclusive); + _data = file.read(_byteRange.size()); } else { - // we have a byte range to handle - - // fix it up based on the known size of the file - _byteRange.fixupRange(file.size()); - - if (_byteRange.fromInclusive > 0) { - // this is a positive byte range, simply skip to that part of the file and read from there - file.seek(_byteRange.fromInclusive); - _data = file.read(_byteRange.size()); - } else { - // this is a negative byte range, we'll need to grab data from the end of the file first - file.seek(file.size() + _byteRange.fromInclusive); - _data = file.read(_byteRange.size()); - } + // this is a negative byte range, we'll need to grab data from the end of the file first + file.seek(file.size() + _byteRange.fromInclusive); + _data = file.read(_byteRange.size()); } _result = ResourceRequest::Success; From e768b720ea0c407919296304635fdb76d20765bd Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 24 Apr 2017 10:51:54 -0700 Subject: [PATCH 066/119] Fix load priority for ktx loading --- .../model-networking/src/model-networking/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 0eda44019b..02e1427ddb 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -399,7 +399,7 @@ void NetworkTexture::startRequestForNextMipLevel() { if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { _ktxResourceState = PENDING_MIP_REQUEST; - setLoadPriority(this, -_originalKtxDescriptor->header.numberOfMipmapLevels + _lowestKnownPopulatedMip); + setLoadPriority(this, -static_cast(_originalKtxDescriptor->header.numberOfMipmapLevels) + _lowestKnownPopulatedMip); init(); TextureCache::attemptRequest(_self); From 430edb1560dcaefb3d3505dadbe999f6286aa596 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 24 Apr 2017 10:52:16 -0700 Subject: [PATCH 067/119] Fix loss-of-precision warning in TextureCache --- .../model-networking/src/model-networking/TextureCache.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 02e1427ddb..d42cb51b6e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -619,14 +619,13 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); ktxData += ktxHighMipData.size(); // TODO Move image offset calculation to ktx ImageDescriptor - int level; - for (level = images.size() - 1; level >= 0; --level) { + for (auto level = images.size() - 1; level >= 0; --level) { auto& image = images[level]; if (image._imageSize > imageSizeRemaining) { break; } ktxData -= image._imageSize; - texture->assignStoredMip(level, image._imageSize, ktxData); + texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); ktxData -= 4; imageSizeRemaining -= (image._imageSize + 4); } From c10e394ad7311dd0c60cbd1542a695c952cfc824 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 24 Apr 2017 12:07:40 -0700 Subject: [PATCH 068/119] Fix unfailable condition in for loop --- .../model-networking/src/model-networking/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index d42cb51b6e..f9ee9efe3f 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -619,7 +619,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { uint8_t* ktxData = reinterpret_cast(ktxHighMipData.data()); ktxData += ktxHighMipData.size(); // TODO Move image offset calculation to ktx ImageDescriptor - for (auto level = images.size() - 1; level >= 0; --level) { + for (int level = static_cast(images.size()) - 1; level >= 0; --level) { auto& image = images[level]; if (image._imageSize > imageSizeRemaining) { break; From e4c21627f92533ef9de141db1cc26e089400abbb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 24 Apr 2017 12:05:34 -0700 Subject: [PATCH 069/119] Remove unused newHeader from Writer.cpp --- libraries/ktx/src/ktx/Writer.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index 0d3d4e1d60..4226b8fa84 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -43,12 +43,9 @@ namespace ktx { std::unique_ptr KTX::createBare(const Header& header, const KeyValues& keyValues) { auto descriptors = header.generateImageDescriptors(); - auto newHeader = header; - Byte minMip = header.numberOfMipmapLevels; auto newKeyValues = keyValues; newKeyValues.emplace_back(KeyValue(HIFI_MIN_POPULATED_MIP_KEY, sizeof(Byte), &minMip)); - newHeader.bytesOfKeyValueData = KeyValue::serializedKeyValuesByteSize(newKeyValues); StoragePointer storagePointer; { From a2f7a8843392f8ccbf2b3bf6a99326285b914cf1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 25 Apr 2017 16:29:36 -0700 Subject: [PATCH 070/119] Start drawing models before we get the textures --- libraries/render-utils/src/MeshPartPayload.cpp | 18 ++++++++++++------ libraries/render-utils/src/MeshPartPayload.h | 17 +++++++++-------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 51ce0fffa7..9485790463 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -118,7 +118,7 @@ void MeshPartPayload::drawCall(gpu::Batch& batch) const { batch.drawIndexed(gpu::TRIANGLES, _drawPart._numIndices, _drawPart._startIndex); } -void MeshPartPayload::bindMesh(gpu::Batch& batch) const { +void MeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); batch.setInputFormat((_drawMesh->getVertexFormat())); @@ -255,7 +255,7 @@ void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::Loca } -void MeshPartPayload::render(RenderArgs* args) const { +void MeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("MeshPartPayload::render"); gpu::Batch& batch = *(args->_batch); @@ -485,7 +485,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { return builder.build(); } -void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { +void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { if (!_isBlendShaped) { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); @@ -517,7 +517,7 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: batch.setModelTransform(_transform); } -float ModelMeshPartPayload::computeFadeAlpha() const { +float ModelMeshPartPayload::computeFadeAlpha() { if (_fadeState == FADE_WAITING_TO_START) { return 0.0f; } @@ -536,7 +536,7 @@ float ModelMeshPartPayload::computeFadeAlpha() const { return Interpolate::simpleNonLinearBlend(fadeAlpha); } -void ModelMeshPartPayload::render(RenderArgs* args) const { +void ModelMeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); if (!_model->addedToScene() || !_model->isVisible()) { @@ -544,7 +544,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } if (_fadeState == FADE_WAITING_TO_START) { - if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { + if (_model->isLoaded()) { if (EntityItem::getEntitiesShouldFadeFunction()()) { _fadeStartTime = usecTimestampNow(); _fadeState = FADE_IN_PROGRESS; @@ -557,6 +557,12 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } } + if (_materialNeedsUpdate && _model->getGeometry()->areTexturesLoaded()) { + qDebug() << "Updating for textures"; + _model->setRenderItemsNeedUpdate(); + _materialNeedsUpdate = false; + } + if (!args) { return; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index ef74011c40..11d1bbf6a7 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -46,11 +46,11 @@ public: virtual render::ItemKey getKey() const; virtual render::Item::Bound getBound() const; virtual render::ShapeKey getShapeKey() const; // shape interface - virtual void render(RenderArgs* args) const; + virtual void render(RenderArgs* args); // ModelMeshPartPayload functions to perform render void drawCall(gpu::Batch& batch) const; - virtual void bindMesh(gpu::Batch& batch) const; + virtual void bindMesh(gpu::Batch& batch); virtual void bindMaterial(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool enableTextures) const; virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const; @@ -93,16 +93,16 @@ public: const Transform& boundTransform, const gpu::BufferPointer& buffer); - float computeFadeAlpha() const; + float computeFadeAlpha(); // Render Item interface render::ItemKey getKey() const override; int getLayer() const; render::ShapeKey getShapeKey() const override; // shape interface - void render(RenderArgs* args) const override; + void render(RenderArgs* args) override; // ModelMeshPartPayload functions to perform render - void bindMesh(gpu::Batch& batch) const override; + void bindMesh(gpu::Batch& batch) override; void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; void initCache(); @@ -116,11 +116,12 @@ public: int _shapeID; bool _isSkinned{ false }; - bool _isBlendShaped{ false }; + bool _isBlendShaped { false }; + bool _materialNeedsUpdate { true }; private: - mutable quint64 _fadeStartTime { 0 }; - mutable uint8_t _fadeState { FADE_WAITING_TO_START }; + quint64 _fadeStartTime { 0 }; + uint8_t _fadeState { FADE_WAITING_TO_START }; }; namespace render { From d338ccac26afcc7ccc924fc82b55add4ca879078 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Apr 2017 16:32:06 -0700 Subject: [PATCH 071/119] Update NetworkTexture to automatically download all mips --- libraries/model-networking/src/model-networking/TextureCache.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f9ee9efe3f..e04e002784 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -276,6 +276,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _maxNumPixels(maxNumPixels) { _textureSource = std::make_shared(); + _lowestRequestedMipLevel = 0; if (!url.isValid()) { _loaded = true; From 382fe5d38d9719bbe4c694922a097692c044f023 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Apr 2017 16:31:42 -0700 Subject: [PATCH 072/119] Update gpu backend to only transfer if it can populate --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index ef938ecd6d..e0c6d86502 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -464,7 +464,7 @@ void GLVariableAllocationSupport::updateMemoryPressure() { newState = MemoryPressureState::Transfer; } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && ((unallocated != 0 && canPromote) || canPopulate)) { + } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && ((unallocated != 0 && canPromote) && canPopulate)) { newState = MemoryPressureState::Undersubscribed; } From 51ee058c0b7b8d3ff2b37ec9c9278eab8e40a6e2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Apr 2017 16:43:03 -0700 Subject: [PATCH 073/119] Update GLTexture to not promote if we can't populate --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index e0c6d86502..fceed7c2eb 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -521,8 +521,7 @@ void GLVariableAllocationSupport::processWorkQueues() { vartexture->demote(); _memoryPressureStateStale = true; } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { - if (!vartexture->canPromote()) { - vartexture->populateTransferQueue(); + if (!vartexture->canPromote() || !vartexture->canPopulate()) { continue; } vartexture->promote(); From 65d30d1d0bd5793e5205de297db7cb1b5a2e83f9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Apr 2017 16:47:38 -0700 Subject: [PATCH 074/119] Remove lowestRequestMip from gltexture --- .../gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 1 - libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 -- .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 15 +++++---------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 04c2201c75..7c43a585b7 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -395,7 +395,6 @@ void GL41VariableAllocationTexture::populateTransferQueue() { bool didQueueTransfer = false; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { - const_cast(_gpuObject).requestInterestInMip(sourceMip); continue; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 21c429c577..15e98c3af7 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -117,8 +117,6 @@ public: void allocateStorage(uint16 mip); void copyMipsFromTexture(); - - uint16 _lowestRequestedMip { 0 }; }; #if 0 diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 5dfbd6e727..39c8c4a7f5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -50,7 +50,7 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend for (uint16_t mip = 0; mip < mipLevels; ++mip) { if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) && texture.isStoredMipFaceAvailable(mip)) { - _lowestRequestedMip = _maxAllocatedMip = _populatedMip = mip; + _maxAllocatedMip = _populatedMip = mip; break; } } @@ -106,8 +106,6 @@ void GL45ResourceTexture::promote() { Q_ASSERT(_allocatedMip > 0); uint16_t targetAllocatedMip = _allocatedMip - std::min(_allocatedMip, 2); if (!_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { - _lowestRequestedMip = targetAllocatedMip; - const_cast(_gpuObject).requestInterestInMip(targetAllocatedMip); return; } GLuint oldId = _id; @@ -187,20 +185,17 @@ void GL45ResourceTexture::populateTransferQueue() { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); - bool transferQueued = false; + bool didQueueTransfer = false; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { - const_cast(_gpuObject).requestInterestInMip(sourceMip); - _lowestRequestedMip = sourceMip; continue; } - _lowestRequestedMip = sourceMip; // If the mip is less than the max transfer size, then just do it in one transfer if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); - transferQueued = true; + didQueueTransfer = true; continue; } @@ -216,12 +211,12 @@ void GL45ResourceTexture::populateTransferQueue() { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; - transferQueued = true; + didQueueTransfer = true; } } // queue up the sampler and populated mip change for after the transfer has completed - if (transferQueued) { + if (didQueueTransfer) { _pendingTransfers.emplace(new TransferJob(*this, [=] { _populatedMip = sourceMip; syncSampler(); From f5bb42b19fa7ccd2c4594aa6a7374aa622f2063d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Apr 2017 16:52:21 -0700 Subject: [PATCH 075/119] Remove request of mip from GL45BackendVariableTexture --- libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 39c8c4a7f5..2a02f9b87b 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -76,10 +76,6 @@ void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) { _size += _gpuObject.evalMipSize(mip); } - if (!_gpuObject.isStoredMipFaceAvailable(allocatedMip, 0)) { - const_cast(_gpuObject).requestInterestInMip(allocatedMip); - } - Backend::updateTextureGPUMemoryUsage(0, _size); } From 4d48cb2e801ee93b3328719bb139c81dfc17fdd5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Apr 2017 16:52:34 -0700 Subject: [PATCH 076/119] Remove extraneous glTextureParameteri logging --- libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 2a02f9b87b..e3f694f551 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -93,7 +93,6 @@ void GL45ResourceTexture::copyMipsFromTexture() { void GL45ResourceTexture::syncSampler() const { Parent::syncSampler(); - qDebug() << "glTextureParameteri " << QString::fromStdString(_source) << _populatedMip << _populatedMip - _allocatedMip; glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); } From 9505bf746cbd2a17b3d3c8f4d12e444b99d504d9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Apr 2017 21:12:14 -0700 Subject: [PATCH 077/119] Fix lower mips not being downloaded after initial mips --- .../model-networking/src/model-networking/TextureCache.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index e04e002784..0e9ce46912 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -635,8 +635,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner texture = textureCache->cacheTextureByHash(filename, texture); - - } _lowestKnownPopulatedMip = _originalKtxDescriptor->header.numberOfMipmapLevels; @@ -660,6 +658,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; } + startRequestForNextMipLevel(); } } From 674e767513a1086c052d77acee36cab14eba1cb2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 08:42:21 -0700 Subject: [PATCH 078/119] Remove MipInterestListener --- libraries/gpu/src/gpu/Texture.cpp | 22 ------------------- libraries/gpu/src/gpu/Texture.h | 11 ---------- .../src/model-networking/TextureCache.cpp | 18 --------------- .../src/model-networking/TextureCache.h | 5 +---- 4 files changed, 1 insertion(+), 55 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 1dbe9db2b4..0ede0406c3 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -476,32 +476,10 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin } } -void Texture::requestInterestInMip(uint16 level) { - if (!_storage->isMipAvailable(level, 0)) { - std::lock_guard lock(_mipInterestListenersMutex); - for (auto& callback : _mipInterestListeners) { - callback->handleMipInterestCallback(level); - } - } -} - bool Texture::isStoredMipFaceAvailable(uint16 level, uint8 face) const { return _storage->isMipAvailable(level, face); } -void Texture::registerMipInterestListener(MipInterestListener* listener) { - std::lock_guard lock(_mipInterestListenersMutex); - _mipInterestListeners.push_back(listener); -} - -void Texture::unregisterMipInterestListener(MipInterestListener* listener) { - std::lock_guard lock(_mipInterestListenersMutex); - auto it = find(_mipInterestListeners.begin(), _mipInterestListeners.end(), listener); - if (it != _mipInterestListeners.end()) { - _mipInterestListeners.erase(it); - } -} - void Texture::setAutoGenerateMips(bool enable) { bool changed = false; if (!_autoGenerateMips) { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 3c82f67c86..a79e679f15 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -488,17 +488,6 @@ public: void setStorage(std::unique_ptr& newStorage); void setKtxBacking(const std::string& filename); - class MipInterestListener { - public: - virtual void handleMipInterestCallback(uint16 level) = 0; - }; - void registerMipInterestListener(MipInterestListener* listener); - void unregisterMipInterestListener(MipInterestListener* listener); - std::vector _mipInterestListeners; - std::mutex _mipInterestListenersMutex; - - void requestInterestInMip(uint16 level); - // Usage is a a set of flags providing Semantic about the usage of the Texture. void setUsage(const Usage& usage) { _usage = usage; } Usage getUsage() const { return _usage; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 0e9ce46912..1d37a26f37 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -245,13 +245,6 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { return result; } -NetworkTexture::~NetworkTexture() { - auto texture = _textureSource->getGPUTexture(); - if (texture) { - texture->unregisterMipInterestListener(this); - } -} - /// Returns a texture version of an image file gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) { QImage image = QImage(path); @@ -381,15 +374,6 @@ void NetworkTexture::makeRequest() { } -void NetworkTexture::handleMipInterestCallback(uint16_t level) { - QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(int, level)); -} - -void NetworkTexture::handleMipInterestLevel(int level) { - _lowestRequestedMipLevel = std::min((uint16_t)level, _lowestRequestedMipLevel); - startRequestForNextMipLevel(); -} - void NetworkTexture::startRequestForNextMipLevel() { if (_lowestKnownPopulatedMip == 0) { qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _lowestKnownPopulatedMip @@ -647,8 +631,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); - - texture->registerMipInterestListener(this); _ktxResourceState = WAITING_FOR_MIP_REQUEST; setImage(texture, header->getPixelWidth(), header->getPixelHeight()); qDebug() << "Loaded KTX: " << QString::fromStdString(hash) << " : " << _url; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 5fb23c0d2d..c7a7799216 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -41,7 +41,7 @@ public: }; /// A texture loaded from the network. -class NetworkTexture : public Resource, public Texture, public gpu::Texture::MipInterestListener { +class NetworkTexture : public Resource, public Texture { Q_OBJECT public: @@ -58,9 +58,6 @@ public: gpu::TexturePointer getFallbackTexture() const; - void handleMipInterestCallback(uint16_t level) override; - Q_INVOKABLE void handleMipInterestLevel(int level); - signals: void networkTextureCreated(const QWeakPointer& self); From b9ec573c8b2765e5bec4a393499c1565f86a0e48 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 10:04:52 -0700 Subject: [PATCH 079/119] Update gl41 an gl45 texture backends to take into account min avail mip --- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 2 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 19 ++++++++++++++++--- .../gpu/gl45/GL45BackendVariableTexture.cpp | 13 ++++++++++--- .../src/model-networking/TextureCache.cpp | 3 +-- .../src/model-networking/TextureCache.h | 1 - 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index dc6d2b3aa7..c0b9ea0e45 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -100,7 +100,7 @@ public: GL41VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); ~GL41VariableAllocationTexture(); - bool canPopulate() const override { return _populatedMip > _allocatedMip && _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } + bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } void allocateStorage(uint16 allocatedMip); void syncSampler() const override; void promote() override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 7c43a585b7..51307511c2 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -231,15 +231,20 @@ using GL41VariableAllocationTexture = GL41Backend::GL41VariableAllocationTexture GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL41Texture(backend, texture) { auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; + _maxAllocatedMip = _populatedMip = mipLevels; + uint16_t minAvailableMip = texture.minAvailableMipLevel(); uvec3 mipDimensions; for (uint16_t mip = 0; mip < mipLevels; ++mip) { - if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) + && mip >= minAvailableMip) { _maxAllocatedMip = _populatedMip = mip; break; } } - uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); + auto targetMip = _populatedMip - std::min(_populatedMip, 2); + uint16_t allocatedMip = std::max(minAvailableMip, targetMip); + allocateStorage(allocatedMip); _memoryPressureStateStale = true; size_t maxFace = GLTexture::getFaceCount(_target); @@ -292,6 +297,14 @@ void GL41VariableAllocationTexture::syncSampler() const { void GL41VariableAllocationTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip > 0); + + uint16_t targetAllocatedMip = _allocatedMip - std::min(_allocatedMip, 2); + targetAllocatedMip = std::max(_gpuObject.minAvailableMipLevel(), targetAllocatedMip); + + if (targetAllocatedMip >= _allocatedMip || !_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { + return; + } + GLuint oldId = _id; auto oldSize = _size; // create new texture @@ -299,7 +312,7 @@ void GL41VariableAllocationTexture::promote() { uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level - allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); + allocateStorage(targetAllocatedMip); withPreservedTexture([&] { GLuint fbo { 0 }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index e3f694f551..9b35f35934 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -46,16 +46,20 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend _maxAllocatedMip = _populatedMip = mipLevels; + uint16_t minAvailableMip = texture.minAvailableMipLevel(); + uvec3 mipDimensions; for (uint16_t mip = 0; mip < mipLevels; ++mip) { if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) - && texture.isStoredMipFaceAvailable(mip)) { + && mip >= minAvailableMip) { _maxAllocatedMip = _populatedMip = mip; break; } } - uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); + auto targetMip = _populatedMip - std::min(_populatedMip, 2); + uint16_t allocatedMip = std::max(minAvailableMip, targetMip); + allocateStorage(allocatedMip); _memoryPressureStateStale = true; copyMipsFromTexture(); @@ -99,8 +103,11 @@ void GL45ResourceTexture::syncSampler() const { void GL45ResourceTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip > 0); + uint16_t targetAllocatedMip = _allocatedMip - std::min(_allocatedMip, 2); - if (!_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { + targetAllocatedMip = std::max(_gpuObject.minAvailableMipLevel(), targetAllocatedMip); + + if (targetAllocatedMip >= _allocatedMip || !_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { return; } GLuint oldId = _id; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1d37a26f37..62b94ed455 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -588,8 +588,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { _ktxResourceState = FAILED_TO_LOAD; finishedLoading(false); return; - } - else { + } else { _file = file; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index c7a7799216..1e61b9ecee 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -46,7 +46,6 @@ class NetworkTexture : public Resource, public Texture { public: NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); - ~NetworkTexture() override; QString getType() const override { return "NetworkTexture"; } From 517bbc795f6590cd490ccf96be408ac6b6749563 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Apr 2017 11:29:42 -0700 Subject: [PATCH 080/119] interface/ --- libraries/avatars/src/AvatarData.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index af43b16861..a4ea016e79 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1532,11 +1532,22 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC } QByteArray AvatarData::identityByteArray() const { + qDebug() << __FUNCTION__; + QByteArray identityData; QDataStream identityStream(&identityData, QIODevice::Append); const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL _avatarEntitiesLock.withReadLock([&] { + + qDebug() << __FUNCTION__ + << "session:" << getSessionUUID() + << "url:" << urlToSend + << "displayName:" << _displayName + << "sessionDisplayName:" << getSessionDisplayNameForTransport() + << "identityUpdatedAt:" << _identityUpdatedAt; + + identityStream << getSessionUUID() << urlToSend << _attachmentData @@ -1720,6 +1731,7 @@ void AvatarData::sendAvatarDataPacket() { } void AvatarData::sendIdentityPacket() { + qDebug() << __FUNCTION__; auto nodeList = DependencyManager::get(); QByteArray identityData = identityByteArray(); @@ -1728,9 +1740,14 @@ void AvatarData::sendIdentityPacket() { packetList->write(identityData); nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { + if (node->getType() == NodeType::AvatarMixer) { + qDebug() << "AvatarData::sendIdentityPacket()... found mixer node, node->getActiveSocket():" << node->getActiveSocket(); + } + return node->getType() == NodeType::AvatarMixer && node->getActiveSocket(); }, [&](const SharedNodePointer& node) { + qDebug() << "AvatarData::sendIdentityPacket()... about to call.... nodeList->sendPacketList(std::move(packetList), *node);"; nodeList->sendPacketList(std::move(packetList), *node); }); From 4f16eb9bccc7f846257828483593d8bf41bc873d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 10:21:27 -0700 Subject: [PATCH 081/119] Cleanup KTX logging and add fragment to resource url to indicate mip level --- .../src/model-networking/TextureCache.cpp | 12 +++++++++--- libraries/networking/src/HTTPResourceRequest.cpp | 5 ----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 62b94ed455..48999c21f3 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -338,7 +338,9 @@ void NetworkTexture::makeRequest() { if (_ktxResourceState == PENDING_INITIAL_LOAD) { _ktxResourceState = LOADING_INITIAL_DATA; - qDebug() << ">>> Making request to " << _url << " for header"; + // Add a fragment to the base url so we can identify the section of the ktx being requested when debugging + // The actual requested url is _activeUrl and will not contain the fragment + _url.setFragment("head"); _ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl); if (!_ktxHeaderRequest) { @@ -366,7 +368,12 @@ void NetworkTexture::makeRequest() { } else if (_ktxResourceState == PENDING_MIP_REQUEST) { if (_lowestKnownPopulatedMip > 0) { _ktxResourceState = REQUESTING_MIP; - startMipRangeRequest(_lowestKnownPopulatedMip - 1, _lowestKnownPopulatedMip - 1); + + // Add a fragment to the base url so we can identify the section of the ktx being requested when debugging + // The actual requested url is _activeUrl and will not contain the fragment + uint16_t nextMip = _lowestKnownPopulatedMip - 1; + _url.setFragment(QString::number(nextMip)); + startMipRangeRequest(nextMip, nextMip); } } else { qWarning(networking) << "NetworkTexture::makeRequest() called while not in a valid state: " << _ktxResourceState; @@ -632,7 +639,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { _ktxResourceState = WAITING_FOR_MIP_REQUEST; setImage(texture, header->getPixelWidth(), header->getPixelHeight()); - qDebug() << "Loaded KTX: " << QString::fromStdString(hash) << " : " << _url; _ktxHeaderRequest->deleteLater(); _ktxHeaderRequest = nullptr; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 52f5ee43f5..c6a4b93e51 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -22,7 +22,6 @@ #include "NetworkLogging.h" HTTPResourceRequest::~HTTPResourceRequest() { - qDebug() << "Cleaning up:" << _url << " " << _byteRange.fromInclusive << "-" << _byteRange.toExclusive; if (_reply) { _reply->disconnect(this); _reply->deleteLater(); @@ -68,7 +67,6 @@ void HTTPResourceRequest::doSend() { // HTTP byte ranges are inclusive on the `to` end: [from, to] byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive - 1); } - qDebug() << "Setting http range to " << byteRange; networkRequest.setRawHeader("Range", byteRange.toLatin1()); } networkRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, false); @@ -79,12 +77,9 @@ void HTTPResourceRequest::doSend() { connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress); setupTimer(); - qDebug() << "Sent: " << _url; } void HTTPResourceRequest::onRequestFinished() { - qDebug() << "On request finished: " << _url; - Q_ASSERT(_state == InProgress); Q_ASSERT(_reply); From 29641ba69a2454e01d736eb3fbd4bcc02c319bbc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 10:55:55 -0700 Subject: [PATCH 082/119] Fix min mip not being written to ktx correctly --- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index b2b08ca170..6fe07d4241 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -207,7 +207,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; if (offset > 0) { - memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + offset, (void*)&_minMipLevelAvailable, 1); + memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + 4 + offset, (void*)&_minMipLevelAvailable, 1); } } } From 3184ddb29f61d0f3f4edd89c95b789bb0ec083fc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 11:00:16 -0700 Subject: [PATCH 083/119] Fix duplicate low mip calculation in NetworkTexture --- .../src/model-networking/TextureCache.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 48999c21f3..011b3f4ed9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -627,14 +627,6 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { texture = textureCache->cacheTextureByHash(filename, texture); } - _lowestKnownPopulatedMip = _originalKtxDescriptor->header.numberOfMipmapLevels; - for (uint16_t l = 0; l < 200; l++) { - if (texture->isStoredMipFaceAvailable(l)) { - _lowestKnownPopulatedMip = l; - break; - } - } - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); _ktxResourceState = WAITING_FOR_MIP_REQUEST; From 0f461a2188f4e471dbc83c4ea0ef896f74a6e72d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 11:34:27 -0700 Subject: [PATCH 084/119] Fix priority being reset when requesting low mips --- .../model-networking/src/model-networking/TextureCache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 011b3f4ed9..c367ad4289 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -391,9 +391,9 @@ void NetworkTexture::startRequestForNextMipLevel() { if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { _ktxResourceState = PENDING_MIP_REQUEST; - setLoadPriority(this, -static_cast(_originalKtxDescriptor->header.numberOfMipmapLevels) + _lowestKnownPopulatedMip); - init(); + setLoadPriority(this, -static_cast(_originalKtxDescriptor->header.numberOfMipmapLevels) + _lowestKnownPopulatedMip); + _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); TextureCache::attemptRequest(_self); } } From 01724c9c907b89503933cf1af4671eba8495cf13 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 11:34:42 -0700 Subject: [PATCH 085/119] Update default load priority to be 0 --- libraries/networking/src/ResourceCache.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 56897ca4cd..0a24cb4d91 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -555,6 +555,10 @@ void Resource::clearLoadPriority(const QPointer& owner) { } float Resource::getLoadPriority() { + if (_loadPriorities.size() == 0) { + return 0; + } + float highestPriority = -FLT_MAX; for (QHash, float>::iterator it = _loadPriorities.begin(); it != _loadPriorities.end(); ) { if (it.key().isNull()) { From 1555fc308861645e1f9746feec4b661a4b7a0a35 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 11:36:33 -0700 Subject: [PATCH 086/119] Update Resource to only clear load priorities if successful --- libraries/networking/src/ResourceCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 0a24cb4d91..7ae75b9538 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -643,12 +643,12 @@ void Resource::attemptRequest() { void Resource::finishedLoading(bool success) { if (success) { qCDebug(networking).noquote() << "Finished loading:" << _url.toDisplayString(); + _loadPriorities.clear(); _loaded = true; } else { qCDebug(networking).noquote() << "Failed to load:" << _url.toDisplayString(); _failedToLoad = true; } - _loadPriorities.clear(); emit finished(success); } From 5dd6daaad78a76565fa839e62ea699312cb8452d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Apr 2017 11:42:38 -0700 Subject: [PATCH 087/119] send identity on nodeActivated not nodeAdded --- interface/src/Application.cpp | 17 +++++++++-------- interface/src/main.cpp | 31 +++++++++++++++++++------------ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2d39a0e993..4801f39272 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5205,14 +5205,7 @@ void Application::resettingDomain() { } void Application::nodeAdded(SharedNodePointer node) const { - if (node->getType() == NodeType::AvatarMixer) { - // new avatar mixer, send off our identity packet right away - - qDebug() << __FUNCTION__ << "about to call... getMyAvatar()->sendIdentityPacket();"; - getMyAvatar()->markIdentityDataChanged(); - getMyAvatar()->sendIdentityPacket(); - getMyAvatar()->resetLastSent(); - } + // nothing to do here } void Application::nodeActivated(SharedNodePointer node) { @@ -5248,6 +5241,14 @@ void Application::nodeActivated(SharedNodePointer node) { if (node->getType() == NodeType::AudioMixer) { DependencyManager::get()->negotiateAudioFormat(); } + + if (node->getType() == NodeType::AvatarMixer) { + // new avatar mixer, send off our identity packet right away + qDebug() << __FUNCTION__ << "about to call... getMyAvatar()->sendIdentityPacket();"; + getMyAvatar()->markIdentityDataChanged(); + getMyAvatar()->sendIdentityPacket(); + getMyAvatar()->resetLastSent(); + } } void Application::nodeKilled(SharedNodePointer node) { diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 39b37e3d19..99dbb1a28e 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -79,6 +79,25 @@ int main(int argc, const char* argv[]) { instanceMightBeRunning = false; } + QCommandLineParser parser; + QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications"); + QCommandLineOption runServerOption("runServer", "Whether to run the server"); + QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath"); + QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run"); + parser.addOption(checkMinSpecOption); + parser.addOption(runServerOption); + parser.addOption(serverContentPathOption); + parser.addOption(allowMultipleInstancesOption); + parser.parse(arguments); + bool runServer = parser.isSet(runServerOption); + bool serverContentPathOptionIsSet = parser.isSet(serverContentPathOption); + QString serverContentPathOptionValue = serverContentPathOptionIsSet ? parser.value(serverContentPathOption) : QString(); + bool allowMultipleInstances = parser.isSet(allowMultipleInstancesOption); + + if (allowMultipleInstances) { + instanceMightBeRunning = false; + } + if (instanceMightBeRunning) { // Try to connect and send message to existing interface instance QLocalSocket socket; @@ -137,18 +156,6 @@ int main(int argc, const char* argv[]) { } } - QCommandLineParser parser; - QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications"); - QCommandLineOption runServerOption("runServer", "Whether to run the server"); - QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath"); - parser.addOption(checkMinSpecOption); - parser.addOption(runServerOption); - parser.addOption(serverContentPathOption); - parser.parse(arguments); - bool runServer = parser.isSet(runServerOption); - bool serverContentPathOptionIsSet = parser.isSet(serverContentPathOption); - QString serverContentPathOptionValue = serverContentPathOptionIsSet ? parser.value(serverContentPathOption) : QString(); - QElapsedTimer startupTime; startupTime.start(); From 75c2776836aee8c5b82aa23ac9c1554bcec9518d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 25 Apr 2017 09:41:37 -0700 Subject: [PATCH 088/119] remove keep-alive packet from avatar-mixer --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 6e3dd150a4..2ad8bb58ed 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -263,16 +263,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // make sure we haven't already sent this data from this sender to this receiver // or that somehow we haven't sent if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { - // don't ignore this avatar if we haven't sent any update for a long while - // in an effort to prevent other interfaces from deleting a stale avatar instance - uint64_t lastBroadcastTime = nodeData->getLastBroadcastTime(avatarNode->getUUID()); - const AvatarMixerClientData* otherNodeData = reinterpret_cast(avatarNode->getLinkedData()); - const uint64_t AVATAR_UPDATE_STALE = AVATAR_UPDATE_TIMEOUT - USECS_PER_SECOND; - if (lastBroadcastTime > otherNodeData->getIdentityChangeTimestamp() && - lastBroadcastTime + AVATAR_UPDATE_STALE > startIgnoreCalculation) { - ++numAvatarsHeldBack; - shouldIgnore = true; - } + ++numAvatarsHeldBack; + shouldIgnore = true; } else if (lastSeqFromSender - lastSeqToReceiver > 1) { // this is a skip - we still send the packet but capture the presence of the skip so we see it happening ++numAvatarsWithSkippedFrames; @@ -285,7 +277,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { int avatarRank = 0; // this is overly conservative, because it includes some avatars we might not consider - int remainingAvatars = (int)sortedAvatars.size(); + int remainingAvatars = (int)sortedAvatars.size(); while (!sortedAvatars.empty()) { AvatarPriority sortData = sortedAvatars.top(); From 08972da843df54f117ab4e0fa763187851fb6bc4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 25 Apr 2017 09:41:21 -0700 Subject: [PATCH 089/119] remove unused Node::_isAlive cruft --- libraries/networking/src/Node.cpp | 1 - libraries/networking/src/Node.h | 4 ---- 2 files changed, 5 deletions(-) diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 033f4bbaa8..60227eeaa1 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -56,7 +56,6 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, NetworkPeer(uuid, publicSocket, localSocket, parent), _type(type), _connectionSecret(connectionSecret), - _isAlive(true), _pingMs(-1), // "Uninitialized" _clockSkewUsec(0), _mutex(), diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 28afb8b943..d1bbffd817 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -54,9 +54,6 @@ public: NodeData* getLinkedData() const { return _linkedData.get(); } void setLinkedData(std::unique_ptr linkedData) { _linkedData = std::move(linkedData); } - bool isAlive() const { return _isAlive; } - void setAlive(bool isAlive) { _isAlive = isAlive; } - int getPingMs() const { return _pingMs; } void setPingMs(int pingMs) { _pingMs = pingMs; } @@ -92,7 +89,6 @@ private: QUuid _connectionSecret; std::unique_ptr _linkedData; - bool _isAlive; int _pingMs; qint64 _clockSkewUsec; QMutex _mutex; From 4511156d7bbba1cd558223eb14a10b6a2e21470f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 25 Apr 2017 09:42:03 -0700 Subject: [PATCH 090/119] cache avatar identity info forever --- interface/src/avatar/Avatar.cpp | 31 ++++++++++--- interface/src/avatar/Avatar.h | 3 ++ interface/src/avatar/AvatarManager.cpp | 60 ++++++++------------------ interface/src/avatar/AvatarManager.h | 5 +-- libraries/avatars/src/AvatarData.cpp | 3 -- libraries/avatars/src/AvatarData.h | 10 +---- libraries/avatars/src/AvatarHashMap.h | 2 +- 7 files changed, 50 insertions(+), 64 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ab3ba206f2..4f8cc6655e 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -115,8 +115,6 @@ Avatar::Avatar(QThread* thread, RigPointer rig) : } Avatar::~Avatar() { - assert(isDead()); // mark dead before calling the dtor - auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; if (entityTree) { @@ -447,6 +445,24 @@ float Avatar::getSimulationRate(const QString& rateName) const { return 0.0f; } +void Avatar::getIdentity(Identity& identity) const { + identity.uuid = getSessionUUID(); + identity.skeletonModelURL = _skeletonModelURL; + identity.attachmentData = _attachmentData; + identity.displayName = _displayName; + identity.sessionDisplayName = _sessionDisplayName; + identity.avatarEntityData = _avatarEntityData; +} + +void Avatar::setIdentity(const Identity& identity) { + assert(identity.uuid == getSessionUUID()); + setSkeletonModelURL(identity.skeletonModelURL); + _attachmentData = identity.attachmentData; + _displayName = identity.displayName; + _sessionDisplayName = identity.sessionDisplayName; + _avatarEntityData = identity.avatarEntityData; +} + bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const { const float HEAD_SPHERE_RADIUS = 0.1f; glm::vec3 theirLookAt = dynamic_pointer_cast(avatar)->getHead()->getLookAtPosition(); @@ -510,12 +526,13 @@ static TextRenderer3D* textRenderer(TextRendererType type) { void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { auto avatarPayload = new render::Payload(self); auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); - _renderItemID = scene->allocateID(); - transaction.resetItem(_renderItemID, avatarPayloadPointer); - _skeletonModel->addToScene(scene, transaction); + if (_skeletonModel->addToScene(scene, transaction)) { + _renderItemID = scene->allocateID(); + transaction.resetItem(_renderItemID, avatarPayloadPointer); - for (auto& attachmentModel : _attachmentModels) { - attachmentModel->addToScene(scene, transaction); + for (auto& attachmentModel : _attachmentModels) { + attachmentModel->addToScene(scene, transaction); + } } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 8c055885fd..3b9bb7e0f2 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -238,6 +238,9 @@ public: return (lerpValue*(4.0f - 2.0f * lerpValue) - 1.0f); } + void getIdentity(Identity& identity) const; + void setIdentity(const Identity& identity); + public slots: // FIXME - these should be migrated to use Pose data instead diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6f9793ad34..e3293a7874 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -214,11 +214,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } avatar->animateScaleChanges(deltaTime); - if (avatar->shouldDie()) { - avatar->die(); - removeAvatar(avatar->getID()); - } - const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; uint64_t now = usecTimestampNow(); if (now < updateExpiry) { @@ -331,35 +326,14 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { return std::make_shared(qApp->thread(), std::make_shared()); } -void AvatarManager::processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode) { - PerformanceTimer perfTimer("receiveAvatar"); - // enumerate over all of the avatars in this packet - // only add them if mixerWeakPointer points to something (meaning that mixer is still around) - while (message->getBytesLeftToRead()) { - AvatarSharedPointer avatarData = parseAvatarData(message, sendingNode); - if (avatarData) { - auto avatar = std::static_pointer_cast(avatarData); - if (avatar->isInScene()) { - if (!_shouldRender) { - // rare transition so we process the transaction immediately - const render::ScenePointer& scene = qApp->getMain3DScene(); - render::Transaction transaction; - avatar->removeFromScene(avatar, scene, transaction); - if (scene) { - scene->enqueueTransaction(transaction); - } - } - } else if (_shouldRender) { - // very rare transition so we process the transaction immediately - const render::ScenePointer& scene = qApp->getMain3DScene(); - render::Transaction transaction; - avatar->addToScene(avatar, scene, transaction); - if (scene) { - scene->enqueueTransaction(transaction); - } - } - } +AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { + AvatarSharedPointer avatarData = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer); + QMap::iterator itr = _identityCache.find(sessionUUID); + if (itr != _identityCache.end()) { + auto avatar = std::static_pointer_cast(avatarData); + avatar->setIdentity(*itr); } + return avatarData; } void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { @@ -367,8 +341,13 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar // removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar // class in this context so we can call methods that don't exist at the base class. - Avatar* avatar = static_cast(removedAvatar.get()); - avatar->die(); + auto avatar = std::static_pointer_cast(removedAvatar); + + // there is no way to request identity data from the avatar-mixer + // therefore whenever we remove an avatar we cache the identity in case we need it later + AvatarData::Identity identity; + avatar->getIdentity(identity); + _identityCache[avatar->getSessionUUID()] = identity; AvatarMotionState* motionState = avatar->getMotionState(); if (motionState) { @@ -404,14 +383,11 @@ void AvatarManager::clearOtherAvatars() { if (avatar->isInScene()) { avatar->removeFromScene(avatar, scene, transaction); } - AvatarMotionState* motionState = avatar->getMotionState(); - if (motionState) { - _motionStatesThatMightUpdate.remove(motionState); - _motionStatesToAddToPhysics.remove(motionState); - _motionStatesToRemoveFromPhysics.push_back(motionState); - } + handleRemovedAvatar(avatar); + avatarIterator = _avatarHash.erase(avatarIterator); + } else { + ++avatarIterator; } - ++avatarIterator; } scene->enqueueTransaction(transaction); _myAvatar->clearLookAtTargetAvatar(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 6eabbd081f..af372f7627 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -98,9 +98,6 @@ public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } void updateAvatarRenderStatus(bool shouldRenderAvatars); -protected slots: - void processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode) override; - private: explicit AvatarManager(QObject* parent = 0); explicit AvatarManager(const AvatarManager& other); @@ -108,6 +105,7 @@ private: void simulateAvatarFades(float deltaTime); AvatarSharedPointer newSharedAvatar() override; + AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; QVector _avatarsToFade; @@ -120,6 +118,7 @@ private: quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. QVector _localLights; + QMap _identityCache; bool _shouldShowReceiveStats = false; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a4ea016e79..e75f801885 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1521,9 +1521,6 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC setAvatarEntityData(identity.avatarEntityData); identityChanged = true; } - // flag this avatar as non-stale by updating _averageBytesReceived - const int BOGUS_NUM_BYTES = 1; - _averageBytesReceived.updateAverage(BOGUS_NUM_BYTES); // use the timestamp from this identity, since we want to honor the updated times in "server clock" // this will overwrite any changes we made locally to this AvatarData's _identityUpdatedAt diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 7b24ae6cd7..036e862b1d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -110,9 +110,7 @@ const char LEFT_HAND_POINTING_FLAG = 1; const char RIGHT_HAND_POINTING_FLAG = 2; const char IS_FINGER_POINTING_FLAG = 4; -const qint64 AVATAR_UPDATE_TIMEOUT = 5 * USECS_PER_SECOND; - -// AvatarData state flags - we store the details about the packet encoding in the first byte, +// AvatarData state flags - we store the details about the packet encoding in the first byte, // before the "header" structure const char AVATARDATA_FLAGS_MINIMUM = 0; @@ -568,7 +566,6 @@ public: void setOwningAvatarMixer(const QWeakPointer& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; } - int getUsecsSinceLastUpdate() const { return _averageBytesReceived.getUsecsSinceLastEvent(); } int getAverageBytesReceivedPerSecond() const; int getReceiveRate() const; @@ -604,9 +601,6 @@ public: return _lastSentJointData; } - - bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_UPDATE_TIMEOUT; } - static const float OUT_OF_VIEW_PENALTY; static void sortAvatars( @@ -631,7 +625,7 @@ public: signals: void displayNameChanged(); - + public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index e944c7c887..21ea8081c7 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -57,7 +57,7 @@ public slots: protected slots: void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID); - virtual void processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode); + void processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode); void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); void processKillAvatar(QSharedPointer message, SharedNodePointer sendingNode); void processExitingSpaceBubble(QSharedPointer message, SharedNodePointer sendingNode); From ac751029c0d9a5248cb499ce509ebde1f4b1ac17 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 25 Apr 2017 15:34:59 -0700 Subject: [PATCH 091/119] remove identity cache code --- interface/src/avatar/Avatar.cpp | 18 ------------------ interface/src/avatar/Avatar.h | 3 --- interface/src/avatar/AvatarManager.cpp | 11 ----------- interface/src/avatar/AvatarManager.h | 2 -- 4 files changed, 34 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4f8cc6655e..4efb68f847 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -445,24 +445,6 @@ float Avatar::getSimulationRate(const QString& rateName) const { return 0.0f; } -void Avatar::getIdentity(Identity& identity) const { - identity.uuid = getSessionUUID(); - identity.skeletonModelURL = _skeletonModelURL; - identity.attachmentData = _attachmentData; - identity.displayName = _displayName; - identity.sessionDisplayName = _sessionDisplayName; - identity.avatarEntityData = _avatarEntityData; -} - -void Avatar::setIdentity(const Identity& identity) { - assert(identity.uuid == getSessionUUID()); - setSkeletonModelURL(identity.skeletonModelURL); - _attachmentData = identity.attachmentData; - _displayName = identity.displayName; - _sessionDisplayName = identity.sessionDisplayName; - _avatarEntityData = identity.avatarEntityData; -} - bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const { const float HEAD_SPHERE_RADIUS = 0.1f; glm::vec3 theirLookAt = dynamic_pointer_cast(avatar)->getHead()->getLookAtPosition(); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 3b9bb7e0f2..8c055885fd 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -238,9 +238,6 @@ public: return (lerpValue*(4.0f - 2.0f * lerpValue) - 1.0f); } - void getIdentity(Identity& identity) const; - void setIdentity(const Identity& identity); - public slots: // FIXME - these should be migrated to use Pose data instead diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index e3293a7874..c0b001911f 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -326,16 +326,6 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { return std::make_shared(qApp->thread(), std::make_shared()); } -AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { - AvatarSharedPointer avatarData = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer); - QMap::iterator itr = _identityCache.find(sessionUUID); - if (itr != _identityCache.end()) { - auto avatar = std::static_pointer_cast(avatarData); - avatar->setIdentity(*itr); - } - return avatarData; -} - void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason); @@ -347,7 +337,6 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar // therefore whenever we remove an avatar we cache the identity in case we need it later AvatarData::Identity identity; avatar->getIdentity(identity); - _identityCache[avatar->getSessionUUID()] = identity; AvatarMotionState* motionState = avatar->getMotionState(); if (motionState) { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index af372f7627..45f1a597eb 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -105,7 +105,6 @@ private: void simulateAvatarFades(float deltaTime); AvatarSharedPointer newSharedAvatar() override; - AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; QVector _avatarsToFade; @@ -118,7 +117,6 @@ private: quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. QVector _localLights; - QMap _identityCache; bool _shouldShowReceiveStats = false; From fd874429c1d203068d9079d90c254815200fd688 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 25 Apr 2017 15:40:51 -0700 Subject: [PATCH 092/119] remove unused cruft --- interface/src/avatar/AvatarManager.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c0b001911f..c4bcb67a16 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -333,11 +333,6 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar // class in this context so we can call methods that don't exist at the base class. auto avatar = std::static_pointer_cast(removedAvatar); - // there is no way to request identity data from the avatar-mixer - // therefore whenever we remove an avatar we cache the identity in case we need it later - AvatarData::Identity identity; - avatar->getIdentity(identity); - AvatarMotionState* motionState = avatar->getMotionState(); if (motionState) { _motionStatesThatMightUpdate.remove(motionState); From 7154a11192e49945a8ac39a9820a12d47546a887 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Apr 2017 14:03:36 -0700 Subject: [PATCH 093/119] add some more logging --- assignment-client/src/Agent.cpp | 1 + assignment-client/src/avatars/AvatarMixer.cpp | 8 ++++++++ assignment-client/src/avatars/AvatarMixerSlave.cpp | 2 ++ libraries/avatars/src/AvatarData.cpp | 5 ++--- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index c8ab489311..3cfccbea9e 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -583,6 +583,7 @@ void Agent::setIsAvatar(bool isAvatar) { void Agent::sendAvatarIdentityPacket() { if (_isAvatar) { auto scriptedAvatar = DependencyManager::get(); + qDebug() << __FUNCTION__ << "about to call sendIdentityPacket()"; scriptedAvatar->sendIdentityPacket(); } } diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 43eac8ebbe..5e131cbcd7 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -70,6 +70,7 @@ AvatarMixer::~AvatarMixer() { } void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { + qDebug() << __FUNCTION__ << "about to call nodeData->getAvatar().identityByteArray()... for node:" << nodeData->getNodeID(); QByteArray individualData = nodeData->getAvatar().identityByteArray(); auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); @@ -215,6 +216,8 @@ void AvatarMixer::manageDisplayName(const SharedNodePointer& node) { soFar.second++; // refcount nodeData->flagIdentityChange(); nodeData->setAvatarSessionDisplayNameMustChange(false); + + qCDebug(avatars) << __FUNCTION__ << "about to call sendIdentityPacket()"; sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name. qCDebug(avatars) << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID(); } @@ -405,9 +408,14 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes // parse the identity packet and update the change timestamp if appropriate AvatarData::Identity identity; + + qCDebug(avatars) << __FUNCTION__ << "about to call parseAvatarIdentityPacket()"; AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); + bool identityChanged = false; bool displayNameChanged = false; + + qCDebug(avatars) << __FUNCTION__ << "about to call processAvatarIdentity()"; avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 2ad8bb58ed..3266276a45 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -67,6 +67,7 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) { int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { + qDebug() << __FUNCTION__ << "about to call nodeData->getConstAvatarData().identityByteArray()... for node:" << nodeData->getNodeID(); QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(); individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); @@ -302,6 +303,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO // the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A. if (nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) { + qCDebug(avatars) << __FUNCTION__ << "about to call sendIdentityPacket()"; identityBytesSent += sendIdentityPacket(otherNodeData, node); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index e75f801885..a9ca366d05 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1529,15 +1529,13 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC } QByteArray AvatarData::identityByteArray() const { - qDebug() << __FUNCTION__; - QByteArray identityData; QDataStream identityStream(&identityData, QIODevice::Append); const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL _avatarEntitiesLock.withReadLock([&] { - qDebug() << __FUNCTION__ + qDebug() << "AvatarData::identityByteArray() ... " << "session:" << getSessionUUID() << "url:" << urlToSend << "displayName:" << _displayName @@ -1731,6 +1729,7 @@ void AvatarData::sendIdentityPacket() { qDebug() << __FUNCTION__; auto nodeList = DependencyManager::get(); + qDebug() << __FUNCTION__ << "about to call identityByteArray()... for getSessionUUID:" << getSessionUUID(); QByteArray identityData = identityByteArray(); auto packetList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); From 70ece9f0fdfcf0b6f20a2b2f9068f6f312b038c8 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 26 Apr 2017 13:56:26 -0700 Subject: [PATCH 094/119] Load skybox first and sounds later --- libraries/audio/src/SoundCache.cpp | 6 +++++- .../model-networking/src/model-networking/TextureCache.cpp | 5 +++++ libraries/render-utils/src/MeshPartPayload.cpp | 1 - 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp index 6b34c68959..1646540da6 100644 --- a/libraries/audio/src/SoundCache.cpp +++ b/libraries/audio/src/SoundCache.cpp @@ -14,6 +14,8 @@ #include "AudioLogging.h" #include "SoundCache.h" +static const int SOUNDS_LOADING_PRIORITY { -7 }; // Make sure sounds load after the low rez texture mips + int soundPointerMetaTypeId = qRegisterMetaType(); SoundCache::SoundCache(QObject* parent) : @@ -37,5 +39,7 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) { QSharedPointer SoundCache::createResource(const QUrl& url, const QSharedPointer& fallback, const void* extra) { qCDebug(audio) << "Requesting sound at" << url.toString(); - return QSharedPointer(new Sound(url), &Resource::deleter); + auto resource = QSharedPointer(new Sound(url), &Resource::deleter); + resource->setLoadPriority(this, SOUNDS_LOADING_PRIORITY); + return resource; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c367ad4289..4597304550 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -50,6 +50,8 @@ Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.k const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; const std::string TextureCache::KTX_EXT { "ktx" }; +static const int SKYBOX_LOAD_PRIORITY { 10 }; // Make sure skybox loads first + TextureCache::TextureCache() : _ktxCache(KTX_DIRNAME, KTX_EXT) { setUnusedResourceCacheSize(0); @@ -259,6 +261,9 @@ QSharedPointer TextureCache::createResource(const QUrl& url, const QSh auto content = textureExtra ? textureExtra->content : QByteArray(); auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; NetworkTexture* texture = new NetworkTexture(url, type, content, maxNumPixels); + if (type == image::TextureUsage::CUBE_TEXTURE) { + texture->setLoadPriority(this, SKYBOX_LOAD_PRIORITY); + } return QSharedPointer(texture, &Resource::deleter); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 9485790463..2e08420073 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -558,7 +558,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) { } if (_materialNeedsUpdate && _model->getGeometry()->areTexturesLoaded()) { - qDebug() << "Updating for textures"; _model->setRenderItemsNeedUpdate(); _materialNeedsUpdate = false; } From a71d246e754bb93a358d2ee3bde55c7808f003a0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 12:50:42 -0700 Subject: [PATCH 095/119] Replace canPopulate with _minAllocatedMip --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 6 ++---- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 6 ++++-- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 1 - libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 12 ++++++++++++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 1 - libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 9 ++++++--- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index fceed7c2eb..dc4b828fbb 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -437,7 +437,6 @@ void GLVariableAllocationSupport::updateMemoryPressure() { size_t idealMemoryAllocation = 0; bool canDemote = false; bool canPromote = false; - bool canPopulate = false; bool hasTransfers = false; for (const auto& texture : strongTextures) { // Race conditions can still leave nulls in the list, so we need to check @@ -452,7 +451,6 @@ void GLVariableAllocationSupport::updateMemoryPressure() { totalVariableMemoryAllocation += gltexture->size(); canDemote |= vartexture->canDemote(); canPromote |= vartexture->canPromote(); - canPopulate |= vartexture->canPopulate(); hasTransfers |= vartexture->hasPendingTransfers(); } @@ -464,7 +462,7 @@ void GLVariableAllocationSupport::updateMemoryPressure() { newState = MemoryPressureState::Transfer; } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && ((unallocated != 0 && canPromote) && canPopulate)) { + } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) { newState = MemoryPressureState::Undersubscribed; } @@ -521,7 +519,7 @@ void GLVariableAllocationSupport::processWorkQueues() { vartexture->demote(); _memoryPressureStateStale = true; } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { - if (!vartexture->canPromote() || !vartexture->canPopulate()) { + if (!vartexture->canPromote()) { continue; } vartexture->promote(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 9aad49546e..3e944ed70d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -112,8 +112,7 @@ protected: static void manageMemory(); //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - virtual bool canPopulate() const = 0; - bool canPromote() const { return _allocatedMip > 0; } + bool canPromote() const { return _allocatedMip > _minAllocatedMip; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return _pendingTransfers.size() > 0; } void executeNextTransfer(const TexturePointer& currentTexture); @@ -131,6 +130,9 @@ protected: // The highest (lowest resolution) mip that we will support, relative to the number // of mips in the gpu::Texture object uint16 _maxAllocatedMip { 0 }; + // The lowest (highest resolution) mip that we will support, relative to the number + // of mips in the gpu::Texture object + uint16 _minAllocatedMip { 0 }; // Contains a series of lambdas that when executed will transfer data to the GPU, modify // the _populatedMip and update the sampler in order to fully populate the allocated texture // until _populatedMip == _allocatedMip diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index c0b9ea0e45..19979a1778 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -100,7 +100,6 @@ public: GL41VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); ~GL41VariableAllocationTexture(); - bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } void allocateStorage(uint16 allocatedMip); void syncSampler() const override; void promote() override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 51307511c2..9fa873f6e2 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -55,6 +55,18 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { default: Q_UNREACHABLE(); } + } else { + if (texture.getUsageType() == TextureUsageType::RESOURCE) { + auto varTex = static_cast (object); + + if (varTex->_minAllocatedMip > 0) { + auto minAvailableMip = texture.minAvailableMipLevel(); + if (minAvailableMip < varTex->_minAllocatedMip) { + varTex->_minAllocatedMip = minAvailableMip; + GL41VariableAllocationTexture::_memoryPressureStateStale = true; + } + } + } } return object; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 15e98c3af7..dbedd81c76 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -100,7 +100,6 @@ public: GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); ~GL45VariableAllocationTexture(); Size size() const override { return _size; } - bool canPopulate() const override { return _gpuObject.isStoredMipFaceAvailable(_populatedMip - 1, 0); } Size _size { 0 }; }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 299a52eddd..120be923f5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -85,10 +85,13 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { if (texture.getUsageType() == TextureUsageType::RESOURCE) { auto varTex = static_cast (object); - if (varTex->canPromote() && varTex->canPopulate()) { - GL45VariableAllocationTexture::_memoryPressureStateStale = true; + if (varTex->_minAllocatedMip > 0) { + auto minAvailableMip = texture.minAvailableMipLevel(); + if (minAvailableMip < varTex->_minAllocatedMip) { + varTex->_minAllocatedMip = minAvailableMip; + GL45VariableAllocationTexture::_memoryPressureStateStale = true; + } } - } } From 6a0474934c286db44415ba0bed7344c728da435e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 13:36:42 -0700 Subject: [PATCH 096/119] Fixup variabletexture ctors --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 9 ++++----- .../gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 9fa873f6e2..14c7148b3b 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -244,18 +244,17 @@ GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; _maxAllocatedMip = _populatedMip = mipLevels; - uint16_t minAvailableMip = texture.minAvailableMipLevel(); + _minAllocatedMip = texture.minAvailableMipLevel(); uvec3 mipDimensions; - for (uint16_t mip = 0; mip < mipLevels; ++mip) { - if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) - && mip >= minAvailableMip) { + for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { _maxAllocatedMip = _populatedMip = mip; break; } } auto targetMip = _populatedMip - std::min(_populatedMip, 2); - uint16_t allocatedMip = std::max(minAvailableMip, targetMip); + uint16_t allocatedMip = std::max(_minAllocatedMip, targetMip); allocateStorage(allocatedMip); _memoryPressureStateStale = true; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 9b35f35934..c25d011073 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -46,19 +46,18 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend _maxAllocatedMip = _populatedMip = mipLevels; - uint16_t minAvailableMip = texture.minAvailableMipLevel(); + _minAllocatedMip = texture.minAvailableMipLevel(); uvec3 mipDimensions; - for (uint16_t mip = 0; mip < mipLevels; ++mip) { - if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS)) - && mip >= minAvailableMip) { + for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { _maxAllocatedMip = _populatedMip = mip; break; } } auto targetMip = _populatedMip - std::min(_populatedMip, 2); - uint16_t allocatedMip = std::max(minAvailableMip, targetMip); + uint16_t allocatedMip = std::max(_minAllocatedMip, targetMip); allocateStorage(allocatedMip); _memoryPressureStateStale = true; From 1bd95ee19fdb79a28e34f6a3ca3815df45e68e6b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 14:26:09 -0700 Subject: [PATCH 097/119] Remove didQueueTransfer check from populateTransferQueue --- .../gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 13 ++++--------- .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 13 ++++--------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 14c7148b3b..948381f288 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -416,7 +416,6 @@ void GL41VariableAllocationTexture::populateTransferQueue() { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); - bool didQueueTransfer = false; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { continue; @@ -426,7 +425,6 @@ void GL41VariableAllocationTexture::populateTransferQueue() { if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); - didQueueTransfer = true; continue; } @@ -442,17 +440,14 @@ void GL41VariableAllocationTexture::populateTransferQueue() { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; - didQueueTransfer = true; } } // queue up the sampler and populated mip change for after the transfer has completed - if (didQueueTransfer) { - _pendingTransfers.emplace(new TransferJob(*this, [=] { - _populatedMip = sourceMip; - syncSampler(); - })); - } + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); } while (sourceMip != _allocatedMip); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index c25d011073..6e00159a2e 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -186,7 +186,6 @@ void GL45ResourceTexture::populateTransferQueue() { --sourceMip; auto targetMip = sourceMip - _allocatedMip; auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); - bool didQueueTransfer = false; for (uint8_t face = 0; face < maxFace; ++face) { if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { continue; @@ -196,7 +195,6 @@ void GL45ResourceTexture::populateTransferQueue() { if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { // Can the mip be transferred in one go _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); - didQueueTransfer = true; continue; } @@ -212,17 +210,14 @@ void GL45ResourceTexture::populateTransferQueue() { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); lineOffset += linesToCopy; - didQueueTransfer = true; } } // queue up the sampler and populated mip change for after the transfer has completed - if (didQueueTransfer) { - _pendingTransfers.emplace(new TransferJob(*this, [=] { - _populatedMip = sourceMip; - syncSampler(); - })); - } + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); } while (sourceMip != _allocatedMip); } From 7bb6010149700be77c9e1337fca131f183bc87be Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 14:37:40 -0700 Subject: [PATCH 098/119] Fixup targetAllocatedMip inside gl textures --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 7 ++----- .../gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 7 +------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 948381f288..5db924dd5c 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -245,6 +245,7 @@ GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr _allocatedMip = mipLevels; _maxAllocatedMip = _populatedMip = mipLevels; _minAllocatedMip = texture.minAvailableMipLevel(); + uvec3 mipDimensions; for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) { if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { @@ -310,11 +311,7 @@ void GL41VariableAllocationTexture::promote() { Q_ASSERT(_allocatedMip > 0); uint16_t targetAllocatedMip = _allocatedMip - std::min(_allocatedMip, 2); - targetAllocatedMip = std::max(_gpuObject.minAvailableMipLevel(), targetAllocatedMip); - - if (targetAllocatedMip >= _allocatedMip || !_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { - return; - } + targetAllocatedMip = std::max(_minAllocatedMip, targetAllocatedMip); GLuint oldId = _id; auto oldSize = _size; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 6e00159a2e..92d820e5f0 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -43,9 +43,7 @@ using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; - _maxAllocatedMip = _populatedMip = mipLevels; - _minAllocatedMip = texture.minAvailableMipLevel(); uvec3 mipDimensions; @@ -104,11 +102,8 @@ void GL45ResourceTexture::promote() { Q_ASSERT(_allocatedMip > 0); uint16_t targetAllocatedMip = _allocatedMip - std::min(_allocatedMip, 2); - targetAllocatedMip = std::max(_gpuObject.minAvailableMipLevel(), targetAllocatedMip); + targetAllocatedMip = std::max(_minAllocatedMip, targetAllocatedMip); - if (targetAllocatedMip >= _allocatedMip || !_gpuObject.isStoredMipFaceAvailable(targetAllocatedMip, 0)) { - return; - } GLuint oldId = _id; auto oldSize = _size; // create new texture From e4e554aaeaf7c3f79ebdd5394ea6011010dc2021 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 14:38:13 -0700 Subject: [PATCH 099/119] Remove dead code from Texture.h --- libraries/gpu/src/gpu/Texture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index a79e679f15..825e9237f5 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -480,7 +480,7 @@ public: // Access the stored mips and faces const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); } - bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const;// { return _storage->isMipAvailable(level, face); } + bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const; Size getStoredMipFaceSize(uint16 level, uint8 face = 0) const { return _storage->getMipFaceSize(level, face); } Size getStoredMipSize(uint16 level) const; Size getStoredSize() const; From c7ac82b4e2b0eb167a88af1aeac6d4595020bdc3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 14:43:25 -0700 Subject: [PATCH 100/119] Remove magic number for kv and image sizes --- libraries/gpu/src/gpu/Texture_ktx.cpp | 6 +++--- libraries/ktx/src/ktx/KTX.h | 2 ++ .../model-networking/src/model-networking/TextureCache.cpp | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 6fe07d4241..207de61b35 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -193,7 +193,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor auto data = file->mutableData(); data += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; - data += 4; + data += ktx::IMAGE_SIZE_WIDTH; auto offset = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); { @@ -207,7 +207,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; if (offset > 0) { - memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + 4 + offset, (void*)&_minMipLevelAvailable, 1); + memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + ktx::KV_SIZE_WIDTH + offset, (void*)&_minMipLevelAvailable, 1); } } } @@ -300,7 +300,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } images.emplace_back(ktx::Image(imageOffset, (uint32_t)mip->getSize(), 0, cubeFaces)); } - imageOffset += static_cast(mip->getSize()) + 4; + imageOffset += static_cast(mip->getSize()) + ktx::IMAGE_SIZE_WIDTH; } } diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index e84cba085d..5a3167cf53 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -390,6 +390,8 @@ namespace ktx { }; static const size_t KTX_HEADER_SIZE = 64; static_assert(sizeof(Header) == KTX_HEADER_SIZE, "KTX Header size is static and should not change from the spec"); + static const size_t KV_SIZE_WIDTH = 4; // Number of bytes for keyAndValueByteSize + static const size_t IMAGE_SIZE_WIDTH = 4; // Number of bytes for imageSize // Key Values struct KeyValue { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 4597304550..74ddf1ae1a 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -429,7 +429,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { } else { ByteRange range; range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData - + _originalKtxDescriptor->images[low]._imageOffset + 4; + + _originalKtxDescriptor->images[low]._imageOffset + ktx::IMAGE_SIZE_WIDTH; range.toExclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData + _originalKtxDescriptor->images[high + 1]._imageOffset; _ktxMipRequest->setByteRange(range); @@ -622,8 +622,8 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { } ktxData -= image._imageSize; texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); - ktxData -= 4; - imageSizeRemaining -= (image._imageSize + 4); + ktxData -= ktx::IMAGE_SIZE_WIDTH; + imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); } // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different From 3de92121e0bd460e7b61118e947d1ccbbceeab8e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Apr 2017 15:28:10 -0700 Subject: [PATCH 101/119] more logging --- assignment-client/src/avatars/AvatarMixer.cpp | 6 +++--- assignment-client/src/avatars/AvatarMixerSlave.cpp | 8 ++++++-- libraries/networking/src/udt/Socket.h | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 5e131cbcd7..540a081fd8 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -70,7 +70,7 @@ AvatarMixer::~AvatarMixer() { } void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { - qDebug() << __FUNCTION__ << "about to call nodeData->getAvatar().identityByteArray()... for node:" << nodeData->getNodeID(); + qDebug() << __FUNCTION__ << "about to call nodeData->getAvatar().identityByteArray()... for node:" << nodeData->getNodeID() << "destinationNode:" << destinationNode->getUUID(); QByteArray individualData = nodeData->getAvatar().identityByteArray(); auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); @@ -409,13 +409,13 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes // parse the identity packet and update the change timestamp if appropriate AvatarData::Identity identity; - qCDebug(avatars) << __FUNCTION__ << "about to call parseAvatarIdentityPacket()"; + qCDebug(avatars) << __FUNCTION__ << "about to call parseAvatarIdentityPacket() for packet from node:" << nodeData->getNodeID(); AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); bool identityChanged = false; bool displayNameChanged = false; - qCDebug(avatars) << __FUNCTION__ << "about to call processAvatarIdentity()"; + qCDebug(avatars) << __FUNCTION__ << "about to call processAvatarIdentity() node:" << nodeData->getNodeID(); avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 3266276a45..c3ff2a74dc 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -67,7 +67,7 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) { int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { - qDebug() << __FUNCTION__ << "about to call nodeData->getConstAvatarData().identityByteArray()... for node:" << nodeData->getNodeID(); + qDebug() << __FUNCTION__ << "about to call nodeData->getConstAvatarData().identityByteArray()... for node:" << nodeData->getNodeID() << "destinationNode:" << destinationNode->getUUID(); QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(); individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); @@ -303,7 +303,11 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO // the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A. if (nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) { - qCDebug(avatars) << __FUNCTION__ << "about to call sendIdentityPacket()"; + qCDebug(avatars) << __FUNCTION__ << "about to call sendIdentityPacket()" + << "other node:" << otherNode->getUUID() + << "destination node:" << nodeData->getNodeID() + << "nodeData->getLastBroadcastTime(otherNode->getUUID()):" << nodeData->getLastBroadcastTime(otherNode->getUUID()) + << "otherNodeData->getIdentityChangeTimestamp():" << otherNodeData->getIdentityChangeTimestamp(); identityBytesSent += sendIdentityPacket(otherNodeData, node); } diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h index 1919e00b41..df03178630 100644 --- a/libraries/networking/src/udt/Socket.h +++ b/libraries/networking/src/udt/Socket.h @@ -26,7 +26,7 @@ #include "TCPVegasCC.h" #include "Connection.h" -//#define UDT_CONNECTION_DEBUG +#define UDT_CONNECTION_DEBUG class UDTTest; From 9c7353deb206581c11245f44993bca56cd0c3fb0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 27 Apr 2017 11:22:25 +1200 Subject: [PATCH 102/119] Make record script able to be put on the marketplace --- scripts/system/html/css/edit-style.css | 21 ++++++++++++------ .../html/img/loader-red-countdown-ring.gif | Bin 0 -> 38563 bytes scripts/system/html/record.html | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 scripts/system/html/img/loader-red-countdown-ring.gif diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 0a449c111d..fcb1815ca4 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -12,49 +12,56 @@ font-family: Raleway-Regular; src: url(../../../../resources/fonts/Raleway-Regular.ttf), /* Windows production */ url(../../../../fonts/Raleway-Regular.ttf), /* OSX production */ - url(../../../../interface/resources/fonts/Raleway-Regular.ttf); /* Development, running script in /HiFi/examples */ + url(../../../../interface/resources/fonts/Raleway-Regular.ttf), /* Development, running script in /HiFi/examples */ + url(../fonts/Raleway-Regular.ttf); /* Marketplace script */ } @font-face { font-family: Raleway-Light; src: url(../../../../resources/fonts/Raleway-Light.ttf), url(../../../../fonts/Raleway-Light.ttf), - url(../../../../interface/resources/fonts/Raleway-Light.ttf); + url(../../../../interface/resources/fonts/Raleway-Light.ttf), + url(../fonts/Raleway-Light.ttf); } @font-face { font-family: Raleway-Bold; src: url(../../../../resources/fonts/Raleway-Bold.ttf), url(../../../../fonts/Raleway-Bold.ttf), - url(../../../../interface/resources/fonts/Raleway-Bold.ttf); + url(../../../../interface/resources/fonts/Raleway-Bold.ttf), + url(../fonts/Raleway-Bold.ttf); } @font-face { font-family: Raleway-SemiBold; src: url(../../../../resources/fonts/Raleway-SemiBold.ttf), url(../../../../fonts/Raleway-SemiBold.ttf), - url(../../../../interface/resources/fonts/Raleway-SemiBold.ttf); + url(../../../../interface/resources/fonts/Raleway-SemiBold.ttf), + url(../fonts/Raleway-SemiBold.ttf); } @font-face { font-family: FiraSans-SemiBold; src: url(../../../../resources/fonts/FiraSans-SemiBold.ttf), url(../../../../fonts/FiraSans-SemiBold.ttf), - url(../../../../interface/resources/fonts/FiraSans-SemiBold.ttf); + url(../../../../interface/resources/fonts/FiraSans-SemiBold.ttf), + url(../fonts/FiraSans-SemiBold.ttf); } @font-face { font-family: AnonymousPro-Regular; src: url(../../../../resources/fonts/AnonymousPro-Regular.ttf), url(../../../../fonts/AnonymousPro-Regular.ttf), - url(../../../../interface/resources/fonts/AnonymousPro-Regular.ttf); + url(../../../../interface/resources/fonts/AnonymousPro-Regular.ttf), + url(../fonts/AnonymousPro-Regular.ttf); } @font-face { font-family: HiFi-Glyphs; src: url(../../../../resources/fonts/hifi-glyphs.ttf), url(../../../../fonts/hifi-glyphs.ttf), - url(../../../../interface/resources/fonts/hifi-glyphs.ttf); + url(../../../../interface/resources/fonts/hifi-glyphs.ttf), + url(../fonts/hifi-glyphs.ttf); } * { diff --git a/scripts/system/html/img/loader-red-countdown-ring.gif b/scripts/system/html/img/loader-red-countdown-ring.gif new file mode 100644 index 0000000000000000000000000000000000000000..eb15b9aedd82f88edba0efea90ad29c45e566456 GIT binary patch literal 38563 zcmeFac{J4h|Nn2yj2ZhdW8cQUGj^3~W(;P=3}eO~HTETmLR88y)+l=j4cVg*rIL&- zN@XotN2ySX(t4Hm_YBq5b-l08=l6bpzTe;ZzQ5;O&+|Oa%kVF^b6*~hmmR^@a)VDS zPb|+z9`2C?3!IkU+=sO7|9O>df4O%3|MKlq{(gH^?0Qt}*s6Bjs`xH7e5X48q9(pg zn{ZZ_*r-ozFd)_$+Sj1$tBmYfCZsY`a7F^XPH^IJkc%JkV91$545EGmD)Bg^OqM z&ZY=S>$?^ql~t1X#MCLxvC2W6?T3=#4 z7&=#r*D`lbU=iBphGDwlsO6zH?IArY=O`4hJ#-juUS;|4(tD_om}_qac*Ylc_ng+@ zD%g$l`T|P9PgRCnPcf&2)Lc8#Fn*A1WyecV9>#sCy;6GA>_KE9?x@}#)sGP#5+DKT zt(7a8Pk?+GqOSH|(+t9?TQ!?XO~sv|q%-g^Z1Dd{QHZ3aqnG(cQks zNc@;`k`g}cjwo#ik(h@!2IG9CNG2x*;b-2QHxT4VA}#ZB+JdlJPQSY>!Rl z!b{iGqiNdM4a0p;TXOnQdBQ3IVhtdbU~rRH@X@K4n48yei|2EaO#eNg#zma?3 zPP?WW2vqSp*F2as&}9@vQf=Gz#?=D}%@cex|5-fcAGW#ywHtd@Fq!<JKJS_OTm4p93G#Mp0QywqnMNrPe9p z5&7hchW$4_TsHpJ{Ql zs|tnjb{Wbe1cFMe-$3&oRBN3+`P=L{Qq}QW<3(Q^9>HJgU%oXyZ^S@uB396nR~Fw? zZ$1aIUv?N1tJ@*RTvEF!N3%7aN$bRa?5e^F8fSg!#3tHvdW4Ka4#s0QD!uAWd#o9? zC`wK{v+Fa6jA;RpJ6k^8eg&^O_-glxKKtBzLA9i7yfcqZe6fI?xYD=3Fp0pY@$riG z!7SoCUFUBDZkIn$3^kNq?%0pQ!vrML8G|o*Q|T&fi$RixS)J1K_FP6bY&H?4jrqnx z0exp0kROxM%f4uWq|#W-gTRZ0RZz=hhbuFOB?S}#V&chXuh7#47&G>04pvKc7OHZf zQbM%ys;FM@5ikKsN`VeA<_v+G^iH9Va!eBIBaw3XXz0LuTLzk8rSr*G0kQ{pWO4ae zf>0F_LKwu#zRgY}RjEh~4ccyeo0FYcrKU+3B6_~f4HbfZ|7R5b`#+=9bz&FDm~|=P zekb-iy1Cr`mvH?Z)GAz1|77)5HT-1_E~w9I5!$o~=X40oy2K_u;%R+iy@7o#%Dx(9 zUtvNjHzl1gCl^_e3oIRSH#%f*ayV+`cx1C<+8VJLSW3bgs}I;X#o1D0?5I(A>RtkE z4}lgzr0pis!tJ@l4kOY2MC<@Z?)}bf3K!aRs>=?l%XX^EI<7rkT-{w<-Ceo&uh9Kh zAO3Hgzlr^yNo?pU&%>WE7AbATrHROyY;VRpso+vYki;tbvTus80=86Z9mZM^k>W^| z#2Z`~N6Gktg^{k*g$F}>tMcS@w+zb@odY2fW`!$W*W0S#vYN(-GNMFax-6|Ebn?1i z@a<7x20YXIvU!pKGU%xWN$$Kg6t3gh$I9u2!pRqvLAl^EHe{O4x4{;Q+(FMCBNwy>bl@7ljP$GVp&^pbarCUB-D{=-K!M}yCk|a05IH^55i<@Z11o_1&<|C>Bf6;SB$AYtN3E;HFwWZ1Axx|HWYAE;(}ImTl6eAY z1MP_<)lLGHWyDzWt@0!@9@`t{2h;fu?YT552JeajoKmlrf(xzITn|VhX_SJPB(;30 zmmWuZm`Q}du6#hw<&sHUgFzPSrI+zVN~7QAfv8@*S0?gB&F%028>mqhO1uUoT+-yl z>B>*Sp0tJav_1{*2VX#yWy3^`$@kAR8n+NBXH8oU?5^c&jTjME&iHM&S4PhS;C#5+ z*IbB@(t~_>m@Uf>bEjvvv2PDq04@NQ$T7UOXOW-A7qYQbZ)lH)52v5E>=RW;@5W@$ zDM1%f*iFyH1pl~|o)y6DNHQQ0q}U)2sKT9`M3Pc3v3G+^OygbK46BjZjB3xD6HVC5 zGdPlr`@(w%8gmLaViH2q#fk-)$(|i~YFLKs=)ZN$*_x#LN|;tR zsR;sd?)LUYeT0y`lSG4ZkBuyCw!*~pJ+u?6#3TDA+P}>8f)de3V?k`h`2%h8 z!#tY2R)gc)#oIv|{sQHfPXhUS%#TOWW8hKNrQ40Wgk(Y6<4JETjagypEi!KsTbSF3h72Mxa`b3!kF1qUT41?+HE;OO0R@WpmP8 z@ls<)KV6W$1cQibgJE6F2%S<-5W2>?+NK-mH#W1PvUFNi#mO&R9xq%)O?;4^ z4N*l^h!{y<+w$eFVQd@)0DK4Vo__*x{$BzZ`L6=_S5EgS+x04c*UMMc>@I8IFKB)T z@DIIgUu$SzZDe0*Ogd>oDm5dYFk6$%7UbLwT*4}lstBBS&&S8d=WogUA0nBdt31zs0$7~+_SC=; zNgao5C;;2b2UEe9Qf7EX!3%^U$|1IHV;r;(Eux&jpDY|1G@$*S1mX9fK@>ieHSqjJro zZmUJA4!=+G314kiUtkaHV>-Jcgnd>!v2w_aGwuu9IXTNFW*xEKN;COVaxk;R=-1z1 zN@SH!BRdE?Vj;MCvuJFW~cJf_$23K{hVk zs@g7(Pp3S_I?ebZN+xsDO&E(L8;d_KvUQ-19cg9U4@|awi*kdJdR7>D1ak>yjA0yI z(hV;!4To-Xot}WXJMi=zad4X9*dr0j8g6dkAGgsOh9;h$2zuY_L(=7#=M~0QMgz%a zZ=G@Z@L-lTYj6Kj?TJkcryi0qis)8ml+)}-FdoiNtd4SG_>v0Bt3Z`VD~OIK4!Hmj zU)8MFq#ZbY%Pgr@18kJX-(DWmLlQgMR+@ubAr^}r=<0ikw;EnaY^i0V@I|3Z#w4@LS0rjE*XU8J-7&iZ z3`quqVz%O2r(j99;(wbkA!(^OOpAtXa=0rNLp;+;l5^j3d=wshRI;%AWdE)g6}!#$ z&u?QK-A}VTdn;c8ed#rA3dKMkaJ)2N+OudigB<7Ye6#Y=ULSNUw-J@R(~7Xuq4O)= zl_T`>ohs(F$$QnD88uMN~C!*Q%hgS@;(0_!Zpy-@uf(;kBiG`f>-_IU}D=(Dm~sTbK*0ldtfxfnalhanuE zSDyF97b&(GnJ1A~{*nE{^Q|hJS3vt_C^QfY5~9mD-khzpc0v0PvX8c26wC4b*bSmtueS=n{ZBhO$2j2Yp!8^%E-Rl zlvJ|jR_AZv8rIpH9JAKsZ|c@{`ce{p7}h`C>iuglUYEjq2-Ju*5&X{zIM8v8#A`a3 z`<3!T4gX?ca|JQYg)4>sQtz&bV=j!>wel~J{(gZJ`lG66{5EVJBR)j=+8oW^RQoCzQwekt->nqn z;_JHrL2PI)6g~$`rqpW=W;~Y}=ufFspqQ5Hr`p}El=G~6?kx`Wu2kGEq-ju_E^r%X z%?vyP8(6GRIzzS4>IbhB%XqH3Pluy~1ZGita<^LcZyFUZx@)g&6a_r;?4>BtBhp&~ zT0N^hI!02L&e=h*%SYuy&|#3iokr0-y$r4CB?XegtATBzQeB8d2BLBT6mPtX1>9{F zHJ8i(DcPMWtvy_Y5vg1rQ6lMNzEE*A9K`#1$zRWB3vMzF=*=?N)eQ7P*VSQ!ZOp5I z5aURcH;s(`l$-7pZWBOK)XSMfTx0hI9N$(ijXkolKr)X{v$B3t1W%;%(hP<{)ujQ` zENK$%?L-ov@x^XUugwE(n52zlBP2;nUiNOBQByrF)1^gwp6Y*nf|s~O8qBE)R2ZG% zYb|hoZf~BwFeS8V{Lq6cY+S}!@Dmv50}_0!7N`))gM}h-d-9o!l=k3*To!ff?Lql8qe4^@R2Kp|39;-!~ z9G|mqEgarfhl*nvN7BB|Q=1KMOJYe%cjevKC`_Y?*Ug@`p4-Q!VR=osYwwfTgx>eK zYI3b$;6#M?6kswiq!@4~ph*yZ*YmRuV9FzIaqN!2NwhK~#KdOK-r&N`JcJPgY2k=P z#eZD&uzS-qK4c)g_>lm=Jo}ELLU=xQChgsqXZEHR2I4m&>>b28F`iqfXAET;;;=Mi z@w*qGXRNw`lkZ4I^HB0~hdrZpJFL;DTIV;kT9;|hA{d}dd)0XZGhdkVZWWY^hZv>` z7!T=gLMXgG(~90`GVbeMGVh-`k-`%+?LJn z_P=xZtqYmBLvyM1A1vQ~_Rd_=mJ!1{pU)CAFFh35{zBXmY{<9!$dFKMP_l&3vxt%; z-WT*-2LGd5kU$gKY5yRw6b@#c=~pJUM=DPgiix2GW6HdEmF^@f^G%C;w(D7drPGA1 zw?EF4M|fph7iH6ZvR<g%T3p<&^_{+r>;Cf(1U{|*v_s`<^r&j)B)cmJ! z|4lf%xp{kg2LuGf#>W1g761Q~6+>5f!2CaB;MvMPXtshGz0nfmYYh?Ca_z3G0OG~e zxBtK`?@hJo(L`MGS`6$_(RW0VIJYKz2er3&GP%N+B&L6GUHBSorbw3&T{JQjy-GtT zFM$S98tA>@2@L??(>tpafNqBs@SIP)_xnO}`<>&?9^hh6_bZP71Qm~0y%8h#666S}HlxriE za@(bg^+yJe+`?^|9T~b`l?%7E8FBYKh%A&ln_feIWTjjt-+k;}hgTV^STQ|)H6!-^ zLry-z1`}p@Y$Ws8oJmMspy3?!Ymt;Mz2Yn^GfMm}m zPWPNPYXyrdSkTcl=w-9JEYi3~T5i1QE*Ng_cBB+dxGz0cTvBHwiXnQ_M@$i_d1(-? zU@$p{NvT#F&4EXN5r-=srrGcq}Iuc$UK1&b1Bdny7P?_Qp}!-it`nt z663<0TQx}}y?i-YapN4;8NWbN!MrnX%GF=lKfbfmRp{`7;9U{pQO+9=>%obh?m+zt zogU9PWsTl#0T#S{o)sPdC}I=%F<8bzLXo6k@_yM~`M0*nq0JtdM%T59mB;(^PMRi@ zH2MyTUpAT#*-Jo6ckU%vfWDd#N(Ml4Bm5XyS{GgpG{AgkUN$1ONGuz<7~+{l@kiyH zj>ZJKdjZCGxXIEW@8UO8PNVAUKcJ%?>}$JcGmLw9e@yYy`PIFAx4uP>7%{rb?gc)$ zHuorT1x52XkZQCC-1oYvgH(EGu^(!joY5g{^6b+G_`|NM&ySuMWOay|RA1f)_uWl- zaX+w5I!`$5F>&v`9exVqd|nL-pb6CG-^|7E4QI}az3uVu;kc@99fuAzY{tDcWORSJ zyA^p65FGll>%cSe3CsdX4qEXcPCfD1im2~ZHQs4!3#a{;uUIUCh4Ed^JrAu9QI+Fen{cW4sU^V}9);u*a) zn67oNP8pLb*6idm(>0i`Im*W^$oXqMto2AYeG6CCuKUV~KZ4zZHftp;Tc=n%YBU$4 z++dfR>XNvcb|-nQinZn|2RS$gt!dhIYkA#Q{=v=fj`BJ>x2*wm-Cg$hD|h*ito*m( z_}?E74-bET{{sgO{N2O)ztzL~gQ+Y6>h;Hq%A0I&)Dr7S;uBKXlBUH->=%^V%KAOk zt%ytnuCtZE24=`ucUL_aI`;yvWl4=%W2=bP9ae6z((nv(E&_y_UXF*pw!}as0Xh{{YZMSP@r52P6%MAc@Thj9j%rYgt3m!6KbI zNm(6xRi!d9v|gz6mh*2rpQzgMA9ulg$|iXt^P3GfH&3-777G-xr)yvy8{Z0u=-M71 zXzc}sRw_hHl;2=}h!{6|CArz>YscP;^a!Tb0_Nkpmm!f_wfsQi4{u0kS3BrddAPkx zbNl!Jn2Fx$l_i?5jAI_~*r!j9U#1k{)>&V9R-JjBj6lI0480r8VF-y=0fUWij&PW0 zA+tOYxOp(>uwf(&mm;LW_94Guq$!C@4lonbA$$O3l0|bcQp8SM*)PE$f1hfCJ4ti= z@TMmgeY+Km#ncYv54H8=EHjjQNLx|-k6|){riuAW&$Qqv?uiha48!CR_5G&9ZM{X0 zJvhN`Sl#1;Q3IRzWZcO!dKk3R zO?ObO_l&nXKU}bTPrECrLGr#c%$UInquFDN^*hzHW%G(wj^XxSgB za6vg{2uTqaHgXkJMvug(Av#;owBaw0uKhN!vT%)8Ony99>mS5{YCV>R*U25% z&KppY0%$?nJr5=yGy3+W<+@QitgYK_Um9r0w7;WeRrDOSyS3lo79ufFOcW4KCfdpO z&B#MrB1IFZEJlyyI4k_{hPsC@Nz!vymSE6rJFWZZeIplUiw_@tNmA%bSK7(vu-r3` zU`Fgqz|uL4h|eKz69P1OOJ0(4ce0}NhUWPE7l22oafs0pK^8G2V}rd1aESb5FI&_M zoXJ^!e!@wfXNdLAIXav7RWP{)D3EiTnV2K<1-9|CdLPI7rZ#yLD5TEvKIdhjjGCh3 zY}nlp(o&-AEDM4`^CG&Jz*{m{jq1L-#Tx+GxOBtE$ugT)0kqp?2ee0slF;395D7MgVxd0Z zWThz%SPzZi$1O2L=t!WjdFo55*Y9KWZr7)rug zARBM(bO7fRXG7g*%cbUCJS~#IrRGi&x3|1rT3)k$xj8JCmuq+3`u$#nT9cYTqu8I| zTu)=aQ}Y+c_ZOY{i|PB{KmW^T$BrGbv9WP+ar^i0KXBl{-<9V7oJuovm1pK>KC8p@ z>&1fkC5$6Jp`U;RrOoz$7_H5xt{p>KXPBlXF^2NAXfgDPr#A+%3{03nlQui)NZHmroyB1Py77Dj?twe8y#cN7c&>65-P>^YB$;(EL|hFbETuza z&av2uu6c5o4wb!(P8ASEw8eB|F6^GvFvZgcWmVE1_Y$u@K+ePjgC#v-_8+F1Hhi`6 zF{DR&H2WiLtZoN~^d$g}kVrYoaliEb8d5oB5Aw;S7(2P+y8=giE?Z!=G}NXV>Ae@0 z6D>`rs*q1R_F9s0Q~5kkFYc9!qY=}5zF8g>G}BxiTzqst^=-byp6rji{H@7CZFldR z?@qu@N2EY;^m4{51hVJ9}3<;W3FVep$9Nb+?>8{=5&5QP?r z8+&gPuaTVXze7Lx+DzVIvlwwf=UX;dqJH4Ki-;L{zM4uh9_cA^WvAul1yiG~iITHZ zfmBLWO78x>I`1(=-P!sH#?CLr&IGLhq?UV61#_ap3LH0?&od0l7tWnDa z8D@7{a{nuAZMo@eD}Q>1c>RdV#601o>&}r>t&oN$%veR#^9$e&p<~XoA|RU>O^KY> zXnJK7`!cYFgS^7Km`**pa;I3lYMyw23f@z5Ie@hi>T`-vz}NV&az5pNc>5b!_8G$D zsNh^Td1X@tL5*`@knsR(K(IVEox?h z!VJw~oqTInh_CbxSv?y;708{$0YD#L05+N`8RGyhv81=ldS;dEl0r&cM=_l8HiqHPg0x26Wz?1w2agv6uks&%Snp z>qebb5U|*NzW42=7FPTYOc5-KDEn#46M^%+gdSmo3UBBZh=fGZS+nLL0X|!F(&zU$ zL35ap&%qG|`R+IS6)B9DxG|92gX+WP%iVlU*1)}+`*~&O&`>-pQE~(c*#foq2<1tV zD58Uurg#9UPXOPdQ~7+Q4jzHEt1w`h&1ef4W+i11$LHP+8n@07^WrcR%#Cyv5*WaE zf2MRMI^L}@9gO89b_Eb1dTHL$XYxH^+))6UW?GGLuk_D7LRLC_LWBV+}@KZ zsl<$Y+?-r!K`!75)wTB1Mu$u*E^mJh3?=-*?4`!w*LX{yMG|SdxhmE9r$h}}>pgvU zbbl0`yw{6P>wQU3*Q{>xn_H#ct7>fzzx<>f`E(}RM7 z!o$O(qN4Wg+xIte|6@DJ*M3K?*I;u+Zz@ccSc-3{>^~x{`UsZWa_ZVKAu}%?F+cvn zJT0T=vE6SmDdHQc*Gs2SHaAWv1X3!7&b^Qpu}BI}FzT;$%HEQw6W`4q7_F6#&m3$v z4Ng;{oTKI&1N)L2^i0}Dh^{Iy3EA!~OPo`U+fu1?o~8`I!2QdD_1*Yw(M&>Tg0KN* zIa)hakpYL#j#h-G$YrR~Je(}&l4WvKUlz%=VKav_j%T`uV)%ml_4Kr^P@YEl%1Y<^ z4$f>ui8NqLkY}=_?7}K=u}|BbE0W-hy+3vvC@QWCM)mokWkB^ z3lX!bS&mr`+W@hmW4I)$sq+*(NRPo%c0{=kjHVjvGq%|qWLu60Tg0&{N#;@JPmUnT zY^e;>N@pxZ@i}xX*&={*@|Yr_MakY0_StElMH8#aZt3B~(>&>iuzWt}zzJrk1t3S= zR({LKz2lO}u>3fGR|JQwUghDGM!iJHDX<*c;3VpHPdf*O@nHqwXj<=sa0|i$qdB6| zUtpCP8=Zqs2@*zSssxv1)T#w$w3Tc4``tBbA?)d~e3p1yp#7}^&+KB04Rs6XRlf7H z6Q_A4*bm4iT?^WW+eD&t?3Gt^DDf5*gq>mw{S&%L#|3Wq+N)@(fs+ptBUS8ADnwcH z%M5SSI;ZaqlgDgkMLdOOBzB*|7_4kKQ;v?9rk)3hRg|~HtS(UDkaQ$s&mLl=s{O9e z)A`;p8TWEqAvS|b-61LSzE~rzg31#Tkm$!4XJ$P{EJRWZ(GCeXNNd<(8#gW}ykU3@ zY;|xRSSg2oP)}9Jex;gcv8xkynoO1ProcC?04t?XrK+O%3ok}SEF!ZxM{Jm%69nL5 z2jG`BG{`watZhyACYdf=E*}@u{@Qv$w&A9DkHqHtRKB(leA0eVMA+!6xN8vXU6KXz z3(u6y`-olc#M}E$P~~E$#Uk%wQg(=>US7FihkCGo9K4cZcQGtCY2r22Jr#04RHnxC z5*690`#_-Ng0n0cHK^Iy$*^-pDf;qhY>=a0(!=^^8Sb5x5bD12J@Kd`bdVq)y%KpQRy{L#<)|R~gR*i556Fn* zmyP8lK7WQ6*g$7Mebui)!q6c75y0ni98*rs2&~G(fLLZT1l-X;`A^+YN;9wY2oiEp z#UA~`Ob+@Zji7$q_*NZm8F_sgsKJ0(Ysekm ztuQ8)bLV$SKiy+)8f)p0yTKuwJIm{kwaMX#m1FwmbwE2Np(%&4l>IhN(RSPxR2YdC z)r1-{+}EU{Eg%Pw}yJ5 zt30>=YaE+|J{aWU7-{`|URR6O;5F1*y*46LN;p?TE;e>By*P9ZDIzG2=9!;{5AyNj zH!LCd?*)z3%0{572~X=Mg)C~W%d7M((1bkNd#WmXbg9k0e%DdDfL-^Fn`sr@%znCP zFNeMoI_Gm&efv52l$Bi}eu>j!N!wh_1XYc0NCj##qf5t_)53|`r^24F^#pEe9|%Tm zl8G9U4ctUAif?6l$TpIBWp+jP-kMSi>Kq{F3P8@v$L8S{CsUSkt~MoF8tiFtsUO~I zUh!=O7nPmeJSF;}b z!iBaM+)=Xe_ITfuAl1()fI>Z^lC}%GPB6V(mnCL!3W$9(xHBH=y(!`WADsn|wzKlo zj|FohNv?6MC)OKc+u27%KJzGN2}#TpfGv%2$sQ_cs2-?#{>UjzSX{I}hTz%%IEP#W zR-_i|Z|FNZq5P~jmv=h2fNyBCi_~!=&we&Uc2E6dno_=$;_;wTEjBd3W0JR2KejqC z+*M{MAl30^b09S$B|Tqw`xZ06K08UDBA0D}+KdFlR%ihlyd|2GqoUH!kwL{qKgv;# zot%;>EPr2>@ei;T<@BnnOzKnCuH_dR)RQx5iSeMJdUk_cPf)?ZAReE>Mx8Mo%mHA* zJN@n~X0j!)QcwBifoL*hNT3r_Q?o%@1lXs*P)sv;7eWdMe-|L z6cS?J&^~LZ)q<97E#I8rS7>=nJ8w4C$Hwo9=))eAoub#`Z+27QDMUI$1h|l`59wAk zMNx0Oha`FcEM{L__>{g=5vTYD`Y?9u;-C@EKs8K%uWd0n9Ouls$cZg(O%KOe#qN9& z9k)N3O+{p7PMmM~Cb(pyU1S4jZJ9<_xTtN9>Kk&n`*KQ{xD|{Y6o3Cp!n^TVHalR` zHnj^LvY7;rRgqjGkALIMSKWP(mPFIfc0ZX59DS04cTQ?e1^_S;xNO6voUk1 zQO>G|hFaICum)aK;spwj({qY^ubV%tTPq8}=F|M-?N4QGDW2uHl3RJ8o)N%1+Ex4( z!2oD;kkY63AjplK;#9q6fM^;Aq+f&)4re7P3t=D@b!;Ke2wt&RI#Ax!9-3WSAjhOI z{m?>{P;2_tdJYS;170bfglC8^p^VT)jFCdT&Dn!=KH;iN(O^pAXP0Grbr!pDz#1$+ zNCy$Vs(@`_KzaG5t#)$&kuY?UbYnNj`~eRiih>m@qJxcC4B?28q|_^FKf1^}|HxdN zkW9JkhO5ZG9LxQ6qsE_yguY{UZDB^2n%yN0ZuhBGhwweFU8C*#^v`zH_+#XcUCdceej>oJVvo?Pp3`$umK^?MoinHOikk>27yX?6GK*0`N z*ZH4qW-n(~&-F&rkEz=~I>+B9dwz7B{`~L<=5l}ee;D9;czFCRf&ZUaqyb&!x%
L&vGtKn1?7r4La_FXs~F7c?}a`xfuZpB21QcLeYb zEf;O_>6U);;OqrUXiD9Y#8~#T-6sS)c_lCIP3(RgC|!5zb@I&J^wp^?2li!UUP7Lc zdPO;&(%rZ_piUEdDjdp?S*R>GMA}a3z}CsnjUW#_TwG$Uy_Nbt+&Ls%_~8!U=PPFJN$91RDUV|=vB@Dd3^z`urDwPD zARO+9(vGHo_QuahEm+(gvFhXgI_%yP!^32S@LmL zHm~DsQz&|WCq;tzsStYkO7xConXYMtAo=~28UE%e#ZXyu z_ZJ?DP*Y&KaQSDnAg@`5WW_rt$PC(LO9b%n>XiThL-LLb@8MM)R;t2or=uTi^2}iV zUf#9ak1u$@d6rA#7YY(WFwcG$9T=|HzG0BxdPO;gw z$mfNxJhWtIUu2w(-zEhS$oljmIIr-;h!iKK8q?%2FJz% zFa5odLG1h4ox9^Xccpv`?q!EYV=foA@5N34`+U5SvL2!b?k-sy71aRFJ4r0l4~TpF zRF-+~`ruj({~R#~jKI3=9x^1FqDm;w(8gnu74#DDkXgHRy=dqDQ3)BrVgsrbkCemy)=z2}5zic~G8H7j@kvp=k36Hp;leD)A?gFnq3!^(JGhuVr1=?j zE(B2(v4P|^jE;6!co0q%8&LerJEiOGlk_~OdSsWq`Bzf4VAqK#obX_t{w3lT|6TXM zT8*1?MGamlwtUo&$0WV$PDEPb2M#Y)B^c^Cj*A{UQOM@ET(TdP*uX-r-fF(gFA^wK zl^f3iTO!M?GZKK(2YKQJ7bYFL=s;dclpcJf8$#?(l$qjzSbbs#Bz6N89W^e zQWtMHhYtW05V4G5%idc`L9*nWViS0wkCsv$KkNm3PN6bo8wJzw&Mwe!4 zk-Fmi9%m7WBNFMp`dL?$8I9i}5KGrY1GI&VzD8d2l+N57I8Q0;eK07maJ(#|_*81E zqQJgebAF3QN9D-*i)e7Pt(wI3=MiOMcGJSgL$)y&+LK=&vAeVI&D-c&=o3ko1M!Sf zUrFJB!=@*ts~S@@#?|j>*{f`_*2ILZuE6T#UwXWOWDUXqWld<200wKT)|76l3I4k4 z;O4`e+^Ox#PD-wdEBjyp+W~;%Zo6dMDRFM|Jk}n@mhRa?0sRp@fs*2FNLOtpMUti< z;9TOSdhq$AuX?!=D0nF@8h0cJ6tv@j`*M&E{0<xiDlTU!j|D&`g5c!+j~9+ovf{VuS>of&0|I z)X&RI#zlLlW@C{LWgPJQfb>;90jE3Q^zxb7-6FRru24nSJ5~FbSX2YLxorAz(RK-^ z?BWBm!4k!MY<@QQXyt`IZ+GFBCr`hKtJQRs2=;&p7EIoI;2&TX3~P12I9(dJogJE; zw!Ot%;nMba7Dcp%{88VvRvQrpaX?f&SN2PwLig=&=fzo5B2I|a0oYm)sZFLqUC(9DgAz6DCveTMs*&Q8ugRu; z;GNNe(zPEZ!X=&oA<&pYy#((Uyy?d5ni zkn|wB+TIqHH3^A2$poFV+e5d$rE8`Pt?p#OvJR+N=YSd zc}Voxcv)$-&_PsJ3e<;Bd1S{2H1cFQ&rBkJV*&)9%_}{HhHQcLh?O*hR6!Mx4MiA1 z5-d>!x`YoNq5wj(|b>M^Aqs}8gmI2lwp+xTNZF^*E8rw z6kD`?sv7aC`^bTzMA)8e-p80okR=oznT-M|z3S#OatH9EIo85aJRmi9j8Ir}lJqT@ z#!u>o{!!Ih3re%(HvBo*{lkpUA7J}mCA`0KczrkIy3}2ZdWmP(n#m0Y_CFRwl79A+ z%T0d>-X9g^g75P_-zRU=w@`kzlo?p3!&q+FYJK=N&W5}FVlSS`4S#=G6}i6j!jHo3 zB!5qSe~Eq9J?9^O@-Jfc=lR*9`ag6$Jw12q*s*KZuD`of|5589p{qQT|CXxNaIS1s zAJovZYK|h9oH`~duGUdEl;;bV*swQTYwTQ|wK6i1Pv56a8>?rUPXs?{Yv|U{sN#Ew ze46f3YnK~ZfJ_{8t3_L7K1BIlpRU(kH+ z=V%5V3i-V`4|KePuK=vn#hO%`5q1P@L(1bDWZrazZ&lEGrBY|Zi|(OE7uD@n@xo5v zuY<~-`-+r?d^_L1H{5{l2W<8Csa9;e`;RFv?aXgV+7$(UJbUtcW5N5GiO#L+9b%HX zV3*@bvI?m?;t+xUNzPjSb)$(yu@}m=ozHNbGz5Pg0@6`|cY;x~IF&Ys!R_Oj2sW}H z&61d3KuvqEH5yGU)9}Zs6H((iqC?frM@Z&s5)daW)4M>_JH0xe7~iaZ9258X8TMAz z#q2yxT!04F7u-Hu0v3~*NRJQ!`BE??gg)UdLKRH95#9w@YNplB7XAos=Pg~-E&yf{ z(|KxRiQpEdvs~S0b;-Bd{nQg1$e*5xdGc0Iu>2J3^7(nyOW?FszYfBNzeO~VzzMcoA|CQpQ#%1*R89jZYz zf-nuv!FC3tA(RCPX7X8KMGvh*9pVUyD|QYuFAqbsD;Un^^7ReW5e2ny3KE@O*>zpV zpXs_XGZ*U6sRP<}{QdqpT&^0bs>!+SQcAR~4<-J5z= zwhcz$-8E}VKknMPGcu%|JuCMO7ULkxdEgx_D=S6#u~l@K#GDn;IUFBn+jcXL)ZM6Lz&Tq8nreP2`h$ z3sRoq0WX}pdH+#jvcjjciQD)A0)g2{N+1UGFd-N#(LBOblU9aq9bpSmVZ4hEcSAhv zNfHMmEaW|xGhF2vqNrI})oxCbyO3%Oon^_--$Q8)zrRa>cX`=Os+(fJs1M8H4bvkU#T~`&q zw%D1wv1)B)Wb`_)*VaU?x1;?iE?&;RY@J%05nAs&b7{TiR{!j5{kos(cU1pm_ODX< z|Lb^pdHucd;yi$o6Pef~>zgN4qXZNhvWGHG;+@RZu15un!wGqX*<)=BH#9UW$7ZvA?eeN! zdlU1YV0DS&7DW+aHzd_$kqT{lvS+HA9v-8)2P=#MUp>4kR$Q9D5hujyh25A-+vtmz z>C2X&jvi^VKGvRNBA4GPHZRanVl5ga&O4v16pTCV@Z_Clx@Ln5yd_~G`SDavs@i0Z z?1ey8{07*vLdj6_Nu?6lUHNCdj$)L;pc?FxIK+JMjXOeO3Y%t;TQ{suyg%xm@vg*n z(0YsK-s&&C*h7`EUsvDckC|>tICFU4fTPmaZ}0mYHAbZ9+jPE~K4OF!E$0ad-7A=M zrQrq(#kXDz@^LpAU19H%6E~wDP)<>|Cd)g!`_i1-3dMzWUBP{hcl!qs0%p~8RT}MU z{-dMTH|Lbo(3fx?M3`hfT*{!bikZ7{gA)yc!^-4}nq8y^I2cZV`U)+xD71a_R-6r4 zN!mRSPaUgzTB6r)H!d8}+9plLOlXg#n`+l;;uUJz9-owmE~FMPx0p&3<<-87#%ZN< z>g?L8aXnQMlg_*~h>+{^xPnT=M6DP`76YxaRns7c)?~n(S552ZLb0!^anBne{S=yb zhelKoaq~(>cDQLj@)c%Rjek!IUl}0}Tx7Iqq(Pglv558_TY1*b`<^l`_&^|Ej~J4o zYwKmIAKMh89TMAAs+~1*zkTzaS!_X|217X7HS1?@{Tjudl;HoYcXm_E*ORMw6m#G1bW)q}8N735*%)NOQ~J|b z=q8-mo^+*-ShDH^J#hrD^|yTq_%;P>mRtKxz$k!MY+)wnLKW_Q_`6hFk%H>iV+;5h zlu@L!^kT)UiUWZccT=UysnE`+v$*Gqd9Uxo29NRVcX6VesGcYxL{`;YwJ=Mr&{`Pj zaB*Jh-Vf3{_>VDvr}(fgN&rYXo^19&QBrnu>Gr%YhtrjD(S%42%HI*DSh6R3b0q}; zeKUUV9ZWSvF;i4sGJ*Y^`2-}3G*nmk#P}*=lH64s0I~?~;oFP_t4|~x_GDlHChwRp zZJq)3=Gbmqs91$64kKJ=dvGZS+vLsyrRt?&L=K^u8fYH@UrrB#)y-7?ii8-^0Uw9O z8E~ul&6EVjx4OZ8gBlzd?tB}uCqc=u#7IZ1N-n7m1xhO$&D7@U5s)!WR_^8`?ygFQ z`qG&j3hRU@P^=K8=$iO29XhcME>SrGQscouOe5I*w%q`cY#y*-5nCi7fvJJ>iO2q& zIg0!vmu12Hha~hbD_Q?h8~^nRjGrw2(*qaHKb-2GwRb4=sBm{eUQz#jHS;-bLW?dp z8$SKZYZNMfO!JnnP4oVkOCqnmQo$k1${}NmW7<|n?kg46=6DZVuaOtG_ErTOr~S6n z*dNQ9zi(<@TND|#)|OsJZ~%py6R*!A?ciG0T>k!9DypPuyv z%|1Rp5fKr8zeeHz?rRjFt2|GC=Q6XBi9aT1vb_o=rqatNrs3RK6nP3Frtb7}HO6H% z_9(V5X>Bz|F-G9}3Cq1DVYz<&sm17|0kw*AbxvhYdrD!~^XreCqMXw4S9uyVej2?C z|L~H;P==6u?=3`6dzh1oR$m~JQE4M2w~?t9GG;@Bt6}y`rOohy`85N9CrxudEI<&Z zDNMw0hhCnfn@zX0@41#|P2Qnl^b#L%&JETDS!MQ0Ql>@)O=*$}45PrzB~O=U!HGVs z=78}>SMp3pgiWH}DH<3P(pcI>upL`+(_D&?`%x#$DTB|8H7T^m4yeKCVxvo|5lTv$ zX*-K829IgJRd~)fzi{DywRi6SO!xmEci7lw*lZ3tWEkeW%^Z`qne*GJ>7Z1IaxA2i zbX_(hL=Ibs6d6e;6S^0-`glx*XMNme!jQw=XU%2(Cer7 z_76O6kLUC8eB2GE@gnJg*}fB+g)c`e7hd{t@FH2~dDlUhWz6ttM&}?|qzES#(hgZv zs_MjTnN>l-Rn0fF00!v~b#?>3vy6DXw`pa7Jy)oKw5%M~-EA;+UT zZ$w#|RaXVrZamjo!mN~7S<2YU(+74vGg8hX98Ru0z4ox02*xU?luFEquh#}#NXE)& zVm~&*-&siOtMpA>K`%b3Zj}O9u@cdt1WI_M-_9nMpk~22Vm5zY?X#J2&y)v=y?Yds z%A&k~U5(&hwrwX#U9nkl)YOBQS^L`kkfQ|un!#YPWW9d-$K>2c>V2=IJrhjw<%olZ z=G2O|gwaMwYopFhh$M^+omxqY09KA}+O7GMW3HeHd=bJWf0f;K5xwkA1Hk;jC z>kDD=r3T+CRfAWhziIiENT44@KU5Pz(RW660e=|7!Ks>Sfn9(`H>?(EdDr)<5Pz6l z#djWZpU(Mh5E6U;biw?j^x+4d*Q!49H&+5%RbGQGD;_{x;-6|m>$bXfk_o7uCeSvX z;mtzQt$iIcyz^iu0w2I|;ZsSm*W_yvYVZT^7t&n<2RAB`!!Y{CoRcujE@qgS2)?HZ z0xHD5{-sEP$LBCd;gOUSKtJ|YzaqK`OXb3?<7T9B$uBg0)gE;pNV4_)yjI4MbzSg; zT#BhnI=iNpf~Xl#;mBH%+E1eJ{t9#m_xuCr8{~{!CsBr~nvCg#h~B6; z%MZyOXH0Zj1y2JyeaVes-d&iR58^G(w#q+hndc8}R-UGgGx(D4(pp$`1ZN@@Kje1< z15#^XS>g|9?@yB;RYebjED=nQrxIAVTLL^+(hlPw%C-HcjP=M2xuN_Nj2Z*Fc3cXT zB>~dxmXI;VGawv84*Jm~bt7fGbYmKhqdbpycAM2k7S3v@&BnO-HcQ?svK63)gHqfg znF@ivj>a-$(x%KZPFjh>x+@rxu1Ql!(;9=X zF{^J-EMoeBJxuwsmg7Gt$@nMR-@lRA<)2G7cxe=U8!Ps`%PRX5PTc)i_r?8+gU^>t zJuaI4$u6o~WEY)TOh7L!iN%?$#U-(LL%etroNF&W241SrSV9?zSHj{OVDU z>rDC!yXf1tj3upII!juNV*P#PN%aeM-);dC)m`3WyA?^3u={2U|X!jqqHKa2yO4>79R=B*)! z5nb0DZE6-SgP-bw)^|;lc$wGI3W0BTo-Nv3XpU4`Z&%w*2sOHIL`!HW+O$`Y+Yw$W zsD4aI>#d_RAs0VN*NavLXM}*Mjl{xgjdbQzbcrEK#l`tGB;ZyMN9*AB2fLbnmyIYk zOb#tShL_7l;nyw1t}&`~@k5C2n9L)kT9EIs$2pi9N!hqr==SHT0i)7>=r@ywWjr7dP8y41_xms|MGQZ7+XPbMuiEu7p2_ta03K)!r-qC^qJ7UJ9vTce%9JTfiPSzRJ%GFI$&FV&u-;qd3ed$=C+{HjV7; zMVI&bg|9@-D4qtK6`WIJ%{!fLnb7y~uW7cvTx%hF8tvTx6?Bm%4GwRh^))lvcexOx zqbwoR4@F8E8jmui+dM@Ga~EZW2C^(o4ddQ`P*>)d?fl_}=R`G=ft;z7C9Cm~1qiTl zECcESp(-W>NvoD&K&o7Kh*BZt`mM3FbuUX#H@&sf9Ez28x2)b$StgaD_Axibbc!P7 z3FfHHOr_(Cd5Q(ICda49G8##8vLW3my$4NT&an{M#$+jEl7zGdga-}uP0^ehmeoz* ze{Mq|zTEJp{?)<~AW9dt9C`n+Oj%q!^|!g@Whq_$SRO4eyV#|?)smN8o zOypU)1TH8}CJR1M2>JCWWW>U7G3I*I$DpSRa`DAl%!J*ir#uzTm?!s~QhC;sAiZZk z58Ha!vNSn$zkgB~IuLrQP6jb>7tKW9^+Hm;h>(rJ9(Ve=6x2<3p1aQ99fL5~cD3i>NUpGAh7`_trDTCX%l(fJO$s(vOu|7?1_TZd;(fSTr%MYH@WVX!OOXvDk!BP}Wx=SC3Te^8&97Xm=Vx@DA zlZ5(1SG5T22DLRZ0)Uz@Uv!d4FuA(^Wh>`R`_3_A z$-Sl}^|2=iQF||gVjpyI8kHK(@9`mhzO}BKYSVh{zVq3HJ@?$tlJTJTMkknqNr-kL>lB=-{+G!mZO}!@^HvW%7WZvJr*Ebvpaqr#liR7Y1QS9sEBejWl-n^92Bbx_Hqp8cmC+I3X2z40l|1C`LZ0ndy{{$W!;lg47`tXxE3z<@(3JxgOG&RfteB zs9#BG!MF&%EONg+8v0XYm(})PeDL(XqJ(g6e+Bu81J0EMG03l!B;MH;i7S-F*F*VB z7rPGLnXAR+$*D`zVsU@fpT_lo%VM7DE5jgau{L=bx*I0GT1uaqvmWz>>^>MkX zYDp#kl~(>|MDsh;@4q(P{O9$Ml4-A=YqqM1cdkTFd2*V9Hm!)>J)lXnmQk9w)^r6)WI~{#`h9he#4x;~ zbHfAc$Z{8Nd%^NHB{;#ZwL=wxdw4jAPLKP8?$vCkqm={l3)Xof`8ZjP5u~nN&#l%M zGqPv&!Vtw;@Q zIJmoxvAbuWdj!x|t1fqU?B2O9?RJJOTgKw#I7-O?VU-eNm!&2;LbUE@X6H`^>3}q+ zx-00`@N<$!cYelW0OysB{R%!!{t|(K%H_{I{X80vD?-}@k_lc`NWR~y0|TK&_`_4D ze2lvMCDJ3~`<;#>PImjtlL!HZ8|=p+?9(_fAyn?i4*eECrzukF3F3*|>QG!4Ny^8( zYxD&-UMIK^AePOflp&&1^MU*VRtta?Yc zGTE<;2daq-vXZ=IYj|{oX&37hbpzy6zU;--CM%SpsZ*3t=%BHv?y-k;N*yd4Ba$6^ ztP*_{`WcTZpFGog7Km8MWdd5iY`7e?`IXVu~pSnOb&ewNL`ro=aQ`v-s6ZG{?SRD_NoQ*G+?Ph|Yg)dmc0 zn%J!tJtha;KdBtH^I6k*YtGxw_w$@P&Gy#YaqM0VyH~n?3XE$Pa!65tzQDUz0eX7Z zp`>-ajjO1^w>kVbuu={BZGoOY>z5sdIo;>+<-_xoHm7r+W?XY48-M3HmA#$;r<$|h z3~##<1{qdO*bCU4eM_}vc0UkWtv3QY}!jG)`(?iA?j-krE3?}mRYP05f& z7~VF)%Z3koXd|cUn14)L7?fY#R(JA2(@QU%WQ&33fs?k;+n@u2O)(+%iWC}@r}(2GC@LTo?0ao6F_WpI zr?h*O^Bh?oa_0u7`e>&4fs)D4WW)18sYg%iQ9p$9CDG`K{r0iCUPH61=7dOtrzJzm zu~W@O2fTi8O!opdhTk6Mlu2mKo~9hDwRg6^x@$S;{NrEUue>yv2vw@mbZElR#2Wm+ zbG-jTgTEx6mpKP|Vpaa)fw7AtXMHzu)XGrYGg)useq}KRebLP0{9+7R4BTCeK`$Qt zilur9xSR7ePnGWY<+7LgO_J)%N>_|J5c}KxuHphL@vwQZ{VHnl;x}{=ue(I|TWZ-@ zNFMbi85xo~+d%I%@HrX!6v32PZCcM$qsWl4T+V#Ga;z7QpQw5Ec6bR{W&>H4(W6trtag;?##4C0Grc3(E0kNdX2Scl;Os(JChaoQ>Hcp71#XGt7?HY>8*1;h}G8@^s+)8zDU>XXw|-u zFWu0iwz*#}^zGy7?5elQ)GKi$=k3$9jT`84e~h^*q0H2jrI63w1@%E3rb36^qo;Ro&tsr&~<2IY?wZWY!<+VO|O2}8b`{;U<=8FyzPJm z7t$whbvPGP9Ijtp1lTx)vAkM(GE@-O{j(yJK_4gObo`hv8`}F$AhV9to;)5%>>FjW zjHZ1>u$Nws*D0Iy2~s?K<`2+Or|$I&N>QxtarcF>c2Z=`o(K;*9H-?clB-K|c>y>S z%s5xOHa(8oRM}g{EP>4VvK8*6RXKS%=FT`5Se|Q+uG_N3lYJd_!VQ)3i@giY%fdh) zq8d~(CG{%E(774+3_frolIp_KW%by8Iys#;~P5C>|9im%B8Mx7{y%}9Ic0OhsY z=|vw?DEMyw7D0JG83hMB>pTZMVqX_2k>U_9v=#5Oq?!%(x2EM0wmA5PT8^#wJuvnj zsfC()kM!%kczp^RT1fDFah-OwamV&mw8x}M(j*3Pj~K9e?w5e{33XoG}%ZAKp_Levj16*)`&mVS&k(o{W)vL z2<;{d?%d89Nzt!*GmVcXwMTDWm@et72l5~IDE(}i-Q2VwHAn3ZWr8G(xrr!zkt9+I zSNK^m*)O>SNV`r>5kyLns)s3>s%%A!Ps&D&q*udP3UkKO!015PLUHmXC6&<)gaC#d zl*vlG8;jX*qCKS|ZIF!D%2p+04TITM0d_70 z_Ah7EmpSut?IhzX_V1esv-tdX-{Sqzx5r0ccD;+--R0Txw~)V}MF8&>y4W~x`o|fo zzfV+s`_I?5jeq}rH^~3B2Kni-J}YHOji$lX%+?oSbaYc>kovy02XvmNDeC+Q-Mt1m z!DD{i`!Yas>P|P73^LBzKY7t0BTRPT7x%uV4*m&vkNU_ilr`6O@Wwj`9J#(F6Fd?k zHpoA65uMk~VA|o&b-S)supYs&>l@N3+hucgd~PHJ5CunCR880B3NZz=W2W(XA7TTP zK`^y7XJ)dVNy}s^uJ@czPueHUsL|Op-+gdjW{!$pO^qA1I6_5DJ!^+s-gb`|#u|UT zIV?nG^r>OaBQs+unV3;fPJ_gjJ^^wZy9ciC!A#rzTBdxV8A6$tnQb)B%x~a*oaf>2 z*F~YzgP%(b4APmoeoq~#IP*~#6!c-E4K12fSF&rz7`dAmrGF)4v;yp;uAtPyrq@PQ>brrL&1qe=ino;?Z=(6@q3#? zasVjh6rk;v{y>r5j%Kpmh+tZ(MXGPe2G2GuzhK>_j7pH6!L)Xa1oF z^@dmwsQkBl>u{j0iLevUtK0l$eETX);~Sfp{;z^z|6| zMjM)Wn{26{dSAZ@BZ9eircU#uC@}gNzlzpyz;{2d1~j*0klusOo^nAF8B)`EX)H(qXE-_Uo4Pr-liWieBlL zV&*_zlq(>wTUTc9zeaJBUhkeU<6V0vhfTR7H8NE4(FRfSvmmh`#a+hW!_P)Z*&e@} z_zwht+`~JSa@9+Ej{{G|de*j`7y}SSb literal 0 HcmV?d00001 diff --git a/scripts/system/html/record.html b/scripts/system/html/record.html index 14a3708a6a..89392e6951 100644 --- a/scripts/system/html/record.html +++ b/scripts/system/html/record.html @@ -65,7 +65,7 @@
From 19d5414c30be0ba212a568c49aa74ae8f7231f4c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 26 Apr 2017 16:23:09 -0700 Subject: [PATCH 103/119] Reduce High Mips request size --- .../model-networking/src/model-networking/TextureCache.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 74ddf1ae1a..55704236e3 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -422,9 +422,10 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { _ktxMipLevelRangeInFlight = { low, high }; if (isHighMipRequest) { + static const int HIGH_MIP_MAX_SIZE = 5516; // This is a special case where we load the high 7 mips ByteRange range; - range.fromInclusive = -15000; + range.fromInclusive = -HIGH_MIP_MAX_SIZE; _ktxMipRequest->setByteRange(range); } else { ByteRange range; From f15a34e145fd1ea64e391d5c4dde3b7d92868147 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 15:41:49 -0700 Subject: [PATCH 104/119] Cleanup data pointer calculations in assignMipData --- libraries/gpu/src/gpu/Texture_ktx.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 207de61b35..e3c4b325fb 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -191,9 +191,9 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor auto file = maybeOpenFile(); - auto data = file->mutableData(); - data += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; - data += ktx::IMAGE_SIZE_WIDTH; + auto imageData = file->mutableData(); + imageData += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; + imageData += ktx::IMAGE_SIZE_WIDTH; auto offset = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); { @@ -204,10 +204,11 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor return; } - memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize); + memcpy(imageData, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; if (offset > 0) { - memcpy(file->mutableData() + ktx::KTX_HEADER_SIZE + ktx::KV_SIZE_WIDTH + offset, (void*)&_minMipLevelAvailable, 1); + auto minMipKeyData = file->mutableData() + ktx::KTX_HEADER_SIZE + ktx::KV_SIZE_WIDTH + offset; + memcpy(minMipKeyData, (void*)&_minMipLevelAvailable, 1); } } } From f509403b25c3169966918fe2b93ae2dec3a08c96 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 16:15:19 -0700 Subject: [PATCH 105/119] Update prioritization of memory pressure states --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index dc4b828fbb..5bd5ac8db1 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -458,12 +458,12 @@ void GLVariableAllocationSupport::updateMemoryPressure() { float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; auto newState = MemoryPressureState::Idle; - if (hasTransfers) { - newState = MemoryPressureState::Transfer; + if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) { + newState = MemoryPressureState::Undersubscribed; } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) { - newState = MemoryPressureState::Undersubscribed; + } else if (hasTransfers) { + newState = MemoryPressureState::Transfer; } if (newState != _memoryPressureState) { @@ -539,6 +539,7 @@ void GLVariableAllocationSupport::processWorkQueues() { } if (workQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; _memoryPressureStateStale = true; } } From 4395cd7ee18f42d1bbc106f5fd925a24f5fa9ad5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 16:30:17 -0700 Subject: [PATCH 106/119] Update hasPendingTransfers to not touch _pendingTransfers --- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 3e944ed70d..cd7b30b961 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -114,7 +114,7 @@ protected: //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } bool canPromote() const { return _allocatedMip > _minAllocatedMip; } bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return _pendingTransfers.size() > 0; } + bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } void executeNextTransfer(const TexturePointer& currentTexture); virtual void populateTransferQueue() = 0; virtual void promote() = 0; From 1f9e07b828cb11f3b4e76d5109a81a0f7cd59aed Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Apr 2017 16:40:13 -0700 Subject: [PATCH 107/119] more logging --- assignment-client/src/avatars/AvatarMixer.cpp | 10 ++++------ assignment-client/src/avatars/AvatarMixerSlave.cpp | 3 +++ libraries/avatars/src/AvatarData.cpp | 7 ++++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 540a081fd8..7ecb4db62f 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -72,14 +72,12 @@ AvatarMixer::~AvatarMixer() { void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { qDebug() << __FUNCTION__ << "about to call nodeData->getAvatar().identityByteArray()... for node:" << nodeData->getNodeID() << "destinationNode:" << destinationNode->getUUID(); QByteArray individualData = nodeData->getAvatar().identityByteArray(); - - auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); - individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); + qDebug() << __FUNCTION__ << "REPLACED " << NUM_BYTES_RFC4122_UUID << "bytes in identityByteArray with:" << nodeData->getNodeID() << ".toRfc4122()..."; - identityPacket->write(individualData); - - DependencyManager::get()->sendPacket(std::move(identityPacket), *destinationNode); + auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); + identityPackets->write(individualData); + DependencyManager::get()->sendPacketList(std::move(identityPackets), *destinationNode); ++_sumIdentityPackets; } diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index c3ff2a74dc..1f0e781b83 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -70,9 +70,12 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, qDebug() << __FUNCTION__ << "about to call nodeData->getConstAvatarData().identityByteArray()... for node:" << nodeData->getNodeID() << "destinationNode:" << destinationNode->getUUID(); QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(); individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious + qDebug() << __FUNCTION__ << "REPLACED " << NUM_BYTES_RFC4122_UUID << "bytes in identityByteArray with:" << nodeData->getNodeID() << ".toRfc4122()..."; + auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); identityPackets->write(individualData); DependencyManager::get()->sendPacketList(std::move(identityPackets), *destinationNode); + _stats.numIdentityPackets++; return individualData.size(); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a9ca366d05..de2cd3a7c3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1737,7 +1737,12 @@ void AvatarData::sendIdentityPacket() { nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { if (node->getType() == NodeType::AvatarMixer) { - qDebug() << "AvatarData::sendIdentityPacket()... found mixer node, node->getActiveSocket():" << node->getActiveSocket(); + qDebug() << "AvatarData::sendIdentityPacket()... found mixer node, node->getActiveSocket():" << node->getActiveSocket() + << "getPublicSocket()" << node->getPublicSocket() + << "getLocalSocket()" << node->getLocalSocket() + << "getSymmetricSocket()" << node->getSymmetricSocket() + << "getActiveSocket()" << *node->getActiveSocket(); + } return node->getType() == NodeType::AvatarMixer && node->getActiveSocket(); From a880ca126291fb4bd8b6007f96273b2c5595c815 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 26 Apr 2017 16:51:26 -0700 Subject: [PATCH 108/119] Reduce log spam --- scripts/tutorials/halfDuplex.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/scripts/tutorials/halfDuplex.js b/scripts/tutorials/halfDuplex.js index fe608eedec..e1ed132233 100644 --- a/scripts/tutorials/halfDuplex.js +++ b/scripts/tutorials/halfDuplex.js @@ -34,13 +34,15 @@ Script.update.connect(function () { var avatars = AvatarList.getAvatarIdentifiers(); avatars.forEach(function (id) { var avatar = AvatarList.getAvatar(id); - if ((MyAvatar.sessionUUID !== avatar.sessionUUID) && (avatar.displayName.indexOf(MICROPHONE_DISPLAY_NAME) !== 0)) { - othersLoudness += Math.round(avatar.audioLoudness); - } - // Mute other microphone avatars to not feedback with muti-source environment - if (avatar.displayName.indexOf(MICROPHONE_DISPLAY_NAME) === 0) { - if (!Users.getPersonalMuteStatus(avatar.sessionUUID)) { - Users.personalMute(avatar.sessionUUID, true); + if (MyAvatar.sessionUUID !== avatar.sessionUUID) { + if (avatar.displayName.indexOf(MICROPHONE_DISPLAY_NAME) !== 0) { + othersLoudness += Math.round(avatar.audioLoudness); + } + // Mute other microphone avatars to not feedback with muti-source environment + if (avatar.displayName.indexOf(MICROPHONE_DISPLAY_NAME) === 0) { + if (!Users.getPersonalMuteStatus(avatar.sessionUUID)) { + Users.personalMute(avatar.sessionUUID, true); + } } } }); From 47cf44dc60819842bd86816d989891490cf0e4a7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 17:11:19 -0700 Subject: [PATCH 109/119] Store offset to min mip kv in KtxStorage --- libraries/gpu/src/gpu/Texture.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 17 ++++++++--------- libraries/ktx/src/ktx/KTX.cpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 825e9237f5..502ad33143 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -330,6 +330,7 @@ public: std::string _filename; std::atomic _minMipLevelAvailable; + size_t _offsetToMinMipKV; ktx::KTXDescriptorPointer _ktxDescriptor; friend class Texture; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index e3c4b325fb..8181bb21ae 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -109,18 +109,18 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { if (_ktxDescriptor->images.size() < _ktxDescriptor->header.numberOfMipmapLevels) { qWarning() << "Bad images found in ktx"; } - auto& keyValues = _ktxDescriptor->keyValues; - auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { - return val._key.compare(ktx::HIFI_MIN_POPULATED_MIP_KEY) == 0; - }); - if (found != keyValues.end()) { - _minMipLevelAvailable = found->_value[0]; + + _offsetToMinMipKV = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); + if (_offsetToMinMipKV) { + auto data = storage->data() + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV; + _minMipLevelAvailable = *data; } else { // Assume all mip levels are available _minMipLevelAvailable = 0; } } + // now that we know the ktx, let's get the header info to configure this Texture::Storage: Format mipFormat = Format::COLOR_BGRA_32; Format texelFormat = Format::COLOR_SRGBA_32; @@ -195,7 +195,6 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor imageData += ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData + _ktxDescriptor->images[level]._imageOffset; imageData += ktx::IMAGE_SIZE_WIDTH; - auto offset = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); { std::lock_guard lock { _cacheFileWriteMutex }; @@ -206,8 +205,8 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor memcpy(imageData, storage->data(), _ktxDescriptor->images[level]._imageSize); _minMipLevelAvailable = level; - if (offset > 0) { - auto minMipKeyData = file->mutableData() + ktx::KTX_HEADER_SIZE + ktx::KV_SIZE_WIDTH + offset; + if (_offsetToMinMipKV > 0) { + auto minMipKeyData = file->mutableData() + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV; memcpy(minMipKeyData, (void*)&_minMipLevelAvailable, 1); } } diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index 0dbc2e720f..cea397927a 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -126,7 +126,7 @@ size_t KTXDescriptor::getValueOffsetForKey(const std::string& key) const { size_t offset { 0 }; for (auto& kv : keyValues) { if (kv._key == key) { - return offset + kv._key.size() + 1; + return offset + ktx::KV_SIZE_WIDTH + kv._key.size() + 1; } offset += kv.serializedByteSize(); } From 960c29edb750b1edb7f41b0ac41e5e63fbc8eaeb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 21 Apr 2017 09:46:04 -0700 Subject: [PATCH 110/119] Fix excessive copying when querying the size of a KTX backed texture --- libraries/gpu/src/gpu/Texture.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index ebde9d4d27..4d66d71567 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -487,12 +487,11 @@ void Texture::setAutoGenerateMips(bool enable) { } Size Texture::getStoredMipSize(uint16 level) const { - PixelsPointer mipFace = accessStoredMipFace(level); Size size = 0; - if (mipFace && mipFace->getSize()) { - for (int face = 0; face < getNumFaces(); face++) { + for (int face = 0; face < getNumFaces(); face++) { + if (isStoredMipFaceAvailable(level, face)) { size += getStoredMipFaceSize(level, face); - } + } } return size; } From fa0fb113293d9847da10089727504f9186c99e10 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 25 Apr 2017 11:55:48 -0700 Subject: [PATCH 111/119] Remove uneeded memory to memory copy for mip transfers --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 42 ++++++++++------------- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 7 ++-- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index a6e6bf4fa3..27a31ca678 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -140,7 +140,7 @@ GLExternalTexture::~GLExternalTexture() { if (recycler) { backend->releaseExternalTexture(_id, recycler); } else { - qWarning() << "No recycler available for texture " << _id << " possible leak"; + qCWarning(gpugllogging) << "No recycler available for texture " << _id << " possible leak"; } const_cast(_id) = 0; } @@ -210,38 +210,32 @@ TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t t format = texelFormat.format; internalFormat = texelFormat.internalFormat; type = texelFormat.type; - auto mipSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face); + _transferSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face); - - if (0 == lines) { - _transferSize = mipSize; - _bufferingLambda = [=] { - auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - _buffer.resize(_transferSize); - memcpy(&_buffer[0], mipData->readData(), _transferSize); - _bufferingCompleted = true; - }; - - } else { + // If we're copying a subsection of the mip, do additional calculations to find the size and offset of the segment + if (0 != lines) { transferDimensions.y = lines; auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - auto bytesPerLine = (uint32_t)mipSize / dimensions.y; - auto sourceOffset = bytesPerLine * lineOffset; + auto bytesPerLine = (uint32_t)_transferSize / dimensions.y; + _transferOffset = bytesPerLine * lineOffset; _transferSize = bytesPerLine * lines; - _bufferingLambda = [=] { - auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - _buffer.resize(_transferSize); - memcpy(&_buffer[0], mipData->readData() + sourceOffset, _transferSize); - _bufferingCompleted = true; - }; } Backend::updateTextureTransferPendingSize(0, _transferSize); + if (_transferSize > GLVariableAllocationSupport::MAX_TRANSFER_SIZE) { + qCWarning(gpugllogging) << "Transfer size of " << _transferSize << " exceeds theoretical maximum transfer size"; + } + + // Buffering can invoke disk IO, so it should be off of the main and render threads + _bufferingLambda = [=] { + _mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face)->createView(_transferSize, _transferOffset); + _bufferingCompleted = true; + }; + _transferLambda = [=] { - _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _buffer.size(), _buffer.data()); - std::vector emptyVector; - _buffer.swap(emptyVector); + _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _mipData->size(), _mipData->readData()); + _mipData.reset(); }; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 8b4b545b7d..b21ff53dd8 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -49,8 +49,10 @@ public: using VoidLambdaQueue = std::queue; using ThreadPointer = std::shared_ptr; const GLTexture& _parent; - // Holds the contents to transfer to the GPU in CPU memory - std::vector _buffer; + Texture::PixelsPointer _mipData; + size_t _transferOffset { 0 }; + size_t _transferSize { 0 }; + // Indicates if a transfer from backing storage to interal storage has started bool _bufferingStarted { false }; bool _bufferingCompleted { false }; @@ -78,7 +80,6 @@ public: #endif private: - size_t _transferSize { 0 }; #if THREADED_TEXTURE_BUFFERING void startBuffering(); #endif From cb299695f461a3ca29dee4858bea330f2607531e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 17:52:53 -0700 Subject: [PATCH 112/119] Update imageOffset to size_t --- libraries/gpu/src/gpu/Texture_ktx.cpp | 1 - libraries/ktx/src/ktx/KTX.cpp | 4 ++-- libraries/ktx/src/ktx/KTX.h | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 8181bb21ae..efff6c7afe 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -97,7 +97,6 @@ struct GPUKTXPayload { return false; } }; -const std::string gpu::SOURCE_HASH_KEY { "hifi.sourceHash" }; const std::string GPUKTXPayload::KEY { "hifi.gpu" }; KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { diff --git a/libraries/ktx/src/ktx/KTX.cpp b/libraries/ktx/src/ktx/KTX.cpp index cea397927a..38bb91e5c2 100644 --- a/libraries/ktx/src/ktx/KTX.cpp +++ b/libraries/ktx/src/ktx/KTX.cpp @@ -136,7 +136,7 @@ size_t KTXDescriptor::getValueOffsetForKey(const std::string& key) const { ImageDescriptors Header::generateImageDescriptors() const { ImageDescriptors descriptors; - uint32_t imageOffset = 0; + size_t imageOffset = 0; for (uint32_t level = 0; level < numberOfMipmapLevels; ++level) { auto imageSize = static_cast(evalImageSize(level)); if (imageSize == 0) { @@ -149,7 +149,7 @@ ImageDescriptors Header::generateImageDescriptors() const { 0 }; - imageOffset += (imageSize * numberOfFaces) + 4; + imageOffset += (imageSize * numberOfFaces) + ktx::IMAGE_SIZE_WIDTH; ImageHeader::FaceOffsets offsets; // TODO Add correct face offsets diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 5a3167cf53..42ff4644ca 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -422,11 +422,11 @@ namespace ktx { // This is the byte offset from the _start_ of the image region. For example, level 0 // will have a byte offset of 0. const uint32_t _numFaces; - const uint32_t _imageOffset; + const size_t _imageOffset; const uint32_t _imageSize; const uint32_t _faceSize; const uint32_t _padding; - ImageHeader(bool cube, uint32_t imageOffset, uint32_t imageSize, uint32_t padding) : + ImageHeader(bool cube, size_t imageOffset, uint32_t imageSize, uint32_t padding) : _numFaces(cube ? NUM_CUBEMAPFACES : 1), _imageOffset(imageOffset), _imageSize(imageSize * _numFaces), @@ -448,10 +448,10 @@ namespace ktx { struct Image : public ImageHeader { FaceBytes _faceBytes; Image(const ImageHeader& header, const FaceBytes& faces) : ImageHeader(header), _faceBytes(faces) {} - Image(uint32_t imageOffset, uint32_t imageSize, uint32_t padding, const Byte* bytes) : + Image(size_t imageOffset, uint32_t imageSize, uint32_t padding, const Byte* bytes) : ImageHeader(false, imageOffset, imageSize, padding), _faceBytes(1, bytes) {} - Image(uint32_t imageOffset, uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : + Image(size_t imageOffset, uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) : ImageHeader(true, imageOffset, pageSize, padding) { if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) { From fee36453a3d699fd29b70182ba7873eb98ccfd8b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 17:53:13 -0700 Subject: [PATCH 113/119] Move hifi.minMip --- libraries/gpu/src/gpu/Texture.h | 4 +++- libraries/ktx/src/ktx/KTX.h | 5 ++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 502ad33143..9b23b4e695 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -34,7 +34,9 @@ namespace ktx { namespace gpu { -extern const std::string SOURCE_HASH_KEY; + +const std::string SOURCE_HASH_KEY { "hifi.sourceHash" }; + const uint8 SOURCE_HASH_BYTES = 16; // THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index 42ff4644ca..e8fa019a07 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -70,10 +70,9 @@ end namespace ktx { - const std::string HIFI_MIN_POPULATED_MIP_KEY = "hifi.minMip"; - - const uint32_t PACKING_SIZE { sizeof(uint32_t) }; + const std::string HIFI_MIN_POPULATED_MIP_KEY{ "hifi.minMip" }; + using Byte = uint8_t; enum class GLType : uint32_t { From f5b7feccde699e0986ac3dfc0577b601e1683dcc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 18:01:56 -0700 Subject: [PATCH 114/119] Update AssetClient/Request to use const byteRange --- libraries/networking/src/AssetClient.cpp | 2 +- libraries/networking/src/AssetClient.h | 2 +- libraries/networking/src/AssetRequest.cpp | 2 +- libraries/networking/src/AssetRequest.h | 4 ++-- libraries/networking/src/ByteRange.h | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 48f8bb87f9..15e0b8c9b5 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -181,7 +181,7 @@ RenameMappingRequest* AssetClient::createRenameMappingRequest(const AssetPath& o return request; } -AssetRequest* AssetClient::createRequest(const AssetHash& hash, ByteRange byteRange) { +AssetRequest* AssetClient::createRequest(const AssetHash& hash, const ByteRange& byteRange) { auto request = new AssetRequest(hash, byteRange); // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index b204fab47e..6f9cc3cd31 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -56,7 +56,7 @@ public: Q_INVOKABLE DeleteMappingsRequest* createDeleteMappingsRequest(const AssetPathList& paths); Q_INVOKABLE SetMappingRequest* createSetMappingRequest(const AssetPath& path, const AssetHash& hash); Q_INVOKABLE RenameMappingRequest* createRenameMappingRequest(const AssetPath& oldPath, const AssetPath& newPath); - Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash, ByteRange byteRange = ByteRange()); + Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash, const ByteRange& byteRange = ByteRange()); Q_INVOKABLE AssetUpload* createUpload(const QString& filename); Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data); diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index e54a058ac2..341c3b45da 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -23,7 +23,7 @@ static int requestID = 0; -AssetRequest::AssetRequest(const QString& hash, ByteRange byteRange) : +AssetRequest::AssetRequest(const QString& hash, const ByteRange& byteRange) : _requestID(++requestID), _hash(hash), _byteRange(byteRange) diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index e617d75157..b808ae0ca6 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -41,7 +41,7 @@ public: UnknownError }; - AssetRequest(const QString& hash, ByteRange byteRange = ByteRange()); + AssetRequest(const QString& hash, const ByteRange& byteRange = ByteRange()); virtual ~AssetRequest() override; Q_INVOKABLE void start(); @@ -65,7 +65,7 @@ private: QByteArray _data; int _numPendingRequests { 0 }; MessageID _assetRequestID { INVALID_MESSAGE_ID }; - ByteRange _byteRange; + const ByteRange _byteRange; }; #endif diff --git a/libraries/networking/src/ByteRange.h b/libraries/networking/src/ByteRange.h index 77932a8c28..6fd3559154 100644 --- a/libraries/networking/src/ByteRange.h +++ b/libraries/networking/src/ByteRange.h @@ -16,8 +16,8 @@ struct ByteRange { int64_t fromInclusive { 0 }; int64_t toExclusive { 0 }; - bool isSet() { return fromInclusive < 0 || fromInclusive < toExclusive; } - int64_t size() { return toExclusive - fromInclusive; } + bool isSet() const { return fromInclusive < 0 || fromInclusive < toExclusive; } + int64_t size() const { return toExclusive - fromInclusive; } // byte ranges are invalid if: // (1) the toExclusive of the range is negative From a26dca73b1b7dbe7b1b6770259db067e27190e9b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Apr 2017 18:02:09 -0700 Subject: [PATCH 115/119] more logging --- libraries/networking/src/LimitedNodeList.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 8feb695c79..fa1842db4c 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -437,6 +437,8 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, const Node& destinationNode) { auto activeSocket = destinationNode.getActiveSocket(); if (activeSocket) { + qDebug() << __FUNCTION__ << "activeSocket:" << *activeSocket; + // close the last packet in the list packetList->closeCurrentPacket(); From 60ba874e2e423301d0116019842282d6322ac54a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 26 Apr 2017 18:02:23 -0700 Subject: [PATCH 116/119] Fix implementation of mutableData in non-File Storage classes --- libraries/shared/src/shared/Storage.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 3983387c15..da5b773d52 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -43,7 +43,7 @@ namespace storage { MemoryStorage(size_t size, const uint8_t* data = nullptr); const uint8_t* data() const override { return _data.data(); } uint8_t* data() { return _data.data(); } - uint8_t* mutableData() override { return 0; } + uint8_t* mutableData() override { return _data.data(); } size_t size() const override { return _data.size(); } operator bool() const override { return true; } private: @@ -73,7 +73,7 @@ namespace storage { public: ViewStorage(const storage::StoragePointer& owner, size_t size, const uint8_t* data); const uint8_t* data() const override { return _data; } - uint8_t* mutableData() override { return 0; } + uint8_t* mutableData() override { throw std::runtime_error("Cannot modify ViewStorage"); } size_t size() const override { return _size; } operator bool() const override { return *_owner; } private: From 834a447d62bda1fa403468421b0f021744f791b4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Apr 2017 18:36:44 -0700 Subject: [PATCH 117/119] remove debugging code --- assignment-client/src/Agent.cpp | 1 - assignment-client/src/avatars/AvatarMixer.cpp | 10 ------- .../src/avatars/AvatarMixerSlave.cpp | 9 ------- interface/src/Application.cpp | 1 - interface/src/avatar/MyAvatar.cpp | 1 - libraries/avatars/src/AvatarData.cpp | 26 ------------------- libraries/networking/src/LimitedNodeList.cpp | 2 -- libraries/networking/src/udt/Socket.h | 2 +- 8 files changed, 1 insertion(+), 51 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 3cfccbea9e..c8ab489311 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -583,7 +583,6 @@ void Agent::setIsAvatar(bool isAvatar) { void Agent::sendAvatarIdentityPacket() { if (_isAvatar) { auto scriptedAvatar = DependencyManager::get(); - qDebug() << __FUNCTION__ << "about to call sendIdentityPacket()"; scriptedAvatar->sendIdentityPacket(); } } diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 7ecb4db62f..a54c418cc3 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -70,15 +70,11 @@ AvatarMixer::~AvatarMixer() { } void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { - qDebug() << __FUNCTION__ << "about to call nodeData->getAvatar().identityByteArray()... for node:" << nodeData->getNodeID() << "destinationNode:" << destinationNode->getUUID(); QByteArray individualData = nodeData->getAvatar().identityByteArray(); individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); - qDebug() << __FUNCTION__ << "REPLACED " << NUM_BYTES_RFC4122_UUID << "bytes in identityByteArray with:" << nodeData->getNodeID() << ".toRfc4122()..."; - auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); identityPackets->write(individualData); DependencyManager::get()->sendPacketList(std::move(identityPackets), *destinationNode); - ++_sumIdentityPackets; } @@ -394,7 +390,6 @@ void AvatarMixer::handleRequestsDomainListDataPacket(QSharedPointer message, SharedNodePointer senderNode) { - qDebug() << __FUNCTION__; auto start = usecTimestampNow(); auto nodeList = DependencyManager::get(); getOrCreateClientData(senderNode); @@ -406,14 +401,9 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes // parse the identity packet and update the change timestamp if appropriate AvatarData::Identity identity; - - qCDebug(avatars) << __FUNCTION__ << "about to call parseAvatarIdentityPacket() for packet from node:" << nodeData->getNodeID(); AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); - bool identityChanged = false; bool displayNameChanged = false; - - qCDebug(avatars) << __FUNCTION__ << "about to call processAvatarIdentity() node:" << nodeData->getNodeID(); avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 1f0e781b83..2ad8bb58ed 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -67,15 +67,11 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) { int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { - qDebug() << __FUNCTION__ << "about to call nodeData->getConstAvatarData().identityByteArray()... for node:" << nodeData->getNodeID() << "destinationNode:" << destinationNode->getUUID(); QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(); individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious - qDebug() << __FUNCTION__ << "REPLACED " << NUM_BYTES_RFC4122_UUID << "bytes in identityByteArray with:" << nodeData->getNodeID() << ".toRfc4122()..."; - auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); identityPackets->write(individualData); DependencyManager::get()->sendPacketList(std::move(identityPackets), *destinationNode); - _stats.numIdentityPackets++; return individualData.size(); } @@ -306,11 +302,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO // the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A. if (nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) { - qCDebug(avatars) << __FUNCTION__ << "about to call sendIdentityPacket()" - << "other node:" << otherNode->getUUID() - << "destination node:" << nodeData->getNodeID() - << "nodeData->getLastBroadcastTime(otherNode->getUUID()):" << nodeData->getLastBroadcastTime(otherNode->getUUID()) - << "otherNodeData->getIdentityChangeTimestamp():" << otherNodeData->getIdentityChangeTimestamp(); identityBytesSent += sendIdentityPacket(otherNodeData, node); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4801f39272..8cbcb668b0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5244,7 +5244,6 @@ void Application::nodeActivated(SharedNodePointer node) { if (node->getType() == NodeType::AvatarMixer) { // new avatar mixer, send off our identity packet right away - qDebug() << __FUNCTION__ << "about to call... getMyAvatar()->sendIdentityPacket();"; getMyAvatar()->markIdentityDataChanged(); getMyAvatar()->sendIdentityPacket(); getMyAvatar()->resetLastSent(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 59b262a308..7bc961c654 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -413,7 +413,6 @@ void MyAvatar::update(float deltaTime) { Q_ARG(glm::vec3, (halfBoundingBoxDimensions*2.0f))); if (getIdentityDataChanged()) { - qDebug() << __FUNCTION__ << "about to call... sendIdentityPacket();"; sendIdentityPacket(); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index de2cd3a7c3..3a6da8bcee 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1489,9 +1489,6 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC return; } - qCDebug(avatars) << __FUNCTION__ << "got identity packet for avatar " << getSessionUUID() - << "identity.updatedAt:" << identity.updatedAt << "_identityUpdatedAt:" << _identityUpdatedAt; - if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { setSkeletonModelURL(identity.skeletonModelURL); identityChanged = true; @@ -1525,7 +1522,6 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC // use the timestamp from this identity, since we want to honor the updated times in "server clock" // this will overwrite any changes we made locally to this AvatarData's _identityUpdatedAt _identityUpdatedAt = identity.updatedAt; - qDebug() << __FUNCTION__ << "_identityUpdatedAt:" << _identityUpdatedAt; } QByteArray AvatarData::identityByteArray() const { @@ -1534,15 +1530,6 @@ QByteArray AvatarData::identityByteArray() const { const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL _avatarEntitiesLock.withReadLock([&] { - - qDebug() << "AvatarData::identityByteArray() ... " - << "session:" << getSessionUUID() - << "url:" << urlToSend - << "displayName:" << _displayName - << "sessionDisplayName:" << getSessionDisplayNameForTransport() - << "identityUpdatedAt:" << _identityUpdatedAt; - - identityStream << getSessionUUID() << urlToSend << _attachmentData @@ -1726,29 +1713,16 @@ void AvatarData::sendAvatarDataPacket() { } void AvatarData::sendIdentityPacket() { - qDebug() << __FUNCTION__; auto nodeList = DependencyManager::get(); - - qDebug() << __FUNCTION__ << "about to call identityByteArray()... for getSessionUUID:" << getSessionUUID(); QByteArray identityData = identityByteArray(); auto packetList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); packetList->write(identityData); nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { - if (node->getType() == NodeType::AvatarMixer) { - qDebug() << "AvatarData::sendIdentityPacket()... found mixer node, node->getActiveSocket():" << node->getActiveSocket() - << "getPublicSocket()" << node->getPublicSocket() - << "getLocalSocket()" << node->getLocalSocket() - << "getSymmetricSocket()" << node->getSymmetricSocket() - << "getActiveSocket()" << *node->getActiveSocket(); - - } - return node->getType() == NodeType::AvatarMixer && node->getActiveSocket(); }, [&](const SharedNodePointer& node) { - qDebug() << "AvatarData::sendIdentityPacket()... about to call.... nodeList->sendPacketList(std::move(packetList), *node);"; nodeList->sendPacketList(std::move(packetList), *node); }); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index fa1842db4c..8feb695c79 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -437,8 +437,6 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, const Node& destinationNode) { auto activeSocket = destinationNode.getActiveSocket(); if (activeSocket) { - qDebug() << __FUNCTION__ << "activeSocket:" << *activeSocket; - // close the last packet in the list packetList->closeCurrentPacket(); diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h index df03178630..1919e00b41 100644 --- a/libraries/networking/src/udt/Socket.h +++ b/libraries/networking/src/udt/Socket.h @@ -26,7 +26,7 @@ #include "TCPVegasCC.h" #include "Connection.h" -#define UDT_CONNECTION_DEBUG +//#define UDT_CONNECTION_DEBUG class UDTTest; From 76902b15d3f3f9cd2087f0b1a7035bbd18bb6651 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Apr 2017 18:51:41 -0700 Subject: [PATCH 118/119] remove one last debug log --- assignment-client/src/avatars/AvatarMixer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index a54c418cc3..05dbfee912 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -210,8 +210,6 @@ void AvatarMixer::manageDisplayName(const SharedNodePointer& node) { soFar.second++; // refcount nodeData->flagIdentityChange(); nodeData->setAvatarSessionDisplayNameMustChange(false); - - qCDebug(avatars) << __FUNCTION__ << "about to call sendIdentityPacket()"; sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name. qCDebug(avatars) << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID(); } From bc2f89329467898760977377ca9fd6ca241842db Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Apr 2017 21:47:54 -0700 Subject: [PATCH 119/119] format and spelling changes --- libraries/avatars/src/AvatarData.cpp | 24 +++++++++++----------- libraries/networking/src/ResourceCache.cpp | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index ad65ba4410..1427ce6359 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1474,12 +1474,12 @@ void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& ide QDataStream packetStream(data); packetStream >> identityOut.uuid - >> identityOut.skeletonModelURL - >> identityOut.attachmentData - >> identityOut.displayName - >> identityOut.sessionDisplayName - >> identityOut.avatarEntityData - >> identityOut.updatedAt; + >> identityOut.skeletonModelURL + >> identityOut.attachmentData + >> identityOut.displayName + >> identityOut.sessionDisplayName + >> identityOut.avatarEntityData + >> identityOut.updatedAt; #ifdef WANT_DEBUG qCDebug(avatars) << __FUNCTION__ @@ -1547,12 +1547,12 @@ QByteArray AvatarData::identityByteArray() const { _avatarEntitiesLock.withReadLock([&] { identityStream << getSessionUUID() - << urlToSend - << _attachmentData - << _displayName - << getSessionDisplayNameForTransport() // depends on _sessionDisplayName - << _avatarEntityData - << _identityUpdatedAt; + << urlToSend + << _attachmentData + << _displayName + << getSessionDisplayNameForTransport() // depends on _sessionDisplayName + << _avatarEntityData + << _identityUpdatedAt; }); return identityData; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index f9694f1cd9..8d4edab2d5 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -747,7 +747,7 @@ bool Resource::handleFailedRequest(ResourceRequest::Result result) { _attempts++; _attemptsRemaining--; - qCDebug(networking) << "Retriable error while loading" << _url << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining; + qCDebug(networking) << "Retryable error while loading" << _url << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining; // retry with increasing delays const int BASE_DELAY_MS = 1000; @@ -765,7 +765,7 @@ bool Resource::handleFailedRequest(ResourceRequest::Result result) { _attemptsRemaining = 0; qCDebug(networking) << "Error loading " << _url << "attempt:" << _attempts << "attemptsRemaining:" << _attemptsRemaining; auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError - : QNetworkReply::UnknownNetworkError; + : QNetworkReply::UnknownNetworkError; emit failed(error); willRetry = false; finishedLoading(false);