From babe8f95bcedc0036f270c353fe9ee2000ec94d6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Sep 2016 13:34:05 -0700 Subject: [PATCH 01/59] tool for testing ice-server --- libraries/networking/src/LimitedNodeList.h | 4 ++-- tools/CMakeLists.txt | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index cd343a5232..1a3599e226 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -166,8 +166,8 @@ public: std::unique_ptr constructPingPacket(PingType_t pingType = PingType::Agnostic); std::unique_ptr constructPingReplyPacket(ReceivedMessage& message); - std::unique_ptr constructICEPingPacket(PingType_t pingType, const QUuid& iceID); - std::unique_ptr constructICEPingReplyPacket(ReceivedMessage& message, const QUuid& iceID); + static std::unique_ptr constructICEPingPacket(PingType_t pingType, const QUuid& iceID); + static std::unique_ptr constructICEPingReplyPacket(ReceivedMessage& message, const QUuid& iceID); void sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index bf645f25c2..a077efc335 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -7,3 +7,6 @@ set_target_properties(udt-test PROPERTIES FOLDER "Tools") add_subdirectory(vhacd-util) set_target_properties(vhacd-util PROPERTIES FOLDER "Tools") + +add_subdirectory(ice-client) +set_target_properties(ice-client PROPERTIES FOLDER "Tools") From cadbbd619496cedde6b0a4f88062631808aa870e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Sep 2016 13:34:32 -0700 Subject: [PATCH 02/59] tool for testing ice-server --- tools/ice-client/CMakeLists.txt | 3 ++ tools/ice-client/src/ICEClientApp.h | 63 +++++++++++++++++++++++++++++ tools/ice-client/src/main.cpp | 23 +++++++++++ 3 files changed, 89 insertions(+) create mode 100644 tools/ice-client/CMakeLists.txt create mode 100644 tools/ice-client/src/ICEClientApp.h create mode 100644 tools/ice-client/src/main.cpp diff --git a/tools/ice-client/CMakeLists.txt b/tools/ice-client/CMakeLists.txt new file mode 100644 index 0000000000..a80145974c --- /dev/null +++ b/tools/ice-client/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME ice-client) +setup_hifi_project(Core Widgets) +link_hifi_libraries(shared networking) diff --git a/tools/ice-client/src/ICEClientApp.h b/tools/ice-client/src/ICEClientApp.h new file mode 100644 index 0000000000..6728a0ed83 --- /dev/null +++ b/tools/ice-client/src/ICEClientApp.h @@ -0,0 +1,63 @@ +// +// ICEClient.h +// tools/ice-client/src +// +// Created by Seth Alves on 2016-9-16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#ifndef hifi_ICEClientApp_h +#define hifi_ICEClientApp_h + +#include +#include +#include +#include +#include + + +class ICEClientApp : public QCoreApplication { + Q_OBJECT +public: + ICEClientApp(int argc, char* argv[]); + ~ICEClientApp(); + +private: + void doSomething(); + void sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, + const QUuid& clientID, const QUuid& peerID); + void icePingDomainServer(); + void processPacket(std::unique_ptr packet); + + bool _verbose; + + unsigned int _actionCount { 0 }; + unsigned int _actionMax { 0 }; + + QUuid _sessionUUID; + + QTimer* _pingDomainTimer { nullptr }; + + HifiSockAddr _iceServerAddr; + + HifiSockAddr _localSockAddr; + HifiSockAddr _publicSockAddr; + udt::Socket* _socket { nullptr }; + + bool _domainServerPeerSet { false }; + NetworkPeer _domainServerPeer; + + // 0 -- time to talk to ice server + // 1 -- waiting for ICEPingReply + // 2 -- pause + // 3 -- pause + int _state { 0 }; +}; + + + +#endif //hifi_ICEClientApp_h diff --git a/tools/ice-client/src/main.cpp b/tools/ice-client/src/main.cpp new file mode 100644 index 0000000000..c70a7eb7d7 --- /dev/null +++ b/tools/ice-client/src/main.cpp @@ -0,0 +1,23 @@ +// +// main.cpp +// tools/ice-client/src +// +// Created by Seth Alves on 2016-9-16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +#include +#include +#include +#include + +#include "ICEClientApp.h" + +using namespace std; + +int main(int argc, char * argv[]) { + ICEClientApp app(argc, argv); + return app.exec(); +} From 6231fc4ba1da32d856e01217ed80f6a14735cd07 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Sep 2016 13:34:40 -0700 Subject: [PATCH 03/59] tool for testing ice-server --- tools/ice-client/src/ICEClientApp.cpp | 195 ++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 tools/ice-client/src/ICEClientApp.cpp diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp new file mode 100644 index 0000000000..f1629bf5e7 --- /dev/null +++ b/tools/ice-client/src/ICEClientApp.cpp @@ -0,0 +1,195 @@ +// +// ICEClient.h +// tools/ice-client/src +// +// Created by Seth Alves on 3/5/15. +// Copyright 2015 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 +// + +#include +#include +#include +#include + +#include "ICEClientApp.h" + +ICEClientApp::ICEClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv) { + // parse command-line + QCommandLineParser parser; + parser.setApplicationDescription("High Fidelity ICE client"); + parser.addHelpOption(); + + const QCommandLineOption helpOption = parser.addHelpOption(); + + const QCommandLineOption verboseOutput("v", "verbose output"); + parser.addOption(verboseOutput); + + const QCommandLineOption iceServerAddressOption("i", "ice-server address", "IP:PORT or HOSTNAME:PORT"); + parser.addOption(iceServerAddressOption); + + const QCommandLineOption howManyTimesOption("n", "how many times to cycle", "0"); + parser.addOption(howManyTimesOption); + + if (!parser.parse(QCoreApplication::arguments())) { + qCritical() << parser.errorText() << endl; + parser.showHelp(); + Q_UNREACHABLE(); + } + + if (parser.isSet(helpOption)) { + parser.showHelp(); + Q_UNREACHABLE(); + } + + _verbose = parser.isSet(verboseOutput); + + if (parser.isSet(howManyTimesOption)) { + _actionMax = parser.value(howManyTimesOption).toInt(); + } + + _iceServerAddr = HifiSockAddr("127.0.0.1", ICE_SERVER_DEFAULT_PORT); + if (parser.isSet(iceServerAddressOption)) { + // parse the IP and port combination for this target + QString hostnamePortString = parser.value(iceServerAddressOption); + + QHostAddress address { hostnamePortString.left(hostnamePortString.indexOf(':')) }; + quint16 port { (quint16) hostnamePortString.mid(hostnamePortString.indexOf(':') + 1).toUInt() }; + + if (address.isNull() || port == 0) { + qCritical() << "Could not parse an IP address and port combination from" << hostnamePortString << "-" << + "The parsed IP was" << address.toString() << "and the parsed port was" << port; + + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + } else { + _iceServerAddr = HifiSockAddr(address, port); + } + } + + qDebug() << "ICE-server address is" << _iceServerAddr; + + _state = 0; + + QTimer* doTimer = new QTimer(this); + connect(doTimer, &QTimer::timeout, this, &ICEClientApp::doSomething); + doTimer->start(200); +} + +ICEClientApp::~ICEClientApp() { + delete _socket; +} + +void ICEClientApp::doSomething() { + if (_actionMax > 0 && _actionCount >= _actionMax) { + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + } + + if (_state == 0) { + _domainServerPeerSet = false; + unsigned int localPort = 0; + _socket = new udt::Socket(); + _socket->bind(QHostAddress::AnyIPv4, localPort); + _socket->setPacketHandler([this](std::unique_ptr packet) { processPacket(std::move(packet)); }); + + qDebug() << "local port is" << _socket->localPort(); + _localSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); + _publicSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); + + QUuid peerID = QUuid("75cd162a-53dc-4292-aaa5-1304ab1bb0f2"); + _sessionUUID = QUuid::createUuid(); + + sendPacketToIceServer(PacketType::ICEServerQuery, _iceServerAddr, _sessionUUID, peerID); + + _state = 1; + _actionCount++; + } else if (_state == 2) { + _state = 3; + } else if (_state == 3) { + qDebug() << ""; + _state = 0; + delete _socket; + _socket = nullptr; + } +} + +void ICEClientApp::sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, + const QUuid& clientID, const QUuid& peerID) { + std::unique_ptr icePacket = NLPacket::create(packetType); + + QDataStream iceDataStream(icePacket.get()); + iceDataStream << clientID << _publicSockAddr << _localSockAddr; + + if (packetType == PacketType::ICEServerQuery) { + assert(!peerID.isNull()); + + iceDataStream << peerID; + + qDebug() << "Sending packet to ICE server to request connection info for peer with ID" + << uuidStringWithoutCurlyBraces(peerID); + } + + // fillPacketHeader(packet, connectionSecret); + _socket->writePacket(*icePacket, _iceServerAddr); +} + +void ICEClientApp::icePingDomainServer() { + if (!_domainServerPeerSet) { + return; + } + + qDebug() << "ice-pinging domain-server"; + + auto localPingPacket = LimitedNodeList::constructICEPingPacket(PingType::Local, _sessionUUID); + _socket->writePacket(*localPingPacket, _domainServerPeer.getLocalSocket()); + + auto publicPingPacket = LimitedNodeList::constructICEPingPacket(PingType::Public, _sessionUUID); + _socket->writePacket(*publicPingPacket, _domainServerPeer.getPublicSocket()); +} + + +void ICEClientApp::processPacket(std::unique_ptr packet) { + auto nlPacket = NLPacket::fromBase(std::move(packet)); + + if (nlPacket->getPayloadSize() < NLPacket::localHeaderSize(PacketType::ICEServerHeartbeat)) { + qDebug() << "got a short packet."; + return; + } + + QSharedPointer message = QSharedPointer::create(*nlPacket); + const HifiSockAddr& senderAddr = message->getSenderSockAddr(); + + if (nlPacket->getType() == PacketType::ICEServerPeerInformation) { + QDataStream iceResponseStream(message->getMessage()); + iceResponseStream >> _domainServerPeer; + _domainServerPeerSet = true; + + icePingDomainServer(); + _pingDomainTimer = new QTimer(this); + connect(_pingDomainTimer, &QTimer::timeout, this, &ICEClientApp::icePingDomainServer); + _pingDomainTimer->start(1000); + + qDebug() << "got ICEServerPeerInformation from" << _domainServerPeer.getUUID(); + + } else if (nlPacket->getType() == PacketType::ICEPing) { + qDebug() << "got packet: " << nlPacket->getType(); + auto replyPacket = LimitedNodeList::constructICEPingReplyPacket(*message, _sessionUUID); + _socket->writePacket(*replyPacket, senderAddr); + + } else if (nlPacket->getType() == PacketType::ICEPingReply) { + qDebug() << "got packet: " << nlPacket->getType(); + if (_domainServerPeerSet && _state == 1 && + (senderAddr == _domainServerPeer.getLocalSocket() || + senderAddr == _domainServerPeer.getPublicSocket())) { + + delete _pingDomainTimer; + _pingDomainTimer = nullptr; + + _state = 2; + } + + } else { + qDebug() << "got unexpected packet: " << nlPacket->getType(); + } +} From d3dc0698a52f4885d72c12ef1d9808828bcd6e82 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Sep 2016 13:51:33 -0700 Subject: [PATCH 04/59] more command-line arguments --- tools/ice-client/src/ICEClientApp.cpp | 22 ++++++++++++++++++++-- tools/ice-client/src/ICEClientApp.h | 1 + 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp index f1629bf5e7..c1c2d047cc 100644 --- a/tools/ice-client/src/ICEClientApp.cpp +++ b/tools/ice-client/src/ICEClientApp.cpp @@ -33,6 +33,10 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv const QCommandLineOption howManyTimesOption("n", "how many times to cycle", "0"); parser.addOption(howManyTimesOption); + const QCommandLineOption domainIDOption("d", "domain-server uuid"); + parser.addOption(domainIDOption); + + if (!parser.parse(QCoreApplication::arguments())) { qCritical() << parser.errorText() << endl; parser.showHelp(); @@ -50,6 +54,11 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv _actionMax = parser.value(howManyTimesOption).toInt(); } + if (parser.isSet(domainIDOption)) { + _domainID = QUuid(parser.value(howManyTimesOption)); + } + + _iceServerAddr = HifiSockAddr("127.0.0.1", ICE_SERVER_DEFAULT_PORT); if (parser.isSet(iceServerAddressOption)) { // parse the IP and port combination for this target @@ -97,12 +106,21 @@ void ICEClientApp::doSomething() { _localSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); _publicSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); - QUuid peerID = QUuid("75cd162a-53dc-4292-aaa5-1304ab1bb0f2"); + // QUuid peerID = QUuid("75cd162a-53dc-4292-aaa5-1304ab1bb0f2"); + QUuid peerID; + if (_domainID == QUuid()) { + // pick a random domain-id + peerID = QUuid::createUuid(); + _state = 2; + } else { + // use the domain UUID given on the command-line + peerID = _domainID; + _state = 1; + } _sessionUUID = QUuid::createUuid(); sendPacketToIceServer(PacketType::ICEServerQuery, _iceServerAddr, _sessionUUID, peerID); - _state = 1; _actionCount++; } else if (_state == 2) { _state = 3; diff --git a/tools/ice-client/src/ICEClientApp.h b/tools/ice-client/src/ICEClientApp.h index 6728a0ed83..de40d07338 100644 --- a/tools/ice-client/src/ICEClientApp.h +++ b/tools/ice-client/src/ICEClientApp.h @@ -39,6 +39,7 @@ private: unsigned int _actionMax { 0 }; QUuid _sessionUUID; + QUuid _domainID; QTimer* _pingDomainTimer { nullptr }; From 793687bb08414bed94f311aee553dfdc3df249f5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Sep 2016 14:11:44 -0700 Subject: [PATCH 05/59] more command-line arguments --- tools/ice-client/src/ICEClientApp.cpp | 7 ++++--- tools/ice-client/src/ICEClientApp.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp index c1c2d047cc..f0000c78cf 100644 --- a/tools/ice-client/src/ICEClientApp.cpp +++ b/tools/ice-client/src/ICEClientApp.cpp @@ -1,5 +1,5 @@ // -// ICEClient.h +// ICEClientApp.cpp // tools/ice-client/src // // Created by Seth Alves on 3/5/15. @@ -33,7 +33,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv const QCommandLineOption howManyTimesOption("n", "how many times to cycle", "0"); parser.addOption(howManyTimesOption); - const QCommandLineOption domainIDOption("d", "domain-server uuid"); + const QCommandLineOption domainIDOption("d", "domain-server uuid", "00000000-0000-0000-0000-000000000000"); parser.addOption(domainIDOption); @@ -55,7 +55,8 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv } if (parser.isSet(domainIDOption)) { - _domainID = QUuid(parser.value(howManyTimesOption)); + _domainID = QUuid(parser.value(domainIDOption)); + qDebug() << "domain-server ID is" << _domainID; } diff --git a/tools/ice-client/src/ICEClientApp.h b/tools/ice-client/src/ICEClientApp.h index de40d07338..efdbc9e022 100644 --- a/tools/ice-client/src/ICEClientApp.h +++ b/tools/ice-client/src/ICEClientApp.h @@ -1,5 +1,5 @@ // -// ICEClient.h +// ICEClientApp.h // tools/ice-client/src // // Created by Seth Alves on 2016-9-16 From 2e0cc158de4500a7b16b304d44ca242ef0e4d02c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Sep 2016 17:21:28 -0700 Subject: [PATCH 06/59] ice test-client uses stun server to get public address --- libraries/networking/src/LimitedNodeList.cpp | 170 ++++++++++--------- libraries/networking/src/LimitedNodeList.h | 8 +- tools/ice-client/src/ICEClientApp.cpp | 106 +++++++++--- 3 files changed, 177 insertions(+), 107 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 5aa31efea4..ec4b2c3573 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -745,8 +745,32 @@ void LimitedNodeList::removeSilentNodes() { const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442; const int NUM_BYTES_STUN_HEADER = 20; -void LimitedNodeList::sendSTUNRequest() { +void LimitedNodeList::makeSTUNRequestPacket(char* stunRequestPacket) { + int packetIndex = 0; + + const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE); + + // leading zeros + message type + const uint16_t REQUEST_MESSAGE_TYPE = htons(0x0001); + memcpy(stunRequestPacket + packetIndex, &REQUEST_MESSAGE_TYPE, sizeof(REQUEST_MESSAGE_TYPE)); + packetIndex += sizeof(REQUEST_MESSAGE_TYPE); + + // message length (no additional attributes are included) + uint16_t messageLength = 0; + memcpy(stunRequestPacket + packetIndex, &messageLength, sizeof(messageLength)); + packetIndex += sizeof(messageLength); + + memcpy(stunRequestPacket + packetIndex, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)); + packetIndex += sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER); + + // transaction ID (random 12-byte unsigned integer) + const uint NUM_TRANSACTION_ID_BYTES = 12; + QUuid randomUUID = QUuid::createUuid(); + memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES); +} + +void LimitedNodeList::sendSTUNRequest() { if (!_stunSockAddr.getAddress().isNull()) { const int NUM_INITIAL_STUN_REQUESTS_BEFORE_FAIL = 10; @@ -762,36 +786,14 @@ void LimitedNodeList::sendSTUNRequest() { } char stunRequestPacket[NUM_BYTES_STUN_HEADER]; - - int packetIndex = 0; - - const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE); - - // leading zeros + message type - const uint16_t REQUEST_MESSAGE_TYPE = htons(0x0001); - memcpy(stunRequestPacket + packetIndex, &REQUEST_MESSAGE_TYPE, sizeof(REQUEST_MESSAGE_TYPE)); - packetIndex += sizeof(REQUEST_MESSAGE_TYPE); - - // message length (no additional attributes are included) - uint16_t messageLength = 0; - memcpy(stunRequestPacket + packetIndex, &messageLength, sizeof(messageLength)); - packetIndex += sizeof(messageLength); - - memcpy(stunRequestPacket + packetIndex, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)); - packetIndex += sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER); - - // transaction ID (random 12-byte unsigned integer) - const uint NUM_TRANSACTION_ID_BYTES = 12; - QUuid randomUUID = QUuid::createUuid(); - memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES); - + makeSTUNRequestPacket(stunRequestPacket); flagTimeForConnectionStep(ConnectionStep::SendSTUNRequest); - _nodeSocket.writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); } } -void LimitedNodeList::processSTUNResponse(std::unique_ptr packet) { +bool LimitedNodeList::parseSTUNResponse(udt::BasePacket* packet, + QHostAddress& newPublicAddress, uint16_t& newPublicPort) { // check the cookie to make sure this is actually a STUN response // and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS const int NUM_BYTES_MESSAGE_TYPE_AND_LENGTH = 4; @@ -803,71 +805,79 @@ void LimitedNodeList::processSTUNResponse(std::unique_ptr packe if (memcmp(packet->getData() + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, - sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)) == 0) { + sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)) != 0) { + return false; + } - // enumerate the attributes to find XOR_MAPPED_ADDRESS_TYPE - while (attributeStartIndex < packet->getDataSize()) { + // enumerate the attributes to find XOR_MAPPED_ADDRESS_TYPE + while (attributeStartIndex < packet->getDataSize()) { + if (memcmp(packet->getData() + attributeStartIndex, &XOR_MAPPED_ADDRESS_TYPE, sizeof(XOR_MAPPED_ADDRESS_TYPE)) == 0) { + const int NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH = 4; + const int NUM_BYTES_FAMILY_ALIGN = 1; + const uint8_t IPV4_FAMILY_NETWORK_ORDER = htons(0x01) >> 8; - if (memcmp(packet->getData() + attributeStartIndex, &XOR_MAPPED_ADDRESS_TYPE, sizeof(XOR_MAPPED_ADDRESS_TYPE)) == 0) { - const int NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH = 4; - const int NUM_BYTES_FAMILY_ALIGN = 1; - const uint8_t IPV4_FAMILY_NETWORK_ORDER = htons(0x01) >> 8; + int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN; - int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN; + uint8_t addressFamily = 0; + memcpy(&addressFamily, packet->getData() + byteIndex, sizeof(addressFamily)); - uint8_t addressFamily = 0; - memcpy(&addressFamily, packet->getData() + byteIndex, sizeof(addressFamily)); + byteIndex += sizeof(addressFamily); - byteIndex += sizeof(addressFamily); + if (addressFamily == IPV4_FAMILY_NETWORK_ORDER) { + // grab the X-Port + uint16_t xorMappedPort = 0; + memcpy(&xorMappedPort, packet->getData() + byteIndex, sizeof(xorMappedPort)); - if (addressFamily == IPV4_FAMILY_NETWORK_ORDER) { - // grab the X-Port - uint16_t xorMappedPort = 0; - memcpy(&xorMappedPort, packet->getData() + byteIndex, sizeof(xorMappedPort)); + newPublicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16); - uint16_t newPublicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16); + byteIndex += sizeof(xorMappedPort); - byteIndex += sizeof(xorMappedPort); + // grab the X-Address + uint32_t xorMappedAddress = 0; + memcpy(&xorMappedAddress, packet->getData() + byteIndex, sizeof(xorMappedAddress)); - // grab the X-Address - uint32_t xorMappedAddress = 0; - memcpy(&xorMappedAddress, packet->getData() + byteIndex, sizeof(xorMappedAddress)); + uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER); - uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER); - - QHostAddress newPublicAddress(stunAddress); - - if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) { - _publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort); - - qCDebug(networking, "New public socket received from STUN server is %s:%hu", - _publicSockAddr.getAddress().toString().toLocal8Bit().constData(), - _publicSockAddr.getPort()); - - if (!_hasCompletedInitialSTUN) { - // if we're here we have definitely completed our initial STUN sequence - stopInitialSTUNUpdate(true); - } - - emit publicSockAddrChanged(_publicSockAddr); - - flagTimeForConnectionStep(ConnectionStep::SetPublicSocketFromSTUN); - } - - // we're done reading the packet so we can return now - return; - } - } else { - // push forward attributeStartIndex by the length of this attribute - const int NUM_BYTES_ATTRIBUTE_TYPE = 2; - - uint16_t attributeLength = 0; - memcpy(&attributeLength, packet->getData() + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE, - sizeof(attributeLength)); - attributeLength = ntohs(attributeLength); - - attributeStartIndex += NUM_BYTES_MESSAGE_TYPE_AND_LENGTH + attributeLength; + // QHostAddress newPublicAddress(stunAddress); + newPublicAddress = QHostAddress(stunAddress); + return true; } + } else { + // push forward attributeStartIndex by the length of this attribute + const int NUM_BYTES_ATTRIBUTE_TYPE = 2; + + uint16_t attributeLength = 0; + memcpy(&attributeLength, packet->getData() + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE, + sizeof(attributeLength)); + attributeLength = ntohs(attributeLength); + + attributeStartIndex += NUM_BYTES_MESSAGE_TYPE_AND_LENGTH + attributeLength; + } + } + return false; +} + + +void LimitedNodeList::processSTUNResponse(std::unique_ptr packet) { + uint16_t newPublicPort; + QHostAddress newPublicAddress; + if (parseSTUNResponse(packet.get(), newPublicAddress, newPublicPort)) { + + if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) { + _publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort); + + qCDebug(networking, "New public socket received from STUN server is %s:%hu", + _publicSockAddr.getAddress().toString().toLocal8Bit().constData(), + _publicSockAddr.getPort()); + + if (!_hasCompletedInitialSTUN) { + // if we're here we have definitely completed our initial STUN sequence + stopInitialSTUNUpdate(true); + } + + emit publicSockAddrChanged(_publicSockAddr); + + flagTimeForConnectionStep(ConnectionStep::SetPublicSocketFromSTUN); } } } diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 1a3599e226..e74a6c49f8 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -146,6 +146,7 @@ public: const NodePermissions& permissions = DEFAULT_AGENT_PERMISSIONS, const QUuid& connectionSecret = QUuid()); + static bool parseSTUNResponse(udt::BasePacket* packet, QHostAddress& newPublicAddress, uint16_t& newPublicPort); bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; } const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; } @@ -232,6 +233,9 @@ public: bool packetVersionMatch(const udt::Packet& packet); bool isPacketVerified(const udt::Packet& packet); + static void makeSTUNRequestPacket(char* stunRequestPacket); + + public slots: void reset(); void eraseAllNodes(); @@ -275,7 +279,7 @@ protected: LimitedNodeList(int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT); LimitedNodeList(LimitedNodeList const&) = delete; // Don't implement, needed to avoid copies of singleton void operator=(LimitedNodeList const&) = delete; // Don't implement, needed to avoid copies of singleton - + qint64 sendPacket(std::unique_ptr packet, const Node& destinationNode, const HifiSockAddr& overridenSockAddr); qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr, @@ -284,7 +288,7 @@ protected: void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret = QUuid()); void setLocalSocket(const HifiSockAddr& sockAddr); - + bool packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet); void processSTUNResponse(std::unique_ptr packet); diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp index f0000c78cf..8614fbf960 100644 --- a/tools/ice-client/src/ICEClientApp.cpp +++ b/tools/ice-client/src/ICEClientApp.cpp @@ -16,7 +16,10 @@ #include "ICEClientApp.h" -ICEClientApp::ICEClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv) { +ICEClientApp::ICEClientApp(int argc, char* argv[]) : + QCoreApplication(argc, argv), + _stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT) +{ // parse command-line QCommandLineParser parser; parser.setApplicationDescription("High Fidelity ICE client"); @@ -80,7 +83,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv qDebug() << "ICE-server address is" << _iceServerAddr; - _state = 0; + setState(lookUpStunServer); QTimer* doTimer = new QTimer(this); connect(doTimer, &QTimer::timeout, this, &ICEClientApp::doSomething); @@ -91,43 +94,71 @@ ICEClientApp::~ICEClientApp() { delete _socket; } +void ICEClientApp::setState(int newState) { + // qDebug() << "state: " << _state << " --> " << newState; + _state = newState; +} + void ICEClientApp::doSomething() { if (_actionMax > 0 && _actionCount >= _actionMax) { + // time to stop. QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); - } - if (_state == 0) { + } else if (_state == lookUpStunServer) { + // lookup STUN server address + if (!_stunSockAddr.getAddress().isNull()) { + qDebug() << "stun server is" << _stunSockAddr; + setState(sendStunRequestPacket); + } + + } else if (_state == sendStunRequestPacket) { + // send STUN request packet + _domainServerPeerSet = false; unsigned int localPort = 0; + delete _socket; _socket = new udt::Socket(); _socket->bind(QHostAddress::AnyIPv4, localPort); - _socket->setPacketHandler([this](std::unique_ptr packet) { processPacket(std::move(packet)); }); + _socket->setPacketHandler([this](std::unique_ptr packet) { processPacket(std::move(packet)); }); + _socket->addUnfilteredHandler(_stunSockAddr, + [this](std::unique_ptr packet) { + processSTUNResponse(std::move(packet)); + }); qDebug() << "local port is" << _socket->localPort(); _localSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); _publicSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); - // QUuid peerID = QUuid("75cd162a-53dc-4292-aaa5-1304ab1bb0f2"); + const int NUM_BYTES_STUN_HEADER = 20; + char stunRequestPacket[NUM_BYTES_STUN_HEADER]; + LimitedNodeList::makeSTUNRequestPacket(stunRequestPacket); + qDebug() << "sending STUN request"; + _socket->writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); + + setState(waitForStunResponse); + + } else if (_state == talkToIceServer) { QUuid peerID; if (_domainID == QUuid()) { - // pick a random domain-id + // pick a random domain-id which will fail peerID = QUuid::createUuid(); - _state = 2; + setState(pause0); } else { // use the domain UUID given on the command-line peerID = _domainID; - _state = 1; + setState(waitForIceReply); } _sessionUUID = QUuid::createUuid(); + qDebug() << "I am" << _sessionUUID; sendPacketToIceServer(PacketType::ICEServerQuery, _iceServerAddr, _sessionUUID, peerID); _actionCount++; - } else if (_state == 2) { - _state = 3; - } else if (_state == 3) { + } else if (_state == pause0) { + setState(pause1); + } else if (_state == pause1) { qDebug() << ""; - _state = 0; + setState(sendStunRequestPacket); delete _socket; _socket = nullptr; } @@ -158,7 +189,7 @@ void ICEClientApp::icePingDomainServer() { return; } - qDebug() << "ice-pinging domain-server"; + qDebug() << "ice-pinging domain-server: " << _domainServerPeer; auto localPingPacket = LimitedNodeList::constructICEPingPacket(PingType::Local, _sessionUUID); _socket->writePacket(*localPingPacket, _domainServerPeer.getLocalSocket()); @@ -167,29 +198,52 @@ void ICEClientApp::icePingDomainServer() { _socket->writePacket(*publicPingPacket, _domainServerPeer.getPublicSocket()); } +void ICEClientApp::processSTUNResponse(std::unique_ptr packet) { + qDebug() << "got stun response"; + if (_state != waitForStunResponse) { + qDebug() << "got unexpected stun response"; + return; + } + + uint16_t newPublicPort; + QHostAddress newPublicAddress; + if (LimitedNodeList::parseSTUNResponse(packet.get(), newPublicAddress, newPublicPort)) { + _publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort); + qDebug() << "My public address is" << _publicSockAddr; + setState(talkToIceServer); + } +} + void ICEClientApp::processPacket(std::unique_ptr packet) { - auto nlPacket = NLPacket::fromBase(std::move(packet)); + std::unique_ptr nlPacket = NLPacket::fromBase(std::move(packet)); if (nlPacket->getPayloadSize() < NLPacket::localHeaderSize(PacketType::ICEServerHeartbeat)) { qDebug() << "got a short packet."; return; } + qDebug() << "here" << nlPacket->getType(); + QSharedPointer message = QSharedPointer::create(*nlPacket); const HifiSockAddr& senderAddr = message->getSenderSockAddr(); if (nlPacket->getType() == PacketType::ICEServerPeerInformation) { QDataStream iceResponseStream(message->getMessage()); - iceResponseStream >> _domainServerPeer; - _domainServerPeerSet = true; + if (!_domainServerPeerSet) { + iceResponseStream >> _domainServerPeer; + qDebug() << "got ICEServerPeerInformation from" << _domainServerPeer; + _domainServerPeerSet = true; - icePingDomainServer(); - _pingDomainTimer = new QTimer(this); - connect(_pingDomainTimer, &QTimer::timeout, this, &ICEClientApp::icePingDomainServer); - _pingDomainTimer->start(1000); - - qDebug() << "got ICEServerPeerInformation from" << _domainServerPeer.getUUID(); + icePingDomainServer(); + _pingDomainTimer = new QTimer(this); + connect(_pingDomainTimer, &QTimer::timeout, this, &ICEClientApp::icePingDomainServer); + _pingDomainTimer->start(500); + } else { + // NetworkPeer domainServerPeer; + // iceResponseStream >> domainServerPeer; + // qDebug() << "got repeat ICEServerPeerInformation from" << domainServerPeer; + } } else if (nlPacket->getType() == PacketType::ICEPing) { qDebug() << "got packet: " << nlPacket->getType(); @@ -198,14 +252,16 @@ void ICEClientApp::processPacket(std::unique_ptr packet) { } else if (nlPacket->getType() == PacketType::ICEPingReply) { qDebug() << "got packet: " << nlPacket->getType(); - if (_domainServerPeerSet && _state == 1 && + if (_domainServerPeerSet && _state == waitForIceReply && (senderAddr == _domainServerPeer.getLocalSocket() || senderAddr == _domainServerPeer.getPublicSocket())) { delete _pingDomainTimer; _pingDomainTimer = nullptr; - _state = 2; + setState(pause0); + } else { + qDebug() << "got unexpected ICEPingReply" << senderAddr; } } else { From b5e216db684e088b06709722408d88ae3129f78a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Sep 2016 17:21:42 -0700 Subject: [PATCH 07/59] ice test-client uses stun server to get public address --- tools/ice-client/src/ICEClientApp.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tools/ice-client/src/ICEClientApp.h b/tools/ice-client/src/ICEClientApp.h index efdbc9e022..eee759b2ba 100644 --- a/tools/ice-client/src/ICEClientApp.h +++ b/tools/ice-client/src/ICEClientApp.h @@ -27,14 +27,29 @@ public: ~ICEClientApp(); private: + enum State { + lookUpStunServer, // 0 + sendStunRequestPacket, // 1 + waitForStunResponse, // 2 + talkToIceServer, // 3 + waitForIceReply, // 4 + pause0, // 5 + pause1 // 6 + }; + + void setState(int newState); + void doSomething(); void sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID); void icePingDomainServer(); + void processSTUNResponse(std::unique_ptr packet); void processPacket(std::unique_ptr packet); bool _verbose; + HifiSockAddr _stunSockAddr; + unsigned int _actionCount { 0 }; unsigned int _actionMax { 0 }; @@ -52,10 +67,6 @@ private: bool _domainServerPeerSet { false }; NetworkPeer _domainServerPeer; - // 0 -- time to talk to ice server - // 1 -- waiting for ICEPingReply - // 2 -- pause - // 3 -- pause int _state { 0 }; }; From c87652ab6dc332e58772f9f5879e5d116315dc48 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Sep 2016 13:34:56 -0700 Subject: [PATCH 08/59] cl flag to cache stun-server resposne --- tools/ice-client/src/ICEClientApp.cpp | 73 +++++++++++++++++---------- tools/ice-client/src/ICEClientApp.h | 5 ++ 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp index 8614fbf960..9f11e0b569 100644 --- a/tools/ice-client/src/ICEClientApp.cpp +++ b/tools/ice-client/src/ICEClientApp.cpp @@ -39,6 +39,9 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : const QCommandLineOption domainIDOption("d", "domain-server uuid", "00000000-0000-0000-0000-000000000000"); parser.addOption(domainIDOption); + const QCommandLineOption cacheSTUNOption("s", "cache stun-server response"); + parser.addOption(cacheSTUNOption); + if (!parser.parse(QCoreApplication::arguments())) { qCritical() << parser.errorText() << endl; @@ -52,6 +55,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : } _verbose = parser.isSet(verboseOutput); + _cacheSTUNResult = parser.isSet(cacheSTUNOption); if (parser.isSet(howManyTimesOption)) { _actionMax = parser.value(howManyTimesOption).toInt(); @@ -99,6 +103,31 @@ void ICEClientApp::setState(int newState) { _state = newState; } +void ICEClientApp::closeSocket() { + _domainServerPeerSet = false; + delete _socket; + _socket = nullptr; +} + +void ICEClientApp::openSocket() { + if (_socket) { + return; + } + + _socket = new udt::Socket(); + unsigned int localPort = 0; + _socket->bind(QHostAddress::AnyIPv4, localPort); + _socket->setPacketHandler([this](std::unique_ptr packet) { processPacket(std::move(packet)); }); + _socket->addUnfilteredHandler(_stunSockAddr, + [this](std::unique_ptr packet) { + processSTUNResponse(std::move(packet)); + }); + + qDebug() << "local port is" << _socket->localPort(); + _localSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); + _publicSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); +} + void ICEClientApp::doSomething() { if (_actionMax > 0 && _actionCount >= _actionMax) { // time to stop. @@ -113,29 +142,22 @@ void ICEClientApp::doSomething() { } else if (_state == sendStunRequestPacket) { // send STUN request packet + closeSocket(); + openSocket(); - _domainServerPeerSet = false; - unsigned int localPort = 0; - delete _socket; - _socket = new udt::Socket(); - _socket->bind(QHostAddress::AnyIPv4, localPort); - _socket->setPacketHandler([this](std::unique_ptr packet) { processPacket(std::move(packet)); }); - _socket->addUnfilteredHandler(_stunSockAddr, - [this](std::unique_ptr packet) { - processSTUNResponse(std::move(packet)); - }); + if (!_cacheSTUNResult || !_stunResultSet) { + const int NUM_BYTES_STUN_HEADER = 20; + char stunRequestPacket[NUM_BYTES_STUN_HEADER]; + LimitedNodeList::makeSTUNRequestPacket(stunRequestPacket); + qDebug() << "sending STUN request"; + _socket->writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); - qDebug() << "local port is" << _socket->localPort(); - _localSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); - _publicSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); - - const int NUM_BYTES_STUN_HEADER = 20; - char stunRequestPacket[NUM_BYTES_STUN_HEADER]; - LimitedNodeList::makeSTUNRequestPacket(stunRequestPacket); - qDebug() << "sending STUN request"; - _socket->writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); - - setState(waitForStunResponse); + setState(waitForStunResponse); + } else { + qDebug() << "using cached STUN resposne"; + _publicSockAddr.setPort(_socket->localPort()); + setState(talkToIceServer); + } } else if (_state == talkToIceServer) { QUuid peerID; @@ -210,6 +232,7 @@ void ICEClientApp::processSTUNResponse(std::unique_ptr packet) if (LimitedNodeList::parseSTUNResponse(packet.get(), newPublicAddress, newPublicPort)) { _publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort); qDebug() << "My public address is" << _publicSockAddr; + _stunResultSet = true; setState(talkToIceServer); } } @@ -223,8 +246,6 @@ void ICEClientApp::processPacket(std::unique_ptr packet) { return; } - qDebug() << "here" << nlPacket->getType(); - QSharedPointer message = QSharedPointer::create(*nlPacket); const HifiSockAddr& senderAddr = message->getSenderSockAddr(); @@ -240,9 +261,9 @@ void ICEClientApp::processPacket(std::unique_ptr packet) { connect(_pingDomainTimer, &QTimer::timeout, this, &ICEClientApp::icePingDomainServer); _pingDomainTimer->start(500); } else { - // NetworkPeer domainServerPeer; - // iceResponseStream >> domainServerPeer; - // qDebug() << "got repeat ICEServerPeerInformation from" << domainServerPeer; + NetworkPeer domainServerPeer; + iceResponseStream >> domainServerPeer; + qDebug() << "got repeat ICEServerPeerInformation from" << domainServerPeer; } } else if (nlPacket->getType() == PacketType::ICEPing) { diff --git a/tools/ice-client/src/ICEClientApp.h b/tools/ice-client/src/ICEClientApp.h index eee759b2ba..a3adc3e665 100644 --- a/tools/ice-client/src/ICEClientApp.h +++ b/tools/ice-client/src/ICEClientApp.h @@ -37,6 +37,9 @@ private: pause1 // 6 }; + void closeSocket(); + void openSocket(); + void setState(int newState); void doSomething(); @@ -47,6 +50,8 @@ private: void processPacket(std::unique_ptr packet); bool _verbose; + bool _cacheSTUNResult; // should we only talk to stun server once? + bool _stunResultSet { false }; // have we already talked to stun server? HifiSockAddr _stunSockAddr; From 45b48c01bfdbe90e829048fc0d449a2c48bcde01 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Sep 2016 13:39:42 -0700 Subject: [PATCH 09/59] command-line arguments to control ice-server ip address and domain-server ID --- domain-server/src/DomainServer.cpp | 78 ++++++++++++++++++++++++------ domain-server/src/DomainServer.h | 6 +++ 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 72e9751294..654fe500c6 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -62,8 +63,11 @@ DomainServer::DomainServer(int argc, char* argv[]) : _webAuthenticationStateSet(), _cookieSessionHash(), _automaticNetworkingSetting(), - _settingsManager() + _settingsManager(), + _iceServerAddr(ICE_SERVER_DEFAULT_HOSTNAME), + _iceServerPort(ICE_SERVER_DEFAULT_PORT) { + parseCommandLine(); qInstallMessageHandler(LogHandler::verboseMessageHandler); LogUtils::init(); @@ -147,6 +151,46 @@ DomainServer::DomainServer(int argc, char* argv[]) : qDebug() << "domain-server is running"; } +void DomainServer::parseCommandLine() { + QCommandLineParser parser; + parser.setApplicationDescription("High Fidelity Domain Server"); + parser.addHelpOption(); + + const QCommandLineOption iceServerAddressOption("i", "ice-server address", "IP:PORT or HOSTNAME:PORT"); + parser.addOption(iceServerAddressOption); + + const QCommandLineOption domainIDOption("d", "domain-server uuid"); + parser.addOption(domainIDOption); + + if (!parser.parse(QCoreApplication::arguments())) { + qCritical() << parser.errorText() << endl; + parser.showHelp(); + Q_UNREACHABLE(); + } + + if (parser.isSet(iceServerAddressOption)) { + // parse the IP and port combination for this target + QString hostnamePortString = parser.value(iceServerAddressOption); + + _iceServerAddr = hostnamePortString.left(hostnamePortString.indexOf(':')); + _iceServerPort = (quint16) hostnamePortString.mid(hostnamePortString.indexOf(':') + 1).toUInt(); + if (_iceServerPort == 0) { + _iceServerPort = ICE_SERVER_DEFAULT_PORT; + } + + if (_iceServerAddr.isEmpty()) { + qCritical() << "Could not parse an IP address and port combination from" << hostnamePortString; + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + } + } + + if (parser.isSet(domainIDOption)) { + _overridingDomainID = QUuid(parser.value(domainIDOption)); + _overRideDomainID = true; + qDebug() << "domain-server ID is" << _overridingDomainID; + } +} + DomainServer::~DomainServer() { // destroy the LimitedNodeList before the DomainServer QCoreApplication is down DependencyManager::destroy(); @@ -295,7 +339,7 @@ void DomainServer::handleTempDomainSuccess(QNetworkReply& requestReply) { auto domainObject = jsonObject[DATA_KEY].toObject()[DOMAIN_KEY].toObject(); if (!domainObject.isEmpty()) { - auto id = domainObject[ID_KEY].toString(); + auto id = _overRideDomainID ? _overridingDomainID.toString() : domainObject[ID_KEY].toString(); auto name = domainObject[NAME_KEY].toString(); auto key = domainObject[KEY_KEY].toString(); @@ -403,26 +447,33 @@ void DomainServer::setupNodeListAndAssignments() { quint16 localHttpsPort = DOMAIN_SERVER_HTTPS_PORT; nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_HTTPS_PORT_SMEM_KEY, this, localHttpsPort); - // set our LimitedNodeList UUID to match the UUID from our config // nodes will currently use this to add resources to data-web that relate to our domain - const QVariant* idValueVariant = valueForKeyPath(settingsMap, METAVERSE_DOMAIN_ID_KEY_PATH); - if (idValueVariant) { - nodeList->setSessionUUID(idValueVariant->toString()); + bool isMetaverseDomain = false; + if (_overRideDomainID) { + nodeList->setSessionUUID(_overridingDomainID); + isMetaverseDomain = true; // assume metaverse domain + } else { + const QVariant* idValueVariant = valueForKeyPath(settingsMap, METAVERSE_DOMAIN_ID_KEY_PATH); + if (idValueVariant) { + nodeList->setSessionUUID(idValueVariant->toString()); + isMetaverseDomain = true; // if we have an ID, we'll assume we're a metaverse domain + } else { + nodeList->setSessionUUID(QUuid::createUuid()); // Use random UUID + } + } - // if we have an ID, we'll assume we're a metaverse domain - // now see if we think we're a temp domain (we have an API key) or a full domain + if (isMetaverseDomain) { + // see if we think we're a temp domain (we have an API key) or a full domain const auto& temporaryDomainKey = DependencyManager::get()->getTemporaryDomainKey(getID()); if (temporaryDomainKey.isEmpty()) { _type = MetaverseDomain; } else { _type = MetaverseTemporaryDomain; } - - } else { - nodeList->setSessionUUID(QUuid::createUuid()); // Use random UUID } + connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded); connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled); @@ -536,7 +587,7 @@ void DomainServer::setupAutomaticNetworking() { } else { qDebug() << "Cannot enable domain-server automatic networking without a domain ID." << "Please add an ID to your config file or via the web interface."; - + return; } } @@ -594,12 +645,11 @@ void DomainServer::setupICEHeartbeatForFullNetworking() { void DomainServer::updateICEServerAddresses() { if (_iceAddressLookupID == -1) { - _iceAddressLookupID = QHostInfo::lookupHost(ICE_SERVER_DEFAULT_HOSTNAME, this, SLOT(handleICEHostInfo(QHostInfo))); + _iceAddressLookupID = QHostInfo::lookupHost(_iceServerAddr, this, SLOT(handleICEHostInfo(QHostInfo))); } } void DomainServer::parseAssignmentConfigs(QSet& excludedTypes) { - // check for configs from the command line, these take precedence const QString ASSIGNMENT_CONFIG_REGEX_STRING = "config-([\\d]+)"; QRegExp assignmentConfigRegex(ASSIGNMENT_CONFIG_REGEX_STRING); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index e30f4515cc..de08c15468 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -105,6 +105,7 @@ signals: private: const QUuid& getID(); + void parseCommandLine(); void setupNodeListAndAssignments(); bool optionallySetupOAuth(); @@ -205,6 +206,11 @@ private: friend class DomainGatekeeper; friend class DomainMetadata; + + QString _iceServerAddr; + int _iceServerPort; + bool _overRideDomainID { false }; // should we override the domain-id from settings? + QUuid _overridingDomainID { QUuid() }; // what should we override it with? }; From eb6d329dfa6159896feb586f798f90e3016adf19 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Sep 2016 15:30:55 -0700 Subject: [PATCH 10/59] exit status depends on what happened. timeout added so it wont wait for server responses forever. --- tools/ice-client/src/ICEClientApp.cpp | 164 ++++++++++++++++++++------ tools/ice-client/src/ICEClientApp.h | 16 +++ 2 files changed, 146 insertions(+), 34 deletions(-) diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp index 9f11e0b569..1a251a45c2 100644 --- a/tools/ice-client/src/ICEClientApp.cpp +++ b/tools/ice-client/src/ICEClientApp.cpp @@ -10,15 +10,16 @@ // #include +#include #include #include #include +#include #include "ICEClientApp.h" ICEClientApp::ICEClientApp(int argc, char* argv[]) : - QCoreApplication(argc, argv), - _stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT) + QCoreApplication(argc, argv) { // parse command-line QCommandLineParser parser; @@ -33,7 +34,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : const QCommandLineOption iceServerAddressOption("i", "ice-server address", "IP:PORT or HOSTNAME:PORT"); parser.addOption(iceServerAddressOption); - const QCommandLineOption howManyTimesOption("n", "how many times to cycle", "0"); + const QCommandLineOption howManyTimesOption("n", "how many times to cycle", "1"); parser.addOption(howManyTimesOption); const QCommandLineOption domainIDOption("d", "domain-server uuid", "00000000-0000-0000-0000-000000000000"); @@ -42,7 +43,6 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : const QCommandLineOption cacheSTUNOption("s", "cache stun-server response"); parser.addOption(cacheSTUNOption); - if (!parser.parse(QCoreApplication::arguments())) { qCritical() << parser.errorText() << endl; parser.showHelp(); @@ -55,18 +55,29 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : } _verbose = parser.isSet(verboseOutput); + if (!_verbose) { + const_cast(&networking())->setEnabled(QtDebugMsg, false); + const_cast(&networking())->setEnabled(QtInfoMsg, false); + const_cast(&networking())->setEnabled(QtWarningMsg, false); + } + + _stunSockAddr = HifiSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT, true); + _cacheSTUNResult = parser.isSet(cacheSTUNOption); if (parser.isSet(howManyTimesOption)) { _actionMax = parser.value(howManyTimesOption).toInt(); + } else { + _actionMax = 1; } if (parser.isSet(domainIDOption)) { _domainID = QUuid(parser.value(domainIDOption)); - qDebug() << "domain-server ID is" << _domainID; + if (_verbose) { + qDebug() << "domain-server ID is" << _domainID; + } } - _iceServerAddr = HifiSockAddr("127.0.0.1", ICE_SERVER_DEFAULT_PORT); if (parser.isSet(iceServerAddressOption)) { // parse the IP and port combination for this target @@ -74,8 +85,11 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : QHostAddress address { hostnamePortString.left(hostnamePortString.indexOf(':')) }; quint16 port { (quint16) hostnamePortString.mid(hostnamePortString.indexOf(':') + 1).toUInt() }; + if (port == 0) { + port = 7337; + } - if (address.isNull() || port == 0) { + if (address.isNull()) { qCritical() << "Could not parse an IP address and port combination from" << hostnamePortString << "-" << "The parsed IP was" << address.toString() << "and the parsed port was" << port; @@ -85,7 +99,9 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : } } - qDebug() << "ICE-server address is" << _iceServerAddr; + if (_verbose) { + qDebug() << "ICE-server address is" << _iceServerAddr; + } setState(lookUpStunServer); @@ -99,7 +115,6 @@ ICEClientApp::~ICEClientApp() { } void ICEClientApp::setState(int newState) { - // qDebug() << "state: " << _state << " --> " << newState; _state = newState; } @@ -123,9 +138,12 @@ void ICEClientApp::openSocket() { processSTUNResponse(std::move(packet)); }); - qDebug() << "local port is" << _socket->localPort(); + if (_verbose) { + qDebug() << "local port is" << _socket->localPort(); + } _localSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); _publicSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort()); + _domainPingCount = 0; } void ICEClientApp::doSomething() { @@ -136,8 +154,15 @@ void ICEClientApp::doSomething() { } else if (_state == lookUpStunServer) { // lookup STUN server address if (!_stunSockAddr.getAddress().isNull()) { - qDebug() << "stun server is" << _stunSockAddr; + if (_verbose) { + qDebug() << "stun server is" << _stunSockAddr; + } setState(sendStunRequestPacket); + } else { + if (_verbose) { + qDebug() << "_stunSockAddr is" << _stunSockAddr.getAddress(); + } + QCoreApplication::exit(stunFailureExitStatus); } } else if (_state == sendStunRequestPacket) { @@ -149,12 +174,26 @@ void ICEClientApp::doSomething() { const int NUM_BYTES_STUN_HEADER = 20; char stunRequestPacket[NUM_BYTES_STUN_HEADER]; LimitedNodeList::makeSTUNRequestPacket(stunRequestPacket); - qDebug() << "sending STUN request"; + if (_verbose) { + qDebug() << "sending STUN request"; + } _socket->writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); + _stunResponseTimerCanceled = false; + _stunResponseTimer.singleShot(stunResponseTimeoutMilliSeconds, this, [&] { + if (_stunResponseTimerCanceled) { + return; + } + if (_verbose) { + qDebug() << "timeout waiting for stun-server response"; + } + QCoreApplication::exit(stunFailureExitStatus); + }); setState(waitForStunResponse); } else { - qDebug() << "using cached STUN resposne"; + if (_verbose) { + qDebug() << "using cached STUN response"; + } _publicSockAddr.setPort(_socket->localPort()); setState(talkToIceServer); } @@ -171,18 +210,30 @@ void ICEClientApp::doSomething() { setState(waitForIceReply); } _sessionUUID = QUuid::createUuid(); - qDebug() << "I am" << _sessionUUID; + if (_verbose) { + qDebug() << "I am" << _sessionUUID; + } sendPacketToIceServer(PacketType::ICEServerQuery, _iceServerAddr, _sessionUUID, peerID); - - _actionCount++; + _iceResponseTimerCanceled = false; + _iceResponseTimer.singleShot(iceResponseTimeoutMilliSeconds, this, [=] { + if (_iceResponseTimerCanceled) { + return; + } + if (_verbose) { + qDebug() << "timeout waiting for ice-server response"; + } + QCoreApplication::exit(iceFailureExitStatus); + }); } else if (_state == pause0) { setState(pause1); } else if (_state == pause1) { - qDebug() << ""; + if (_verbose) { + qDebug() << ""; + } + closeSocket(); setState(sendStunRequestPacket); - delete _socket; - _socket = nullptr; + _actionCount++; } } @@ -198,42 +249,68 @@ void ICEClientApp::sendPacketToIceServer(PacketType packetType, const HifiSockAd iceDataStream << peerID; - qDebug() << "Sending packet to ICE server to request connection info for peer with ID" - << uuidStringWithoutCurlyBraces(peerID); + if (_verbose) { + qDebug() << "Sending packet to ICE server to request connection info for peer with ID" + << uuidStringWithoutCurlyBraces(peerID); + } } // fillPacketHeader(packet, connectionSecret); _socket->writePacket(*icePacket, _iceServerAddr); } +void ICEClientApp::checkDomainPingCount() { + _domainPingCount++; + if (_domainPingCount > 5) { + if (_verbose) { + qDebug() << "too many unanswered pings to domain-server."; + } + QCoreApplication::exit(domainPingExitStatus); + } +} + void ICEClientApp::icePingDomainServer() { if (!_domainServerPeerSet) { return; } - qDebug() << "ice-pinging domain-server: " << _domainServerPeer; + if (_verbose) { + qDebug() << "ice-pinging domain-server: " << _domainServerPeer; + } auto localPingPacket = LimitedNodeList::constructICEPingPacket(PingType::Local, _sessionUUID); _socket->writePacket(*localPingPacket, _domainServerPeer.getLocalSocket()); auto publicPingPacket = LimitedNodeList::constructICEPingPacket(PingType::Public, _sessionUUID); _socket->writePacket(*publicPingPacket, _domainServerPeer.getPublicSocket()); + checkDomainPingCount(); } void ICEClientApp::processSTUNResponse(std::unique_ptr packet) { - qDebug() << "got stun response"; - if (_state != waitForStunResponse) { - qDebug() << "got unexpected stun response"; - return; + if (_verbose) { + qDebug() << "got stun response"; } + if (_state != waitForStunResponse) { + if (_verbose) { + qDebug() << "got unexpected stun response"; + } + QCoreApplication::exit(stunFailureExitStatus); + } + + _stunResponseTimer.stop(); + _stunResponseTimerCanceled = true; uint16_t newPublicPort; QHostAddress newPublicAddress; if (LimitedNodeList::parseSTUNResponse(packet.get(), newPublicAddress, newPublicPort)) { _publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort); - qDebug() << "My public address is" << _publicSockAddr; + if (_verbose) { + qDebug() << "My public address is" << _publicSockAddr; + } _stunResultSet = true; setState(talkToIceServer); + } else { + QCoreApplication::exit(stunFailureExitStatus); } } @@ -242,7 +319,9 @@ void ICEClientApp::processPacket(std::unique_ptr packet) { std::unique_ptr nlPacket = NLPacket::fromBase(std::move(packet)); if (nlPacket->getPayloadSize() < NLPacket::localHeaderSize(PacketType::ICEServerHeartbeat)) { - qDebug() << "got a short packet."; + if (_verbose) { + qDebug() << "got a short packet."; + } return; } @@ -250,10 +329,16 @@ void ICEClientApp::processPacket(std::unique_ptr packet) { const HifiSockAddr& senderAddr = message->getSenderSockAddr(); if (nlPacket->getType() == PacketType::ICEServerPeerInformation) { + // cancel the timeout timer + _iceResponseTimer.stop(); + _iceResponseTimerCanceled = true; + QDataStream iceResponseStream(message->getMessage()); if (!_domainServerPeerSet) { iceResponseStream >> _domainServerPeer; - qDebug() << "got ICEServerPeerInformation from" << _domainServerPeer; + if (_verbose) { + qDebug() << "got ICEServerPeerInformation from" << _domainServerPeer; + } _domainServerPeerSet = true; icePingDomainServer(); @@ -263,16 +348,23 @@ void ICEClientApp::processPacket(std::unique_ptr packet) { } else { NetworkPeer domainServerPeer; iceResponseStream >> domainServerPeer; - qDebug() << "got repeat ICEServerPeerInformation from" << domainServerPeer; + if (_verbose) { + qDebug() << "got repeat ICEServerPeerInformation from" << domainServerPeer; + } } } else if (nlPacket->getType() == PacketType::ICEPing) { - qDebug() << "got packet: " << nlPacket->getType(); + if (_verbose) { + qDebug() << "got packet: " << nlPacket->getType(); + } auto replyPacket = LimitedNodeList::constructICEPingReplyPacket(*message, _sessionUUID); _socket->writePacket(*replyPacket, senderAddr); + checkDomainPingCount(); } else if (nlPacket->getType() == PacketType::ICEPingReply) { - qDebug() << "got packet: " << nlPacket->getType(); + if (_verbose) { + qDebug() << "got packet: " << nlPacket->getType(); + } if (_domainServerPeerSet && _state == waitForIceReply && (senderAddr == _domainServerPeer.getLocalSocket() || senderAddr == _domainServerPeer.getPublicSocket())) { @@ -282,10 +374,14 @@ void ICEClientApp::processPacket(std::unique_ptr packet) { setState(pause0); } else { - qDebug() << "got unexpected ICEPingReply" << senderAddr; + if (_verbose) { + qDebug() << "got unexpected ICEPingReply" << senderAddr; + } } } else { - qDebug() << "got unexpected packet: " << nlPacket->getType(); + if (_verbose) { + qDebug() << "got unexpected packet: " << nlPacket->getType(); + } } } diff --git a/tools/ice-client/src/ICEClientApp.h b/tools/ice-client/src/ICEClientApp.h index a3adc3e665..3635bc07f4 100644 --- a/tools/ice-client/src/ICEClientApp.h +++ b/tools/ice-client/src/ICEClientApp.h @@ -26,6 +26,13 @@ public: ICEClientApp(int argc, char* argv[]); ~ICEClientApp(); + const int stunFailureExitStatus { 1 }; + const int iceFailureExitStatus { 2 }; + const int domainPingExitStatus { 3 }; + + const int stunResponseTimeoutMilliSeconds { 2000 }; + const int iceResponseTimeoutMilliSeconds { 2000 }; + private: enum State { lookUpStunServer, // 0 @@ -48,6 +55,7 @@ private: void icePingDomainServer(); void processSTUNResponse(std::unique_ptr packet); void processPacket(std::unique_ptr packet); + void checkDomainPingCount(); bool _verbose; bool _cacheSTUNResult; // should we only talk to stun server once? @@ -73,8 +81,16 @@ private: NetworkPeer _domainServerPeer; int _state { 0 }; + + QTimer _stunResponseTimer; + bool _stunResponseTimerCanceled { false }; + QTimer _iceResponseTimer; + bool _iceResponseTimerCanceled { false }; + int _domainPingCount { 0 }; }; + + #endif //hifi_ICEClientApp_h From 8108557e10f271124f825a294af28d4816460bf8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Sep 2016 15:37:50 -0700 Subject: [PATCH 11/59] whitespace --- domain-server/src/DomainServer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 654fe500c6..5965b8aa5c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -473,7 +473,6 @@ void DomainServer::setupNodeListAndAssignments() { } } - connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded); connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled); @@ -587,7 +586,6 @@ void DomainServer::setupAutomaticNetworking() { } else { qDebug() << "Cannot enable domain-server automatic networking without a domain ID." << "Please add an ID to your config file or via the web interface."; - return; } } From ba8aa5b50cc1a01b31c1c62663c79cd5295cee58 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Sep 2016 16:38:34 -0700 Subject: [PATCH 12/59] fix camelcase --- domain-server/src/DomainServer.cpp | 6 +++--- domain-server/src/DomainServer.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 6a30604abd..4489f0d607 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -198,7 +198,7 @@ void DomainServer::parseCommandLine() { if (parser.isSet(domainIDOption)) { _overridingDomainID = QUuid(parser.value(domainIDOption)); - _overRideDomainID = true; + _overrideDomainID = true; qDebug() << "domain-server ID is" << _overridingDomainID; } } @@ -351,7 +351,7 @@ void DomainServer::handleTempDomainSuccess(QNetworkReply& requestReply) { auto domainObject = jsonObject[DATA_KEY].toObject()[DOMAIN_KEY].toObject(); if (!domainObject.isEmpty()) { - auto id = _overRideDomainID ? _overridingDomainID.toString() : domainObject[ID_KEY].toString(); + auto id = _overrideDomainID ? _overridingDomainID.toString() : domainObject[ID_KEY].toString(); auto name = domainObject[NAME_KEY].toString(); auto key = domainObject[KEY_KEY].toString(); @@ -462,7 +462,7 @@ void DomainServer::setupNodeListAndAssignments() { // set our LimitedNodeList UUID to match the UUID from our config // nodes will currently use this to add resources to data-web that relate to our domain bool isMetaverseDomain = false; - if (_overRideDomainID) { + if (_overrideDomainID) { nodeList->setSessionUUID(_overridingDomainID); isMetaverseDomain = true; // assume metaverse domain } else { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index de08c15468..066f2be0d1 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -209,7 +209,7 @@ private: QString _iceServerAddr; int _iceServerPort; - bool _overRideDomainID { false }; // should we override the domain-id from settings? + bool _overrideDomainID { false }; // should we override the domain-id from settings? QUuid _overridingDomainID { QUuid() }; // what should we override it with? }; From b9ea93de57c81e16960d009ccb6e757a99c0e265 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 21 Sep 2016 10:08:02 -0700 Subject: [PATCH 13/59] use qWarning rather than qCritical because qCritical may exit --- domain-server/src/DomainServer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 4489f0d607..99bbffa750 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -175,7 +175,7 @@ void DomainServer::parseCommandLine() { parser.addOption(domainIDOption); if (!parser.parse(QCoreApplication::arguments())) { - qCritical() << parser.errorText() << endl; + qWarning() << parser.errorText() << endl; parser.showHelp(); Q_UNREACHABLE(); } @@ -191,7 +191,7 @@ void DomainServer::parseCommandLine() { } if (_iceServerAddr.isEmpty()) { - qCritical() << "Could not parse an IP address and port combination from" << hostnamePortString; + qWarning() << "Could not parse an IP address and port combination from" << hostnamePortString; QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); } } @@ -210,7 +210,7 @@ DomainServer::~DomainServer() { void DomainServer::queuedQuit(QString quitMessage, int exitCode) { if (!quitMessage.isEmpty()) { - qCritical() << qPrintable(quitMessage); + qWarning() << qPrintable(quitMessage); } QCoreApplication::exit(exitCode); From 785e187a270302784c5cf2628d0b16def0f664ca Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 21 Sep 2016 11:39:25 -0700 Subject: [PATCH 14/59] edit.js uses notification.js to indicate errors instead of a modal dialog. in HMD, notification messages no longer jitter around --- scripts/system/edit.js | 16 +++--- scripts/system/notifications.js | 87 ++++++++++++++++++++------------- 2 files changed, 61 insertions(+), 42 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index d90566c619..d673bb4653 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -184,7 +184,7 @@ var toolBar = (function () { properties.position = position; entityID = Entities.addEntity(properties); } else { - Window.alert("Can't create " + properties.type + ": " + properties.type + " would be out of bounds."); + Window.notifyEditError("Can't create " + properties.type + ": " + properties.type + " would be out of bounds."); } selectionManager.clearSelections(); @@ -445,7 +445,7 @@ var toolBar = (function () { return; } if (active && !Entities.canRez() && !Entities.canRezTmp()) { - Window.alert(INSUFFICIENT_PERMISSIONS_ERROR_MSG); + Window.notifyEditError(INSUFFICIENT_PERMISSIONS_ERROR_MSG); return; } Messages.sendLocalMessage("edit-events", JSON.stringify({ @@ -1082,13 +1082,13 @@ function handeMenuEvent(menuItem) { deleteSelectedEntities(); } else if (menuItem === "Export Entities") { if (!selectionManager.hasSelection()) { - Window.alert("No entities have been selected."); + Window.notifyEditError("No entities have been selected."); } else { var filename = Window.save("Select Where to Save", "", "*.json"); if (filename) { var success = Clipboard.exportEntities(filename, selectionManager.selections); if (!success) { - Window.alert("Export failed."); + Window.notifyEditError("Export failed."); } } } @@ -1156,7 +1156,7 @@ function getPositionToImportEntity() { } function importSVO(importURL) { if (!Entities.canAdjustLocks()) { - Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); + Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; } @@ -1188,10 +1188,10 @@ function importSVO(importURL) { Window.raiseMainWindow(); } else { - Window.alert("Can't import objects: objects would be out of bounds."); + Window.notifyEditError("Can't import objects: objects would be out of bounds."); } } else { - Window.alert("There was an error importing the entity file."); + Window.notifyEditError("There was an error importing the entity file."); } Overlays.editOverlay(importingSVOTextOverlay, { @@ -1481,7 +1481,7 @@ var PropertiesTool = function (opts) { // If any of the natural dimensions are not 0, resize if (properties.type === "Model" && naturalDimensions.x === 0 && naturalDimensions.y === 0 && naturalDimensions.z === 0) { - Window.alert("Cannot reset entity to its natural dimensions: Model URL" + + Window.notifyEditError("Cannot reset entity to its natural dimensions: Model URL" + " is invalid or the model has not yet been loaded."); } else { Entities.editEntity(selectionManager.selections[i], { diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index f41b0502c8..f3ba466342 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -58,6 +58,8 @@ // } // } +/* global Script, Controller, Overlays, SoundArray, Quat, Vec3, MyAvatar, Menu, HMD, AudioDevice, LODManager, Settings, Camera */ + (function() { // BEGIN LOCAL_SCOPE Script.include("./libraries/soundArray.js"); @@ -76,11 +78,9 @@ var fontSize = 12.0; var PERSIST_TIME_2D = 10.0; // Time in seconds before notification fades var PERSIST_TIME_3D = 15.0; var persistTime = PERSIST_TIME_2D; -var clickedText = false; var frame = 0; var ourWidth = Window.innerWidth; var ourHeight = Window.innerHeight; -var text = "placeholder"; var ctrlIsPressed = false; var ready = true; var MENU_NAME = 'Tools > Notifications'; @@ -97,12 +97,14 @@ var NotificationType = { WINDOW_RESIZE: 3, LOD_WARNING: 4, CONNECTION_REFUSED: 5, + EDIT_ERROR: 6, properties: [ { text: "Mute Toggle" }, { text: "Snapshot" }, { text: "Window Resize" }, { text: "Level of Detail" }, - { text: "Connection Refused" } + { text: "Connection Refused" }, + { text: "Edit error" } ], getTypeFromMenuItem: function(menuItemName) { if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) { @@ -253,6 +255,9 @@ function notify(notice, button, height, imageProperties, image) { positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y); + notice.parentID = MyAvatar.sessionUUID; + notice.parentJointIndex = -2; + if (!image) { notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE; notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE; @@ -270,6 +275,8 @@ function notify(notice, button, height, imageProperties, image) { button.url = button.imageURL; button.scale = button.width * NOTIFICATION_3D_SCALE; button.isFacingAvatar = false; + button.parentID = MyAvatar.sessionUUID; + button.parentJointIndex = -2; buttons.push((Overlays.addOverlay("image3d", button))); overlay3DDetails.push({ @@ -279,6 +286,34 @@ function notify(notice, button, height, imageProperties, image) { width: noticeWidth, height: noticeHeight }); + + + var defaultEyePosition, + avatarOrientation, + notificationPosition, + notificationOrientation, + buttonPosition; + + if (isOnHMD && notifications.length > 0) { + // Update 3D overlays to maintain positions relative to avatar + defaultEyePosition = MyAvatar.getDefaultEyePosition(); + avatarOrientation = MyAvatar.orientation; + + for (i = 0; i < notifications.length; i += 1) { + notificationPosition = Vec3.sum(defaultEyePosition, + Vec3.multiplyQbyV(avatarOrientation, + overlay3DDetails[i].notificationPosition)); + notificationOrientation = Quat.multiply(avatarOrientation, + overlay3DDetails[i].notificationOrientation); + buttonPosition = Vec3.sum(defaultEyePosition, + Vec3.multiplyQbyV(avatarOrientation, + overlay3DDetails[i].buttonPosition)); + Overlays.editOverlay(notifications[i], { position: notificationPosition, + rotation: notificationOrientation }); + Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: notificationOrientation }); + } + } + } else { if (!image) { notificationText = Overlays.addOverlay("text", notice); @@ -429,11 +464,6 @@ function update() { noticeOut, buttonOut, arraysOut, - defaultEyePosition, - avatarOrientation, - notificationPosition, - notificationOrientation, - buttonPosition, positions, i, j, @@ -457,7 +487,8 @@ function update() { Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); if (isOnHMD) { - positions = calculate3DOverlayPositions(overlay3DDetails[i].width, overlay3DDetails[i].height, locationY); + positions = calculate3DOverlayPositions(overlay3DDetails[i].width, + overlay3DDetails[i].height, locationY); overlay3DDetails[i].notificationOrientation = positions.notificationOrientation; overlay3DDetails[i].notificationPosition = positions.notificationPosition; overlay3DDetails[i].buttonPosition = positions.buttonPosition; @@ -480,22 +511,6 @@ function update() { } } } - - if (isOnHMD && notifications.length > 0) { - // Update 3D overlays to maintain positions relative to avatar - defaultEyePosition = MyAvatar.getDefaultEyePosition(); - avatarOrientation = MyAvatar.orientation; - - for (i = 0; i < notifications.length; i += 1) { - notificationPosition = Vec3.sum(defaultEyePosition, - Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].notificationPosition)); - notificationOrientation = Quat.multiply(avatarOrientation, overlay3DDetails[i].notificationOrientation); - buttonPosition = Vec3.sum(defaultEyePosition, - Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].buttonPosition)); - Overlays.editOverlay(notifications[i], { position: notificationPosition, rotation: notificationOrientation }); - Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: notificationOrientation }); - } - } } var STARTUP_TIMEOUT = 500, // ms @@ -532,12 +547,17 @@ function onDomainConnectionRefused(reason) { createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED); } +function onEditError(msg) { + createNotification(wordWrap(msg), NotificationType.EDIT_ERROR); +} + + function onSnapshotTaken(path, notify) { if (notify) { var imageProperties = { path: "file:///" + path, aspectRatio: Window.innerWidth / Window.innerHeight - } + }; createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties); } } @@ -571,8 +591,6 @@ function keyReleaseEvent(key) { // Triggers notification on specific key driven events function keyPressEvent(key) { - var noteString; - if (key.key === 16777249) { ctrlIsPressed = true; } @@ -622,13 +640,13 @@ function menuItemEvent(menuItem) { } LODManager.LODDecreased.connect(function() { - var warningText = "\n" - + "Due to the complexity of the content, the \n" - + "level of detail has been decreased. " - + "You can now see: \n" - + LODManager.getLODFeedbackText(); + var warningText = "\n" + + "Due to the complexity of the content, the \n" + + "level of detail has been decreased. " + + "You can now see: \n" + + LODManager.getLODFeedbackText(); - if (lodTextID == false) { + if (lodTextID === false) { lodTextID = createNotification(warningText, NotificationType.LOD_WARNING); } else { Overlays.editOverlay(lodTextID, { text: warningText }); @@ -644,6 +662,7 @@ Script.scriptEnding.connect(scriptEnding); Menu.menuItemEvent.connect(menuItemEvent); Window.domainConnectionRefused.connect(onDomainConnectionRefused); Window.snapshotTaken.connect(onSnapshotTaken); +Window.notifyEditError = onEditError; setup(); From 5d3d4bdbd944b2111d21812695fc02921ad186d1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Sep 2016 18:01:08 -0700 Subject: [PATCH 15/59] add support for redirection on domain at max capacity --- .../resources/describe-settings.json | 8 ++++ domain-server/src/DomainGatekeeper.cpp | 41 +++++++++++++------ domain-server/src/DomainGatekeeper.h | 3 +- interface/src/Application.cpp | 11 ++++- interface/src/Application.h | 2 +- .../src/scripting/WindowScriptingInterface.h | 2 +- libraries/networking/src/DomainHandler.cpp | 9 +++- libraries/networking/src/DomainHandler.h | 2 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- 10 files changed, 61 insertions(+), 22 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index c9d7ea77d5..84408d2e9a 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -380,6 +380,14 @@ "default": "0", "advanced": false }, + { + "name": "maximum_user_capacity_redirect_location", + "label": "Redirect to Location on Maximum Capacity", + "help": "Is there another domain, you'd like to redirect clients to when the maximum number of avatars are connected.", + "placeholder": "", + "default": "", + "advanced": false + }, { "name": "standard_permissions", "type": "table", diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 23a53c3eb0..e33cbe1755 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -317,6 +317,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo } const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity"; +const QString MAXIMUM_USER_CAPACITY_REDIRECT_LOCATION = "security.maximum_user_capacity_redirect_location"; SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection, const QString& username, @@ -363,7 +364,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) { sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.", - nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::TooManyUsers); + nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorized); #ifdef WANT_DEBUG qDebug() << "stalling login due to permissions:" << username; #endif @@ -372,8 +373,16 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect if (!userPerms.can(NodePermissions::Permission::canConnectPastMaxCapacity) && !isWithinMaxCapacity()) { // we can't allow this user to connect because we are at max capacity + QString redirectOnMaxCapacity; + const QVariant* redirectOnMaxCapacityVariant = + valueForKeyPath(_server->_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY_REDIRECT_LOCATION); + if (redirectOnMaxCapacityVariant && redirectOnMaxCapacityVariant->canConvert()) { + redirectOnMaxCapacity = redirectOnMaxCapacityVariant->toString(); + qDebug() << "Redirection domain:" << redirectOnMaxCapacity; + } + sendConnectionDeniedPacket("Too many connected users.", nodeConnection.senderSockAddr, - DomainHandler::ConnectionRefusedReason::TooManyUsers); + DomainHandler::ConnectionRefusedReason::TooManyUsers, redirectOnMaxCapacity); #ifdef WANT_DEBUG qDebug() << "stalling login due to max capacity:" << username; #endif @@ -623,22 +632,30 @@ void DomainGatekeeper::sendProtocolMismatchConnectionDenial(const HifiSockAddr& } void DomainGatekeeper::sendConnectionDeniedPacket(const QString& reason, const HifiSockAddr& senderSockAddr, - DomainHandler::ConnectionRefusedReason reasonCode) { + DomainHandler::ConnectionRefusedReason reasonCode, + QString extraInfo) { // this is an agent and we've decided we won't let them connect - send them a packet to deny connection - QByteArray utfString = reason.toUtf8(); - quint16 payloadSize = utfString.size(); + QByteArray utfReasonString = reason.toUtf8(); + quint16 reasonSize = utfReasonString.size(); + + QByteArray utfExtraInfo = extraInfo.toUtf8(); + quint16 extraInfoSize = utfExtraInfo.size(); // setup the DomainConnectionDenied packet auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, - payloadSize + sizeof(payloadSize) + sizeof(uint8_t)); + sizeof(uint8_t) + // reasonCode + reasonSize + sizeof(reasonSize) + + extraInfoSize + sizeof(extraInfoSize)); // pack in the reason the connection was denied (the client displays this) - if (payloadSize > 0) { - uint8_t reasonCodeWire = (uint8_t)reasonCode; - connectionDeniedPacket->writePrimitive(reasonCodeWire); - connectionDeniedPacket->writePrimitive(payloadSize); - connectionDeniedPacket->write(utfString); - } + uint8_t reasonCodeWire = (uint8_t)reasonCode; + connectionDeniedPacket->writePrimitive(reasonCodeWire); + connectionDeniedPacket->writePrimitive(reasonSize); + connectionDeniedPacket->write(utfReasonString); + + // write the extra info as well + connectionDeniedPacket->writePrimitive(extraInfoSize); + connectionDeniedPacket->write(utfExtraInfo); // send the packet off DependencyManager::get()->sendPacket(std::move(connectionDeniedPacket), senderSockAddr); diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 06ecfcf285..b7d2a03af6 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -88,7 +88,8 @@ private: void sendConnectionTokenPacket(const QString& username, const HifiSockAddr& senderSockAddr); static void sendConnectionDeniedPacket(const QString& reason, const HifiSockAddr& senderSockAddr, - DomainHandler::ConnectionRefusedReason reasonCode = DomainHandler::ConnectionRefusedReason::Unknown); + DomainHandler::ConnectionRefusedReason reasonCode = DomainHandler::ConnectionRefusedReason::Unknown, + QString extraInfo = QString()); void pingPunchForConnectingPeer(const SharedNetworkPeer& peer); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8a37662ca9..97a10ea232 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1239,8 +1239,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : firstRun.set(false); } -void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCode) { - switch (static_cast(reasonCode)) { +void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { + DomainHandler::ConnectionRefusedReason reasonCode = static_cast(reasonCodeInt); + + if (reasonCode == DomainHandler::ConnectionRefusedReason::TooManyUsers && !extraInfo.isEmpty()) { + DependencyManager::get()->handleLookupString(extraInfo); + return; + } + + switch (reasonCode) { case DomainHandler::ConnectionRefusedReason::ProtocolMismatch: case DomainHandler::ConnectionRefusedReason::TooManyUsers: case DomainHandler::ConnectionRefusedReason::Unknown: { diff --git a/interface/src/Application.h b/interface/src/Application.h index 02682defca..4c52ff8526 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -375,7 +375,7 @@ private slots: void nodeKilled(SharedNodePointer node); static void packetSent(quint64 length); void updateDisplayMode(); - void domainConnectionRefused(const QString& reasonMessage, int reason); + void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo); private: static void initDisplay(); diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 715d0657a3..9303636a1f 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -58,7 +58,7 @@ public slots: signals: void domainChanged(const QString& domainHostname); void svoImportRequested(const QString& url); - void domainConnectionRefused(const QString& reasonMessage, int reasonCode); + void domainConnectionRefused(const QString& reasonMessage, int reasonCode, const QString& extraInfo); void snapshotTaken(const QString& path, bool notify); void snapshotShared(const QString& error); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 739c0f8f4a..eecc1515f5 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -402,13 +402,18 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointerreadWithoutCopy(reasonSize); QString reasonMessage = QString::fromUtf8(reasonText); + quint16 extraInfoSize; + message->readPrimitive(&extraInfoSize); + auto extraInfoUtf8= message->readWithoutCopy(extraInfoSize); + QString extraInfo = QString::fromUtf8(extraInfoUtf8); + // output to the log so the user knows they got a denied connection request // and check and signal for an access token so that we can make sure they are logged in - qCWarning(networking) << "The domain-server denied a connection request: " << reasonMessage; + qCWarning(networking) << "The domain-server denied a connection request: " << reasonMessage << " extraInfo:" << extraInfo; if (!_domainConnectionRefusals.contains(reasonMessage)) { _domainConnectionRefusals.insert(reasonMessage); - emit domainConnectionRefused(reasonMessage, (int)reasonCode); + emit domainConnectionRefused(reasonMessage, (int)reasonCode, extraInfo); } auto accountManager = DependencyManager::get(); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 50639a4817..7f89b47197 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -123,7 +123,7 @@ signals: void settingsReceived(const QJsonObject& domainSettingsObject); void settingsReceiveFail(); - void domainConnectionRefused(QString reasonMessage, int reason); + void domainConnectionRefused(QString reasonMessage, int reason, const QString& extraInfo); private: bool reasonSuggestsLogin(ConnectionRefusedReason reasonCode); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 0f3d5885ff..ec4e724c1b 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -64,7 +64,7 @@ PacketVersion versionForPacketType(PacketType packetType) { return 18; // Introduction of node ignore request (which replaced an unused packet tpye) case PacketType::DomainConnectionDenied: - return static_cast(DomainConnectionDeniedVersion::IncludesReasonCode); + return static_cast(DomainConnectionDeniedVersion::IncludesExtraInfo); case PacketType::DomainConnectRequest: return static_cast(DomainConnectRequestVersion::HasProtocolVersions); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 25500b984f..aa775b9f53 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -206,7 +206,8 @@ enum class DomainConnectRequestVersion : PacketVersion { enum class DomainConnectionDeniedVersion : PacketVersion { ReasonMessageOnly = 17, - IncludesReasonCode + IncludesReasonCode, + IncludesExtraInfo }; enum class DomainServerAddedNodeVersion : PacketVersion { From dc5e63c4f146806f7e735de1df1b1c35fcae3f21 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 22 Sep 2016 10:25:26 -0700 Subject: [PATCH 16/59] re-enable web entity-items on linux --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index cc022d9df2..1c177cffc4 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -198,15 +198,10 @@ void RenderableWebEntityItem::render(RenderArgs* args) { #endif if (!_webSurface) { - #if defined(Q_OS_LINUX) - // these don't seem to work on Linux - return; - #else if (!buildWebSurface(static_cast(args->_renderer))) { return; } _fadeStartTime = usecTimestampNow(); - #endif } _lastRenderTime = usecTimestampNow(); From 0192f72a110446cf184c7a65445920da906c85cc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 22 Sep 2016 10:34:00 -0700 Subject: [PATCH 17/59] use named constant rather than 7337 --- tools/ice-client/src/ICEClientApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp index 1a251a45c2..992014ad7d 100644 --- a/tools/ice-client/src/ICEClientApp.cpp +++ b/tools/ice-client/src/ICEClientApp.cpp @@ -86,7 +86,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) : QHostAddress address { hostnamePortString.left(hostnamePortString.indexOf(':')) }; quint16 port { (quint16) hostnamePortString.mid(hostnamePortString.indexOf(':') + 1).toUInt() }; if (port == 0) { - port = 7337; + port = ICE_SERVER_DEFAULT_PORT; } if (address.isNull()) { From bd2ee56a27058706ea1455c70c49d411c31cdb1b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 22 Sep 2016 12:45:49 -0700 Subject: [PATCH 18/59] fix domain-server command-line arguments --- domain-server/src/DomainServer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 99bbffa750..e3670108bb 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -174,6 +174,13 @@ void DomainServer::parseCommandLine() { const QCommandLineOption domainIDOption("d", "domain-server uuid"); parser.addOption(domainIDOption); + const QCommandLineOption getTempNameOption("get-temp-name", "Request a temporary domain-name"); + parser.addOption(getTempNameOption); + + const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option"); + parser.addOption(masterConfigOption); + + if (!parser.parse(QCoreApplication::arguments())) { qWarning() << parser.errorText() << endl; parser.showHelp(); From a7f0ba24ed1b5944afb620f81d0d10c99b6eaf94 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 10 Sep 2016 13:26:30 -0700 Subject: [PATCH 19/59] Dynamic texture memory management --- interface/src/ui/CachesSizeDialog.cpp | 5 +- libraries/gpu-gl/src/gpu/gl/GLBackend.h | 1 + libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 241 +++++++++++++- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h | 2 +- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 73 ++--- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 70 ++-- .../gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 6 +- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 2 - .../src/gpu/gl41/GL41BackendTexture.cpp | 59 +--- libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 5 + libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 71 +++-- .../src/gpu/gl45/GL45BackendTexture.cpp | 300 +++++++++++++++--- .../src/model-networking/TextureCache.cpp | 3 +- tests/render-perf/src/main.cpp | 14 +- tests/render-texture-load/src/main.cpp | 125 +++++++- 15 files changed, 733 insertions(+), 244 deletions(-) diff --git a/interface/src/ui/CachesSizeDialog.cpp b/interface/src/ui/CachesSizeDialog.cpp index dee9452c73..935a6d126e 100644 --- a/interface/src/ui/CachesSizeDialog.cpp +++ b/interface/src/ui/CachesSizeDialog.cpp @@ -58,7 +58,10 @@ void CachesSizeDialog::confirmClicked(bool checked) { DependencyManager::get()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES); DependencyManager::get()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES); + // Disabling the texture cache because it's a liability in cases where we're overcommiting GPU memory +#if 0 DependencyManager::get()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES); +#endif QDialog::close(); } @@ -78,4 +81,4 @@ void CachesSizeDialog::reject() { void CachesSizeDialog::closeEvent(QCloseEvent* event) { QDialog::closeEvent(event); emit closed(); -} \ No newline at end of file +} diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index bc348e3c3f..29e66da020 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -365,6 +365,7 @@ protected: typedef void (GLBackend::*CommandCall)(const Batch&, size_t); static CommandCall _commandCalls[Batch::NUM_COMMANDS]; friend class GLState; + friend class GLTexture; }; } } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index 4bff5c87bd..39c3bd2f67 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -12,9 +12,244 @@ using namespace gpu; using namespace gpu::gl; -GLTexelFormat GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { - GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }; - return texel; +GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { + GLenum result = GL_RGBA8; + switch (dstFormat.getDimension()) { + case gpu::SCALAR: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + case gpu::SRGB: + case gpu::SRGBA: + switch (dstFormat.getType()) { + case gpu::UINT32: + result = GL_R32UI; + break; + case gpu::INT32: + result = GL_R32I; + break; + case gpu::NUINT32: + result = GL_R8; + break; + case gpu::NINT32: + result = GL_R8_SNORM; + break; + case gpu::FLOAT: + result = GL_R32F; + break; + case gpu::UINT16: + result = GL_R16UI; + break; + case gpu::INT16: + result = GL_R16I; + break; + case gpu::NUINT16: + result = GL_R16; + break; + case gpu::NINT16: + result = GL_R16_SNORM; + break; + case gpu::HALF: + result = GL_R16F; + break; + case gpu::UINT8: + result = GL_R8UI; + break; + case gpu::INT8: + result = GL_R8I; + break; + case gpu::NUINT8: + if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { + result = GL_SLUMINANCE8; + } else { + result = GL_R8; + } + break; + case gpu::NINT8: + result = GL_R8_SNORM; + break; + + default: + Q_UNREACHABLE(); + break; + } + break; + + case gpu::COMPRESSED_R: + result = GL_COMPRESSED_RED_RGTC1; + break; + + case gpu::R11G11B10: + // the type should be float + result = GL_R11F_G11F_B10F; + break; + + case gpu::DEPTH: + result = GL_DEPTH_COMPONENT32; + switch (dstFormat.getType()) { + case gpu::UINT32: + case gpu::INT32: + case gpu::NUINT32: + case gpu::NINT32: + result = GL_DEPTH_COMPONENT32; + break; + case gpu::FLOAT: + result = GL_DEPTH_COMPONENT32F; + break; + case gpu::UINT16: + case gpu::INT16: + case gpu::NUINT16: + case gpu::NINT16: + case gpu::HALF: + result = GL_DEPTH_COMPONENT16; + break; + case gpu::UINT8: + case gpu::INT8: + case gpu::NUINT8: + case gpu::NINT8: + result = GL_DEPTH_COMPONENT24; + break; + default: + Q_UNREACHABLE(); + break; + } + break; + + case gpu::DEPTH_STENCIL: + result = GL_DEPTH24_STENCIL8; + break; + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + case gpu::VEC2: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + result = GL_RG8; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC3: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + case gpu::RGBA: + result = GL_RGB8; + break; + case gpu::SRGB: + case gpu::SRGBA: + result = GL_SRGB8; // standard 2.2 gamma correction color + break; + case gpu::COMPRESSED_RGB: + result = GL_COMPRESSED_RGB; + break; + case gpu::COMPRESSED_SRGB: + result = GL_COMPRESSED_SRGB; + break; + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + + break; + } + + case gpu::VEC4: { + switch (dstFormat.getSemantic()) { + case gpu::RGB: + result = GL_RGB8; + break; + case gpu::RGBA: + switch (dstFormat.getType()) { + case gpu::UINT32: + result = GL_RGBA32UI; + break; + case gpu::INT32: + result = GL_RGBA32I; + break; + case gpu::FLOAT: + result = GL_RGBA32F; + break; + case gpu::UINT16: + result = GL_RGBA16UI; + break; + case gpu::INT16: + result = GL_RGBA16I; + break; + case gpu::NUINT16: + result = GL_RGBA16; + break; + case gpu::NINT16: + result = GL_RGBA16_SNORM; + break; + case gpu::HALF: + result = GL_RGBA16F; + break; + case gpu::UINT8: + result = GL_RGBA8UI; + break; + case gpu::INT8: + result = GL_RGBA8I; + break; + case gpu::NUINT8: + result = GL_RGBA8; + break; + case gpu::NINT8: + result = GL_RGBA8_SNORM; + break; + case gpu::NUINT32: + case gpu::NINT32: + case gpu::NUM_TYPES: // quiet compiler + Q_UNREACHABLE(); + } + break; + case gpu::SRGB: + result = GL_SRGB8; + break; + case gpu::SRGBA: + result = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color + break; + case gpu::COMPRESSED_RGBA: + result = GL_COMPRESSED_RGBA; + break; + case gpu::COMPRESSED_SRGBA: + result = GL_COMPRESSED_SRGB_ALPHA; + break; + + // FIXME: WE will want to support this later + /* + case gpu::COMPRESSED_BC3_RGBA: + result = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + case gpu::COMPRESSED_BC3_SRGBA: + result = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + break; + + case gpu::COMPRESSED_BC7_RGBA: + result = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + break; + case gpu::COMPRESSED_BC7_SRGBA: + result = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; + break; + */ + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + break; + } + + default: + qCDebug(gpugllogging) << "Unknown combination of texel format"; + } + return result; } GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h index bc3ec55066..94ded3dc23 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h @@ -21,7 +21,7 @@ public: static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) { return evalGLTexelFormat(dstFormat, dstFormat); } - static GLTexelFormat evalGLTexelFormatInternal(const Element& dstFormat); + static GLenum evalGLTexelFormatInternal(const Element& dstFormat); static GLTexelFormat evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat); }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 255258d762..d5a68bd99b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -118,34 +118,19 @@ float GLTexture::getMemoryPressure() { return (float)consumedGpuMemory / (float)availableTextureMemory; } -GLTexture::DownsampleSource::DownsampleSource(const std::weak_ptr& backend, GLTexture* oldTexture) : - _backend(backend), - _size(oldTexture ? oldTexture->_size : 0), - _texture(oldTexture ? oldTexture->takeOwnership() : 0), - _minMip(oldTexture ? oldTexture->_minMip : 0), - _maxMip(oldTexture ? oldTexture->_maxMip : 0) -{ -} -GLTexture::DownsampleSource::~DownsampleSource() { - if (_texture) { - auto backend = _backend.lock(); - if (backend) { - backend->releaseTexture(_texture, _size); - } - } -} - -GLTexture::GLTexture(const std::weak_ptr& backend, const gpu::Texture& texture, GLuint id, GLTexture* originalTexture, bool transferrable) : +// Create the texture and allocate storage +GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : GLObject(backend, texture, id), _storageStamp(texture.getStamp()), _target(getGLTextureType(texture)), _maxMip(texture.maxMip()), _minMip(texture.minMip()), _virtualSize(texture.evalTotalSize()), - _transferrable(transferrable), - _downsampleSource(backend, originalTexture) + _transferrable(transferrable) { + auto strongBackend = _backend.lock(); + strongBackend->recycle(); if (_transferrable) { uint16 mipCount = usedMipLevels(); _currentMaxMipCount = std::max(_currentMaxMipCount, mipCount); @@ -154,27 +139,9 @@ GLTexture::GLTexture(const std::weak_ptr& backend, const gpu::Texture } else { ++_textureCountByMips[mipCount]; } - } + } Backend::incrementTextureGPUCount(); Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize); -} - - -// Create the texture and allocate storage -GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : - GLTexture(backend, texture, id, nullptr, transferrable) -{ - // FIXME, do during allocation - //Backend::updateTextureGPUMemoryUsage(0, _size); - Backend::setGPUObject(texture, this); -} - -// Create the texture and copy from the original higher resolution version -GLTexture::GLTexture(const std::weak_ptr& backend, const gpu::Texture& texture, GLuint id, GLTexture* originalTexture) : - GLTexture(backend, texture, id, originalTexture, originalTexture->_transferrable) -{ - Q_ASSERT(_minMip >= originalTexture->_minMip); - // Set the GPU object last because that implicitly destroys the originalTexture object Backend::setGPUObject(texture, this); } @@ -196,6 +163,7 @@ GLTexture::~GLTexture() { auto backend = _backend.lock(); if (backend) { backend->releaseTexture(_id, _size); + backend->recycle(); } } Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); @@ -210,6 +178,28 @@ void GLTexture::createTexture() { }); } +void GLTexture::withPreservedTexture(std::function f) const { + GLint boundTex = -1; + switch (_target) { + case GL_TEXTURE_2D: + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); + break; + + case GL_TEXTURE_CUBE_MAP: + glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); + break; + + default: + qFatal("Unsupported texture type"); + } + (void)CHECK_GL_ERROR(); + + glBindTexture(_target, _texture); + f(); + glBindTexture(_target, boundTex); + (void)CHECK_GL_ERROR(); +} + void GLTexture::setSize(GLuint size) const { Backend::updateTextureGPUMemoryUsage(_size, size); const_cast(_size) = size; @@ -257,11 +247,6 @@ void GLTexture::postTransfer() { setSyncState(GLSyncState::Idle); ++_transferCount; - //// The public gltexture becaomes available - //_id = _privateTexture; - - _downsampleSource.reset(); - // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory switch (_gpuObject.getType()) { case Texture::TEX_2D: diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 742b223e36..e3bee4e103 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -73,14 +73,7 @@ public: return nullptr; } - // Do we need to reduce texture memory usage? - if (object->isOverMaxMemory() && texturePointer->incremementMinMip()) { - // WARNING, this code path will essentially `delete this`, - // so no dereferencing of this instance should be done past this point - object = new GLTextureType(backend.shared_from_this(), texture, object); - _textureTransferHelper->transferTexture(texturePointer); - return nullptr; - } + ((GLTexture*)object)->updateMips(); return object; } @@ -96,30 +89,23 @@ public: } else { object = Backend::getGPUObject(*texture); } + if (!object) { return 0; } - GLuint result = object->_id; + if (!shouldSync) { + return object->_id; + } // Don't return textures that are in transfer state - if (shouldSync) { - if ((object->getSyncState() != GLSyncState::Idle) || - // Don't return transferrable textures that have never completed transfer - (!object->_transferrable || 0 != object->_transferCount)) { - // Will be either 0 or the original texture being downsampled. - result = object->_downsampleSource._texture; - } + if ((object->getSyncState() != GLSyncState::Idle) || + // Don't return transferrable textures that have never completed transfer + (!object->_transferrable || 0 != object->_transferCount)) { + return 0; } - return result; - } - - // Used by derived classes and helpers to ensure the actual GL object exceeds the lifetime of `this` - GLuint takeOwnership() { - GLuint result = _id; - const_cast(_id) = 0; - return result; + return object->_id; } ~GLTexture(); @@ -128,25 +114,11 @@ public: const Stamp _storageStamp; const GLenum _target; const uint16 _maxMip; - const uint16 _minMip; + uint16 _minMip; const GLuint _virtualSize; // theoretical size as expected Stamp _contentStamp { 0 }; const bool _transferrable; Size _transferCount { 0 }; - - struct DownsampleSource { - using Pointer = std::shared_ptr; - DownsampleSource(const std::weak_ptr& backend) : _backend(backend), _size(0), _texture(0), _minMip(0), _maxMip(0) {} - DownsampleSource(const std::weak_ptr& backend, GLTexture* originalTexture); - ~DownsampleSource(); - void reset() const { const_cast(_texture) = 0; } - const std::weak_ptr& _backend; - const GLuint _size { 0 }; - const GLuint _texture { 0 }; - const uint16 _minMip { 0 }; - const uint16 _maxMip { 0 }; - } _downsampleSource; - GLuint size() const { return _size; } GLSyncState getSyncState() const { return _syncState; } @@ -160,7 +132,7 @@ public: bool isReady() const; // Execute any post-move operations that must occur only on the main thread - void postTransfer(); + virtual void postTransfer(); bool isOverMaxMemory() const; @@ -170,33 +142,34 @@ public: static const GLenum CUBE_FACE_LAYOUT[6]; static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS]; static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; -protected: - static const std::vector& getFaceTargets(GLenum textureType); - - static GLenum getGLTextureType(const Texture& texture); // Return a floating point value indicating how much of the allowed // texture memory we are currently consuming. A value of 0 indicates // no texture memory usage, while a value of 1 indicates all available / allowed memory // is consumed. A value above 1 indicates that there is a problem. static float getMemoryPressure(); +protected: + + static const std::vector& getFaceTargets(GLenum textureType); + + static GLenum getGLTextureType(const Texture& texture); const GLuint _size { 0 }; // true size as reported by the gl api std::atomic _syncState { GLSyncState::Idle }; GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable); - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, GLTexture* originalTexture); void setSyncState(GLSyncState syncState) { _syncState = syncState; } void createTexture(); - + + virtual void updateMips() {} virtual void allocateStorage() const = 0; virtual void updateSize() const = 0; virtual void syncSampler() const = 0; virtual void generateMips() const = 0; - virtual void withPreservedTexture(std::function f) const = 0; + virtual void withPreservedTexture(std::function f) const; protected: void setSize(GLuint size) const; @@ -207,9 +180,6 @@ protected: virtual void finishTransfer(); private: - - GLTexture(const std::weak_ptr& backend, const gpu::Texture& gpuTexture, GLuint id, GLTexture* originalTexture, bool transferrable); - friend class GLTextureTransferHelper; friend class GLBackend; }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp index ae8739bb3b..c66ea39f56 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp @@ -144,9 +144,9 @@ bool GLTextureTransferHelper::process() { } gltexture->finishTransfer(); - glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, gltexture->_id, 0); - glBlitNamedFramebuffer(_readFramebuffer, _drawFramebuffer, 0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); - clientWait(); + //glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, gltexture->_id, 0); + //glBlitNamedFramebuffer(_readFramebuffer, _drawFramebuffer, 0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); + //clientWait(); gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp(); gltexture->updateSize(); gltexture->setSyncState(gpu::gl::GLSyncState::Transferred); diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index c89024b7e8..5d03997b44 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -43,7 +43,6 @@ public: GLuint allocate(); public: GL41Texture(const std::weak_ptr& backend, const Texture& buffer, bool transferrable); - GL41Texture(const std::weak_ptr& backend, const Texture& buffer, GL41Texture* original); protected: void transferMip(uint16_t mipLevel, uint8_t face) const; @@ -52,7 +51,6 @@ public: void updateSize() const override; void syncSampler() const override; void generateMips() const override; - void withPreservedTexture(std::function f) const override; }; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 3d55802ec2..3fb729711d 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -40,30 +40,6 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texture, bool transf GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) : GLTexture(backend, texture, allocate(), transferrable) {} -GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture, GL41Texture* original) : GLTexture(backend, texture, allocate(), original) {} - -void GL41Texture::withPreservedTexture(std::function f) const { - GLint boundTex = -1; - switch (_target) { - case GL_TEXTURE_2D: - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); - break; - - case GL_TEXTURE_CUBE_MAP: - glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); - break; - - default: - qFatal("Unsupported texture type"); - } - (void)CHECK_GL_ERROR(); - - glBindTexture(_target, _texture); - f(); - glBindTexture(_target, boundTex); - (void)CHECK_GL_ERROR(); -} - void GL41Texture::generateMips() const { withPreservedTexture([&] { glGenerateMipmap(_target); @@ -147,35 +123,12 @@ void GL41Texture::startTransfer() { glBindTexture(_target, _id); (void)CHECK_GL_ERROR(); - if (_downsampleSource._texture) { - GLuint fbo { 0 }; - glGenFramebuffers(1, &fbo); - (void)CHECK_GL_ERROR(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - (void)CHECK_GL_ERROR(); - // Find the distance between the old min mip and the new one - uint16 mipOffset = _minMip - _downsampleSource._minMip; - for (uint16 i = _minMip; i <= _maxMip; ++i) { - uint16 targetMip = i - _minMip; - uint16 sourceMip = targetMip + mipOffset; - Vec3u dimensions = _gpuObject.evalMipDimensions(i); - for (GLenum target : getFaceTargets(_target)) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, _downsampleSource._texture, sourceMip); - (void)CHECK_GL_ERROR(); - glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, dimensions.x, dimensions.y); - (void)CHECK_GL_ERROR(); - } - } - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fbo); - } else { - // transfer pixels from each faces - uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1; - for (uint8_t f = 0; f < numFaces; f++) { - for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - if (_gpuObject.isStoredMipFaceAvailable(i, f)) { - transferMip(i, f); - } + // transfer pixels from each faces + uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1; + for (uint8_t f = 0; f < numFaces; f++) { + for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { + if (_gpuObject.isStoredMipFaceAvailable(i, f)) { + transferMip(i, f); } } } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index dad1f07ed3..bae6326e8f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -147,3 +147,8 @@ void GL45Backend::do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOf _stats._DSNumAPIDrawcalls++; (void)CHECK_GL_ERROR(); } + +void GL45Backend::recycle() const { + Parent::recycle(); + derezTextures(); +} diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index db297e77fd..c78e9888ca 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -18,29 +18,6 @@ namespace gpu { namespace gl45 { using namespace gpu::gl; -struct TransferState { - GLTexture& _texture; - GLenum _internalFormat { GL_RGBA8 }; - GLTexelFormat _texelFormat; - uint8_t _face { 0 }; - uint16_t _mipLevel { 0 }; - uint32_t _bytesPerLine { 0 }; - uint32_t _bytesPerPixel { 0 }; - uint32_t _bytesPerPage { 0 }; - GLuint _maxSparseLevel { 0 }; - - uvec3 _mipDimensions; - uvec3 _mipOffset; - uvec3 _pageSize; - const uint8_t* _srcPointer { nullptr }; - uvec3 currentPageSize() const; - void updateSparse(); - void updateMip(); - void populatePage(std::vector& dest); - bool increment(); - TransferState(GLTexture& texture); -}; - class GL45Backend : public GLBackend { using Parent = GLBackend; // Context Backend static interface required @@ -55,12 +32,47 @@ public: static GLuint allocate(const Texture& texture); public: GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable); - GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLTexture* original); ~GL45Texture(); + void postTransfer() override; + + struct SparseInfo { + GL45Texture& _texture; + uvec3 _pageDimensions; + GLuint _maxSparseLevel { 0 }; + uint32_t _maxPages { 0 }; + uint32_t _pageBytes { 0 }; + SparseInfo(GL45Texture& texture); + void update(); + uvec3 getPageCounts(const uvec3& dimensions) const; + uint32_t getPageCount(const uvec3& dimensions) const; + }; + + struct TransferState { + GL45Texture& _texture; + GLTexelFormat _texelFormat; + uint8_t _face { 0 }; + uint16_t _mipLevel { 0 }; + uint32_t _bytesPerLine { 0 }; + uint32_t _bytesPerPixel { 0 }; + uint32_t _bytesPerPage { 0 }; + + uvec3 _mipDimensions; + uvec3 _mipOffset; + const uint8_t* _srcPointer { nullptr }; + uvec3 currentPageSize() const; + void updateMip(); + void populatePage(std::vector& dest); + bool increment(); + TransferState(GL45Texture& texture); + }; + protected: + void updateMips() override; + void stripToMip(uint16_t newMinMip); void startTransfer() override; bool continueTransfer() override; + void finishTransfer() override; void incrementalTransfer(const uvec3& size, const gpu::Texture::PixelsPointer& mip, std::function f) const; void transferMip(uint16_t mipLevel, uint8_t face = 0) const; void allocateMip(uint16_t mipLevel, uint8_t face = 0) const; @@ -69,12 +81,21 @@ public: void syncSampler() const override; void generateMips() const override; void withPreservedTexture(std::function f) const override; - + void derez(); + SparseInfo _sparseInfo; TransferState _transferState; + uint32_t _allocatedPages { 0 }; + uint32_t _lastMipAllocatedPages { 0 }; + bool _sparse { false }; + + friend class GL45Backend; }; protected: + void recycle() const override; + void derezTextures() const; + GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index b511ed7811..a1c9129887 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -24,28 +24,66 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; +#ifdef THREADED_TEXTURE_TRANSFER #define SPARSE_TEXTURES 1 +#else +#define SPARSE_TEXTURES 0 +#endif // Allocate 1 MB of buffer space for paged transfers #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) using GL45Texture = GL45Backend::GL45Texture; +static std::map> texturesByMipCounts; + GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { return GL45Texture::sync(*this, texture, transfer); } -TransferState::TransferState(GLTexture& texture) : _texture(texture) { +using SparseInfo = GL45Backend::GL45Texture::SparseInfo; + +SparseInfo::SparseInfo(GL45Texture& texture) + : _texture(texture) { } -void TransferState::updateSparse() { +void SparseInfo::update() { glGetTextureParameterIuiv(_texture._id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel); - _internalFormat = gl::GLTexelFormat::evalGLTexelFormat(_texture._gpuObject.getTexelFormat(), _texture._gpuObject.getTexelFormat()).internalFormat; + GLenum internalFormat = gl::GLTexelFormat::evalGLTexelFormat(_texture._gpuObject.getTexelFormat(), _texture._gpuObject.getTexelFormat()).internalFormat; ivec3 pageSize; - glGetInternalformativ(_texture._target, _internalFormat, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &pageSize.x); - glGetInternalformativ(_texture._target, _internalFormat, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &pageSize.y); - glGetInternalformativ(_texture._target, _internalFormat, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &pageSize.z); - _pageSize = uvec3(pageSize); + glGetInternalformativ(_texture._target, internalFormat, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &pageSize.x); + glGetInternalformativ(_texture._target, internalFormat, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &pageSize.y); + glGetInternalformativ(_texture._target, internalFormat, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &pageSize.z); + _pageDimensions = uvec3(pageSize); + _pageBytes = _texture._gpuObject.getTexelFormat().getSize(); + _pageBytes *= _pageDimensions.x * _pageDimensions.y * _pageDimensions.z; + + for (uint16_t mipLevel = 0; mipLevel <= _maxSparseLevel; ++mipLevel) { + auto mipDimensions = _texture._gpuObject.evalMipDimensions(mipLevel); + auto mipPageCount = getPageCount(mipDimensions); + _maxPages += mipPageCount; + } + if (_texture._target == GL_TEXTURE_CUBE_MAP) { + _maxPages *= GLTexture::CUBE_NUM_FACES; + } +} + +uvec3 SparseInfo::getPageCounts(const uvec3& dimensions) const { + auto result = (dimensions / _pageDimensions) + + glm::clamp(dimensions % _pageDimensions, glm::uvec3(0), glm::uvec3(1)); + return result; + + +} + +uint32_t SparseInfo::getPageCount(const uvec3& dimensions) const { + auto pageCounts = getPageCounts(dimensions); + return pageCounts.x * pageCounts.y * pageCounts.z; +} + +using TransferState = GL45Backend::GL45Texture::TransferState; + +TransferState::TransferState(GL45Texture& texture) : _texture(texture) { } void TransferState::updateMip() { @@ -64,18 +102,19 @@ void TransferState::updateMip() { } bool TransferState::increment() { - if ((_mipOffset.x + _pageSize.x) < _mipDimensions.x) { - _mipOffset.x += _pageSize.x; + const SparseInfo& sparse = _texture._sparseInfo; + if ((_mipOffset.x + sparse._pageDimensions.x) < _mipDimensions.x) { + _mipOffset.x += sparse._pageDimensions.x; return true; } - if ((_mipOffset.y + _pageSize.y) < _mipDimensions.y) { + if ((_mipOffset.y + sparse._pageDimensions.y) < _mipDimensions.y) { _mipOffset.x = 0; - _mipOffset.y += _pageSize.y; + _mipOffset.y += sparse._pageDimensions.y; return true; } - if (_mipOffset.z + _pageSize.z < _mipDimensions.z) { + if (_mipOffset.z + sparse._pageDimensions.z < _mipDimensions.z) { _mipOffset.x = 0; _mipOffset.y = 0; ++_mipOffset.z; @@ -125,7 +164,7 @@ void TransferState::populatePage(std::vector& buffer) { } uvec3 TransferState::currentPageSize() const { - return glm::clamp(_mipDimensions - _mipOffset, uvec3(1), _pageSize); + return glm::clamp(_mipDimensions - _mipOffset, uvec3(1), _texture._sparseInfo._pageDimensions); } GLuint GL45Texture::allocate(const Texture& texture) { @@ -139,25 +178,44 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { } GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) - : GLTexture(backend, texture, allocate(texture), transferrable), _transferState(*this) { - + : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) { #if SPARSE_TEXTURES - if (transferrable) { + _sparse = _transferrable; +#endif + if (_sparse) { glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); } -#endif } -GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, GLTexture* original) - : GLTexture(backend, texture, allocate(texture), original), _transferState(*this) { } - GL45Texture::~GL45Texture() { - // FIXME do we need to explicitly deallocate the virtual memory here? - //if (_transferrable) { - // for (uint16_t mipLevel = 0; mipLevel < usedMipLevels(); ++i) { - // glTexturePageCommitmentEXT(_id, mipLevel, offset.x, offset.y, offset.z, size.x, size.y, size.z, GL_TRUE); - // } - //} + if (_sparse) { + auto mipLevels = usedMipLevels(); + if (texturesByMipCounts.count(mipLevels)) { + auto& textures = texturesByMipCounts[mipLevels]; + textures.erase(this); + if (textures.empty()) { + texturesByMipCounts.erase(mipLevels); + } + } + + auto originalAllocatedPages = _allocatedPages; + auto maxSparseMip = std::min(_maxMip, _sparseInfo._maxSparseLevel); + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { + auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions); + for (uint8_t face = 0; face < maxFace; ++face) { + glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, mipDimensions.x, mipDimensions.y, mipDimensions.z, GL_FALSE); + assert(deallocatedPages <= _allocatedPages); + _allocatedPages -= deallocatedPages; + } + } + if (0 != _allocatedPages) { + auto maxSize = _gpuObject.evalMipDimensions(0); + qDebug() << "Allocated pages remaining " << _id << " " << _allocatedPages; + qDebug() << originalAllocatedPages; + } + } } void GL45Texture::withPreservedTexture(std::function f) const { @@ -183,19 +241,20 @@ void GL45Texture::allocateStorage() const { } void GL45Texture::updateSize() const { - setSize(_virtualSize); - if (!_id) { - return; - } - if (_gpuObject.getTexelFormat().isCompressed()) { qFatal("Compressed textures not yet supported"); } + + if (_transferrable) { + setSize(_allocatedPages * _sparseInfo._pageBytes); + } else { + setSize(_virtualSize); + } } void GL45Texture::startTransfer() { Parent::startTransfer(); - _transferState.updateSparse(); + _sparseInfo.update(); _transferState.updateMip(); } @@ -207,14 +266,20 @@ bool GL45Texture::continueTransfer() { uvec3 pageSize = _transferState.currentPageSize(); uvec3 offset = _transferState._mipOffset; -#if SPARSE_TEXTURES - if (_transferState._mipLevel <= _transferState._maxSparseLevel) { - glTexturePageCommitmentEXT(_id, _transferState._mipLevel, + // FIXME we should be using the DSA for all of this + if (_sparse && _transferState._mipLevel <= _sparseInfo._maxSparseLevel) { + if (_allocatedPages > _sparseInfo._maxPages) { + qDebug() << "Exceeded max page allocation!"; + } + glBindTexture(_target, _id); + // FIXME we should be using glTexturePageCommitmentEXT, but for some reason it causes out of memory errors. + // Either I'm not understanding how it should work or there's a driver bug. + glTexPageCommitmentARB(_target, _transferState._mipLevel, offset.x, offset.y, _transferState._face, pageSize.x, pageSize.y, pageSize.z, GL_TRUE); + ++_allocatedPages; } -#endif if (_transferState._srcPointer) { // Transfer the mip data @@ -236,16 +301,31 @@ bool GL45Texture::continueTransfer() { } serverWait(); - return _transferState.increment(); + auto currentMip = _transferState._mipLevel; + auto result = _transferState.increment(); + if (_transferState._mipLevel != currentMip && currentMip <= _sparseInfo._maxSparseLevel) { + auto mipDimensions = _gpuObject.evalMipDimensions(currentMip); + auto mipExpectedPages = _sparseInfo.getPageCount(mipDimensions); + auto newPages = _allocatedPages - _lastMipAllocatedPages; + if (newPages != mipExpectedPages) { + qWarning() << "Unexpected page allocation size... " << newPages << " " << mipExpectedPages; + } + _lastMipAllocatedPages = _allocatedPages; + } + return result; } -void GL45Backend::GL45Texture::syncSampler() const { +void GL45Texture::finishTransfer() { + Parent::finishTransfer(); +} + +void GL45Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); const auto& fm = FILTER_MODES[sampler.getFilter()]; glTextureParameteri(_id, GL_TEXTURE_MIN_FILTER, fm.minFilter); glTextureParameteri(_id, GL_TEXTURE_MAG_FILTER, fm.magFilter); - + if (sampler.doComparison()) { glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); glTextureParameteri(_id, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); @@ -257,9 +337,149 @@ void GL45Backend::GL45Texture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, (uint16)sampler.getMipOffset()); + auto baseMip = std::max(sampler.getMipOffset(), _minMip); + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); } +void GL45Texture::postTransfer() { + Parent::postTransfer(); + if (_transferrable) { + auto mipLevels = usedMipLevels(); + if (mipLevels > 1 && _minMip < _sparseInfo._maxSparseLevel) { + auto& textureMap = texturesByMipCounts; + texturesByMipCounts[mipLevels].insert(this); + } + } +} + +void GL45Texture::stripToMip(uint16_t newMinMip) { + if (!_sparse) { + return; + } + + if (newMinMip < _minMip) { + qWarning() << "Cannot decrease the min mip"; + return; + } + + if (newMinMip >= _sparseInfo._maxSparseLevel) { + qWarning() << "Cannot increase the min mip into the mip tail"; + return; + } + + auto mipLevels = usedMipLevels(); + assert(0 != texturesByMipCounts.count(mipLevels)); + assert(0 != texturesByMipCounts[mipLevels].count(this)); + texturesByMipCounts[mipLevels].erase(this); + if (texturesByMipCounts[mipLevels].empty()) { + texturesByMipCounts.erase(mipLevels); + } + + // FIXME this shouldn't be necessary should it? +#if 1 + glGenerateTextureMipmap(_id); +#else + static GLuint framebuffers[2] = { 0, 0 }; + static std::once_flag initFramebuffers; + std::call_once(initFramebuffers, [&] { + glCreateFramebuffers(2, framebuffers); + }); + auto readSize = _gpuObject.evalMipDimensions(_minMip); + auto drawSize = _gpuObject.evalMipDimensions(newMinMip); + glNamedFramebufferTexture(framebuffers[0], GL_COLOR_ATTACHMENT0, _id, _minMip); + glNamedFramebufferTexture(framebuffers[1], GL_COLOR_ATTACHMENT0, _id, newMinMip); + glBlitNamedFramebuffer(framebuffers[0], framebuffers[1], + 0, 0, readSize.x, readSize.y, + 0, 0, drawSize.x, drawSize.y, + GL_COLOR_BUFFER_BIT, GL_LINEAR); +#endif + + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions); + for (uint8_t face = 0; face < maxFace; ++face) { + glTexturePageCommitmentEXT(_id, mip, + 0, 0, face, + mipDimensions.x, mipDimensions.y, mipDimensions.z, + GL_FALSE); + assert(deallocatedPages < _allocatedPages); + _allocatedPages -= deallocatedPages; + } + } + + _minMip = newMinMip; + // Re-sync the sampler to force access to the new mip level + syncSampler(); + size_t oldSize = _size; + updateSize(); + size_t newSize = _size; + if (newSize > oldSize) { + qDebug() << "WTF"; + qDebug() << "\told size " << oldSize; + qDebug() << "\tnew size " << newSize; + } + + // Re-insert into the texture-by-mips map if appropriate + mipLevels = usedMipLevels(); + if (mipLevels > 1 && _minMip < _sparseInfo._maxSparseLevel) { + auto& textureMap = texturesByMipCounts; + texturesByMipCounts[mipLevels].insert(this); + } +} + +void GL45Texture::updateMips() { + if (!_sparse) { + return; + } + bool modified = false; + auto newMinMip = std::min(_gpuObject.minMip(), _sparseInfo._maxSparseLevel); + if (_minMip < newMinMip) { + stripToMip(newMinMip); + } +} + +void GL45Texture::derez() { + if (!_sparse) { + return; + } + assert(_minMip < _sparseInfo._maxSparseLevel); + assert(_minMip < _maxMip); + assert(_transferrable); + stripToMip(_minMip + 1); +} + +void GL45Backend::derezTextures() const { + if (GLTexture::getMemoryPressure() < 1.0f) { + return; + } + qDebug() << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage(); + qDebug() << "Used texture memory " << Context::getTextureGPUMemoryUsage(); + + if (texturesByMipCounts.empty()) { + qDebug() << "No available textures to derez"; + return; + } + + auto mipLevel = texturesByMipCounts.rbegin()->first; + if (mipLevel <= 1) { + qDebug() << "Max mip levels " << mipLevel; + return; + } + + auto& textureMap = texturesByMipCounts; + auto newMipLevel = mipLevel - 1; + qDebug() << "Derez a texture"; + GL45Texture* targetTexture = nullptr; + { + auto& textures = texturesByMipCounts[mipLevel]; + assert(!textures.empty()); + targetTexture = *textures.begin(); + } + targetTexture->derez(); + + qDebug() << "New Used texture memory " << Context::getTextureGPUMemoryUsage(); +} diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 32a6f4c323..5bb983c7f9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -35,8 +35,7 @@ #include "ModelNetworkingLogging.h" TextureCache::TextureCache() { - const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; - setUnusedResourceCacheSize(TEXTURE_DEFAULT_UNUSED_MAX_SIZE); + setUnusedResourceCacheSize(0); setObjectName("TextureCache"); // Expose enum Type to JS/QML via properties diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 19bec7767d..267d192c7f 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -666,6 +666,14 @@ protected: toggleCulling(); return; + case Qt::Key_Home: + gpu::Texture::setAllowedGPUMemoryUsage(0); + return; + + case Qt::Key_End: + gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(256)); + return; + default: break; @@ -776,10 +784,11 @@ private: }; void updateText() { - QString title = QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4") + QString title = QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4 Max GPU %5") .arg(_fps).arg(_cullingEnabled) .arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2)) - .arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2)); + .arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2)) + .arg(toHumanSize(gpu::Texture::getAllowedGPUMemoryUsage(), 2)); setTitle(title); #if 0 { @@ -1111,7 +1120,6 @@ int main(int argc, char** argv) { QCoreApplication::setApplicationName("RenderPerf"); QCoreApplication::setOrganizationName("High Fidelity"); QCoreApplication::setOrganizationDomain("highfidelity.com"); - qInstallMessageHandler(messageHandler); QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow::setup(); diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp index a1c5f74777..ca0416baeb 100644 --- a/tests/render-texture-load/src/main.cpp +++ b/tests/render-texture-load/src/main.cpp @@ -297,8 +297,6 @@ public: }; QTestWindow() { - - _currentTexture = _textures.end(); { QStringList stringList; QFile textFile(DATA_DIR.path() + "/loads.txt"); @@ -318,12 +316,13 @@ public: QString timeStr = s.left(index); auto time = timeStr.toUInt(); QString path = DATA_DIR.path() + "/" + s.right(s.length() - index).trimmed(); - qDebug() << "Path " << path; if (!QFileInfo(path).exists()) { continue; } - _textureLoads.push({ time, path, s }); + qDebug() << "Path " << path; + _texturesFiles.push_back({ time, path, s }); } + _textures.resize(_texturesFiles.size()); } installEventFilter(this); @@ -383,6 +382,33 @@ protected: } void keyPressEvent(QKeyEvent* event) override { + switch (event->key()) { + case Qt::Key_Left: + prevTexture(); + break; + case Qt::Key_Right: + nextTexture(); + break; + case Qt::Key_Return: + reportMemory(); + break; + case Qt::Key_PageDown: + derezTexture(); + break; + case Qt::Key_Home: + unloadAll(); + break; + case Qt::Key_End: + loadAll(); + break; + case Qt::Key_Down: + loadTexture(); + break; + case Qt::Key_Up: + unloadTexture(); + break; + } + QWindow::keyPressEvent(event); } void keyReleaseEvent(QKeyEvent* event) override { @@ -395,10 +421,80 @@ protected: resizeWindow(ev->size()); } + void nextTexture() { + if (_textures.empty()) { + return; + } + auto textureCount = _textures.size(); + _currentTextureIndex = (_currentTextureIndex + 1) % textureCount; + loadTexture(); + } + + void prevTexture() { + if (_textures.empty()) { + return; + } + auto textureCount = _textures.size(); + _currentTextureIndex = (_currentTextureIndex + textureCount - 1) % textureCount; + loadTexture(); + } + + void reportMemory() { + static GLint lastMemory = 0; + GLint availableMem; + glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &availableMem); + qDebug() << "Memory available " << availableMem; + if (lastMemory != 0) { + qDebug() << "Delta " << availableMem - lastMemory; + } + lastMemory = availableMem; + } + + void derezTexture() { + if (!_textures[_currentTextureIndex]) { + return; + } + auto texture = _textures[_currentTextureIndex]; + texture->setMinMip(texture->minMip() + 1); + } + + void loadTexture() { + if (_textures[_currentTextureIndex]) { + return; + } + auto file = _texturesFiles[_currentTextureIndex].file; + qDebug() << "Loading texture " << file; + _textures[_currentTextureIndex] = DependencyManager::get()->getImageTexture(file); + } + + void unloadTexture() { + if (_textures.empty()) { + return; + } + _textures[_currentTextureIndex].reset(); + } + + void loadAll() { + for (auto i = 0; i < _texturesFiles.size(); ++i) { + if (_textures[i]) { + continue; + } + auto file = _texturesFiles[i].file; + qDebug() << "Loading texture " << file; + _textures[i] = DependencyManager::get()->getImageTexture(file); + } + } + + void unloadAll() { + for (auto& texture : _textures) { + texture.reset(); + } + } + private: - std::queue _textureLoads; - std::list _textures; - std::list::iterator _currentTexture; + size_t _currentTextureIndex { 0 }; + std::vector _texturesFiles; + std::vector _textures; uint16_t _fps; gpu::PipelinePointer _simplePipeline; @@ -438,7 +534,8 @@ private: auto now = usecTimestampNow(); static auto last = now; auto delta = (now - last) / USECS_PER_MSEC; - if (!_textureLoads.empty()) { +#if 0 + if (!_textures.empty()) { const auto& front = _textureLoads.front(); if (delta >= front.time) { QFileInfo fileInfo(front.file); @@ -456,6 +553,7 @@ private: } } } +#endif } void render() { @@ -474,14 +572,8 @@ private: auto vpsize = framebuffer->getSize(); auto vppos = ivec2(0); batch.setViewportTransform(ivec4(vppos, vpsize)); - if (_currentTexture != _textures.end()) { - ++_currentTexture; - } - if (_currentTexture == _textures.end()) { - _currentTexture = _textures.begin(); - } - if (_currentTexture != _textures.end()) { - batch.setResourceTexture(0, *_currentTexture); + if (!_textures.empty()) { + batch.setResourceTexture(0, _textures[_currentTextureIndex]); } batch.setPipeline(_simplePipeline); batch.draw(gpu::TRIANGLE_STRIP, 4); @@ -564,7 +656,6 @@ int main(int argc, char** argv) { }).waitForDownload(); } - QTestWindow::setup(); QTestWindow window; app.exec(); From 93b0364f73827c877ff1254efb322ba489c3baf5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Sep 2016 10:17:20 -0700 Subject: [PATCH 20/59] Working on sparse stability --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 38 --------- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 2 - .../src/gpu/gl45/GL45BackendTexture.cpp | 82 ++++++++----------- tests/render-perf/src/main.cpp | 34 -------- 4 files changed, 34 insertions(+), 122 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index d5a68bd99b..a04ae13b05 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -17,8 +17,6 @@ using namespace gpu; using namespace gpu::gl; std::shared_ptr GLTexture::_textureTransferHelper; -static std::map _textureCountByMips; -static uint16 _currentMaxMipCount { 0 }; // FIXME placeholder for texture memory over-use #define DEFAULT_MAX_MEMORY_MB 256 @@ -131,34 +129,12 @@ GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& tex { auto strongBackend = _backend.lock(); strongBackend->recycle(); - if (_transferrable) { - uint16 mipCount = usedMipLevels(); - _currentMaxMipCount = std::max(_currentMaxMipCount, mipCount); - if (!_textureCountByMips.count(mipCount)) { - _textureCountByMips[mipCount] = 1; - } else { - ++_textureCountByMips[mipCount]; - } - } Backend::incrementTextureGPUCount(); Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize); Backend::setGPUObject(texture, this); } GLTexture::~GLTexture() { - if (_transferrable) { - uint16 mipCount = usedMipLevels(); - Q_ASSERT(_textureCountByMips.count(mipCount)); - auto& numTexturesForMipCount = _textureCountByMips[mipCount]; - --numTexturesForMipCount; - if (0 == numTexturesForMipCount) { - _textureCountByMips.erase(mipCount); - if (mipCount == _currentMaxMipCount) { - _currentMaxMipCount = (_textureCountByMips.empty() ? 0 : _textureCountByMips.rbegin()->first); - } - } - } - if (_id) { auto backend = _backend.lock(); if (backend) { @@ -213,20 +189,6 @@ bool GLTexture::isOutdated() const { return GLSyncState::Idle == _syncState && _contentStamp < _gpuObject.getDataStamp(); } -bool GLTexture::isOverMaxMemory() const { - // FIXME switch to using the max mip count used from the previous frame - if (usedMipLevels() < _currentMaxMipCount) { - return false; - } - Q_ASSERT(usedMipLevels() == _currentMaxMipCount); - - if (getMemoryPressure() < 1.0f) { - return false; - } - - return true; -} - bool GLTexture::isReady() const { // If we have an invalid texture, we're never ready if (isInvalid()) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index e3bee4e103..a54e45989d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -134,8 +134,6 @@ public: // Execute any post-move operations that must occur only on the main thread virtual void postTransfer(); - bool isOverMaxMemory() const; - uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; } static const size_t CUBE_NUM_FACES = 6; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index a1c9129887..889229c1aa 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "../gl/GLTexelFormat.h" @@ -24,11 +25,8 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -#ifdef THREADED_TEXTURE_TRANSFER -#define SPARSE_TEXTURES 1 -#else -#define SPARSE_TEXTURES 0 -#endif +static const QString DEBUG_FLAG("HIFI_ENABLE_SPARSE_TEXTURES"); +static bool enableSparseTextures = true; // QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); // Allocate 1 MB of buffer space for paged transfers #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) @@ -36,6 +34,7 @@ using namespace gpu::gl45; using GL45Texture = GL45Backend::GL45Texture; static std::map> texturesByMipCounts; +static Mutex texturesByMipCountsMutex; GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { return GL45Texture::sync(*this, texture, transfer); @@ -179,9 +178,9 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) { -#if SPARSE_TEXTURES - _sparse = _transferrable; -#endif + + _sparse = _transferrable && (_target != GL_TEXTURE_CUBE_MAP); + if (_sparse) { glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); } @@ -190,11 +189,15 @@ GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& GL45Texture::~GL45Texture() { if (_sparse) { auto mipLevels = usedMipLevels(); - if (texturesByMipCounts.count(mipLevels)) { - auto& textures = texturesByMipCounts[mipLevels]; - textures.erase(this); - if (textures.empty()) { - texturesByMipCounts.erase(mipLevels); + + { + Lock lock(texturesByMipCountsMutex); + if (texturesByMipCounts.count(mipLevels)) { + auto& textures = texturesByMipCounts[mipLevels]; + textures.erase(this); + if (textures.empty()) { + texturesByMipCounts.erase(mipLevels); + } } } @@ -271,10 +274,10 @@ bool GL45Texture::continueTransfer() { if (_allocatedPages > _sparseInfo._maxPages) { qDebug() << "Exceeded max page allocation!"; } - glBindTexture(_target, _id); + //glBindTexture(_target, _id); // FIXME we should be using glTexturePageCommitmentEXT, but for some reason it causes out of memory errors. // Either I'm not understanding how it should work or there's a driver bug. - glTexPageCommitmentARB(_target, _transferState._mipLevel, + glTexturePageCommitmentEXT(_id, _transferState._mipLevel, offset.x, offset.y, _transferState._face, pageSize.x, pageSize.y, pageSize.z, GL_TRUE); @@ -303,7 +306,7 @@ bool GL45Texture::continueTransfer() { serverWait(); auto currentMip = _transferState._mipLevel; auto result = _transferState.increment(); - if (_transferState._mipLevel != currentMip && currentMip <= _sparseInfo._maxSparseLevel) { + if (_sparse && _transferState._mipLevel != currentMip && currentMip <= _sparseInfo._maxSparseLevel) { auto mipDimensions = _gpuObject.evalMipDimensions(currentMip); auto mipExpectedPages = _sparseInfo.getPageCount(mipDimensions); auto newPages = _allocatedPages - _lastMipAllocatedPages; @@ -346,10 +349,10 @@ void GL45Texture::syncSampler() const { void GL45Texture::postTransfer() { Parent::postTransfer(); - if (_transferrable) { + if (_sparse) { auto mipLevels = usedMipLevels(); if (mipLevels > 1 && _minMip < _sparseInfo._maxSparseLevel) { - auto& textureMap = texturesByMipCounts; + Lock lock(texturesByMipCountsMutex); texturesByMipCounts[mipLevels].insert(this); } } @@ -371,32 +374,16 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { } auto mipLevels = usedMipLevels(); - assert(0 != texturesByMipCounts.count(mipLevels)); - assert(0 != texturesByMipCounts[mipLevels].count(this)); - texturesByMipCounts[mipLevels].erase(this); - if (texturesByMipCounts[mipLevels].empty()) { - texturesByMipCounts.erase(mipLevels); + { + Lock lock(texturesByMipCountsMutex); + assert(0 != texturesByMipCounts.count(mipLevels)); + assert(0 != texturesByMipCounts[mipLevels].count(this)); + texturesByMipCounts[mipLevels].erase(this); + if (texturesByMipCounts[mipLevels].empty()) { + texturesByMipCounts.erase(mipLevels); + } } - // FIXME this shouldn't be necessary should it? -#if 1 - glGenerateTextureMipmap(_id); -#else - static GLuint framebuffers[2] = { 0, 0 }; - static std::once_flag initFramebuffers; - std::call_once(initFramebuffers, [&] { - glCreateFramebuffers(2, framebuffers); - }); - auto readSize = _gpuObject.evalMipDimensions(_minMip); - auto drawSize = _gpuObject.evalMipDimensions(newMinMip); - glNamedFramebufferTexture(framebuffers[0], GL_COLOR_ATTACHMENT0, _id, _minMip); - glNamedFramebufferTexture(framebuffers[1], GL_COLOR_ATTACHMENT0, _id, newMinMip); - glBlitNamedFramebuffer(framebuffers[0], framebuffers[1], - 0, 0, readSize.x, readSize.y, - 0, 0, drawSize.x, drawSize.y, - GL_COLOR_BUFFER_BIT, GL_LINEAR); -#endif - uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { auto mipDimensions = _gpuObject.evalMipDimensions(mip); @@ -425,8 +412,8 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { // Re-insert into the texture-by-mips map if appropriate mipLevels = usedMipLevels(); - if (mipLevels > 1 && _minMip < _sparseInfo._maxSparseLevel) { - auto& textureMap = texturesByMipCounts; + if (_sparse && mipLevels > 1 && _minMip < _sparseInfo._maxSparseLevel) { + Lock lock(texturesByMipCountsMutex); texturesByMipCounts[mipLevels].insert(this); } } @@ -443,9 +430,7 @@ void GL45Texture::updateMips() { } void GL45Texture::derez() { - if (!_sparse) { - return; - } + assert(_sparse); assert(_minMip < _sparseInfo._maxSparseLevel); assert(_minMip < _maxMip); assert(_transferrable); @@ -459,6 +444,7 @@ void GL45Backend::derezTextures() const { qDebug() << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage(); qDebug() << "Used texture memory " << Context::getTextureGPUMemoryUsage(); + Lock lock(texturesByMipCountsMutex); if (texturesByMipCounts.empty()) { qDebug() << "No available textures to derez"; return; @@ -479,7 +465,7 @@ void GL45Backend::derezTextures() const { assert(!textures.empty()); targetTexture = *textures.begin(); } + lock.unlock(); targetTexture->derez(); - qDebug() << "New Used texture memory " << Context::getTextureGPUMemoryUsage(); } diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 267d192c7f..bbe1c33bb1 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -286,10 +286,6 @@ public: } _context.makeCurrent(); - glewExperimental = true; - glewInit(); - glGetError(); - _frameTimes.resize(FRAME_TIME_BUFFER_SIZE, 0); { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); @@ -552,36 +548,6 @@ public: _renderThread.initialize(this, _initContext); _initContext.makeCurrent(); -#if 0 - glfwInit(); - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - resizeWindow(QSize(800, 600)); - _window = glfwCreateWindow(_size.width(), _size.height(), "Window Title", NULL, NULL); - if (!_window) { - throw std::runtime_error("Could not create window"); - } - - glfwSetWindowUserPointer(_window, this); - glfwSetKeyCallback(_window, KeyboardHandler); - glfwSetMouseButtonCallback(_window, MouseHandler); - glfwSetCursorPosCallback(_window, MouseMoveHandler); - glfwSetWindowCloseCallback(_window, CloseHandler); - glfwSetFramebufferSizeCallback(_window, FramebufferSizeHandler); - glfwSetScrollCallback(_window, MouseScrollHandler); - - - glfwMakeContextCurrent(_window); - GLDebug::setupLogger(this); -#endif - -#ifdef Q_OS_WIN - //wglSwapIntervalEXT(0); -#endif - // FIXME use a wait condition QThread::msleep(1000); _renderThread.submitFrame(gpu::FramePointer()); From a55669b83e783057c793714e17f4d99e6bfd5b95 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Sep 2016 11:57:25 -0700 Subject: [PATCH 21/59] Only enable sparse textures by env variable --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 889229c1aa..e198417db4 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -26,7 +26,7 @@ using namespace gpu::gl; using namespace gpu::gl45; static const QString DEBUG_FLAG("HIFI_ENABLE_SPARSE_TEXTURES"); -static bool enableSparseTextures = true; // QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); +static bool enableSparseTextures = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); // Allocate 1 MB of buffer space for paged transfers #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) @@ -179,7 +179,7 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) { - _sparse = _transferrable && (_target != GL_TEXTURE_CUBE_MAP); + _sparse = enableSparseTextures && _transferrable && (_target != GL_TEXTURE_CUBE_MAP); if (_sparse) { glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); From 151df325194353f4ae19c837b1785dd7779f34a4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Sep 2016 01:17:23 -0700 Subject: [PATCH 22/59] Check sparse texture support before enabling --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 1 + libraries/gpu-gl/src/gpu/gl/GLTexture.h | 1 + .../src/gpu/gl45/GL45BackendTexture.cpp | 20 ++++++++++--------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index a04ae13b05..a279bec11c 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -122,6 +122,7 @@ GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& tex GLObject(backend, texture, id), _storageStamp(texture.getStamp()), _target(getGLTextureType(texture)), + _internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())), _maxMip(texture.maxMip()), _minMip(texture.minMip()), _virtualSize(texture.evalTotalSize()), diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index a54e45989d..fa893ae52a 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -113,6 +113,7 @@ public: const GLuint& _texture { _id }; const Stamp _storageStamp; const GLenum _target; + const GLenum _internalFormat; const uint16 _maxMip; uint16 _minMip; const GLuint _virtualSize; // theoretical size as expected diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index e198417db4..5dd356349d 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -179,10 +179,13 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) { - _sparse = enableSparseTextures && _transferrable && (_target != GL_TEXTURE_CUBE_MAP); - - if (_sparse) { - glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); + if (enableSparseTextures && _transferrable && (_target != GL_TEXTURE_CUBE_MAP)) { + GLint pageSizesCount = 0; + glGetInternalformativ(_target, _internalFormat, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &pageSizesCount); + if (pageSizesCount > 0) { + _sparse = true; + glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); + } } } @@ -231,15 +234,14 @@ void GL45Texture::generateMips() const { } void GL45Texture::allocateStorage() const { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); - glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); - glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); if (_gpuObject.getTexelFormat().isCompressed()) { qFatal("Compressed textures not yet supported"); } + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); + glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); // Get the dimensions, accounting for the downgrade level Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip); - glTextureStorage2D(_id, usedMipLevels(), texelFormat.internalFormat, dimensions.x, dimensions.y); + glTextureStorage2D(_id, usedMipLevels(), _internalFormat, dimensions.x, dimensions.y); (void)CHECK_GL_ERROR(); } @@ -368,7 +370,7 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { return; } - if (newMinMip >= _sparseInfo._maxSparseLevel) { + if (newMinMip > _sparseInfo._maxSparseLevel) { qWarning() << "Cannot increase the min mip into the mip tail"; return; } From 2b5ed86a62d43cc91236ebe1c8bcfa9e88327aff Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Sep 2016 01:21:41 -0700 Subject: [PATCH 23/59] Fixing warnings --- .../gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 14 +++++--------- tests/render-texture-load/src/main.cpp | 3 ++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 5dd356349d..9a9185cd7c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -204,7 +204,6 @@ GL45Texture::~GL45Texture() { } } - auto originalAllocatedPages = _allocatedPages; auto maxSparseMip = std::min(_maxMip, _sparseInfo._maxSparseLevel); uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { @@ -216,10 +215,9 @@ GL45Texture::~GL45Texture() { _allocatedPages -= deallocatedPages; } } + if (0 != _allocatedPages) { - auto maxSize = _gpuObject.evalMipDimensions(0); - qDebug() << "Allocated pages remaining " << _id << " " << _allocatedPages; - qDebug() << originalAllocatedPages; + qWarning() << "Allocated pages remaining " << _id << " " << _allocatedPages; } } } @@ -424,7 +422,6 @@ void GL45Texture::updateMips() { if (!_sparse) { return; } - bool modified = false; auto newMinMip = std::min(_gpuObject.minMip(), _sparseInfo._maxSparseLevel); if (_minMip < newMinMip) { stripToMip(newMinMip); @@ -443,8 +440,6 @@ void GL45Backend::derezTextures() const { if (GLTexture::getMemoryPressure() < 1.0f) { return; } - qDebug() << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage(); - qDebug() << "Used texture memory " << Context::getTextureGPUMemoryUsage(); Lock lock(texturesByMipCountsMutex); if (texturesByMipCounts.empty()) { @@ -458,9 +453,10 @@ void GL45Backend::derezTextures() const { return; } + qDebug() << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage(); + qDebug() << "Used texture memory " << Context::getTextureGPUMemoryUsage(); + auto& textureMap = texturesByMipCounts; - auto newMipLevel = mipLevel - 1; - qDebug() << "Derez a texture"; GL45Texture* targetTexture = nullptr; { auto& textures = texturesByMipCounts[mipLevel]; diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp index ca0416baeb..fb0b16d16c 100644 --- a/tests/render-texture-load/src/main.cpp +++ b/tests/render-texture-load/src/main.cpp @@ -475,7 +475,7 @@ protected: } void loadAll() { - for (auto i = 0; i < _texturesFiles.size(); ++i) { + for (size_t i = 0; i < _texturesFiles.size(); ++i) { if (_textures[i]) { continue; } @@ -534,6 +534,7 @@ private: auto now = usecTimestampNow(); static auto last = now; auto delta = (now - last) / USECS_PER_MSEC; + Q_UNUSED(delta); #if 0 if (!_textures.empty()) { const auto& front = _textureLoads.front(); From 07ccbc6f82f940c68d9a28ab8ba8f0bb182eaf6c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Sep 2016 02:13:20 -0700 Subject: [PATCH 24/59] Fixing warnings --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 9a9185cd7c..ac1f4fddca 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -456,7 +456,6 @@ void GL45Backend::derezTextures() const { qDebug() << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage(); qDebug() << "Used texture memory " << Context::getTextureGPUMemoryUsage(); - auto& textureMap = texturesByMipCounts; GL45Texture* targetTexture = nullptr; { auto& textures = texturesByMipCounts[mipLevel]; From f9857c7ac4d89898c43daa5f8ffdfcaf1920d72e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Sep 2016 12:15:14 -0700 Subject: [PATCH 25/59] Disable threaded texture transfers --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 4 ++++ .../gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 18 ++++++++---------- .../gpu-gl/src/gpu/gl/GLTextureTransfer.h | 13 ++++++------- .../gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 2 +- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index e3f391126b..1385a56f65 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -679,6 +679,10 @@ void GLBackend::recycle() const { glDeleteQueries((GLsizei)ids.size(), ids.data()); } } + +#ifndef THREADED_TEXTURE_TRANSFER + gl::GLTexture::_textureTransferHelper->process(); +#endif } void GLBackend::setCameraCorrection(const Mat4& correction) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp index c66ea39f56..4cb8e4fad1 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp @@ -35,6 +35,8 @@ GLTextureTransferHelper::GLTextureTransferHelper() { initialize(true, QThread::LowPriority); // Clean shutdown on UNIX, otherwise _canvas is freed early connect(qApp, &QCoreApplication::aboutToQuit, [&] { terminate(); }); +#else + initialize(false, QThread::LowPriority); #endif } @@ -43,23 +45,18 @@ GLTextureTransferHelper::~GLTextureTransferHelper() { if (isStillRunning()) { terminate(); } +#else + terminate(); #endif } void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { GLTexture* object = Backend::getGPUObject(*texturePointer); -#ifdef THREADED_TEXTURE_TRANSFER Backend::incrementTextureGPUTransferCount(); object->setSyncState(GLSyncState::Pending); Lock lock(_mutex); _pendingTextures.push_back(texturePointer); -#else - for (object->startTransfer(); object->continueTransfer(); ) { } - object->finishTransfer(); - object->_contentStamp = texturePointer->getDataStamp(); - object->setSyncState(GLSyncState::Transferred); -#endif } void GLTextureTransferHelper::setup() { @@ -101,7 +98,6 @@ void GLTextureTransferHelper::shutdown() { } bool GLTextureTransferHelper::process() { -#ifdef THREADED_TEXTURE_TRANSFER // Take any new textures off the queue TextureList newTransferTextures; { @@ -123,7 +119,9 @@ bool GLTextureTransferHelper::process() { // No transfers in progress, sleep if (_transferringTextures.empty()) { +#ifdef THREADED_TEXTURE_TRANSFER QThread::usleep(1); +#endif return true; } @@ -159,6 +157,7 @@ bool GLTextureTransferHelper::process() { _textureIterator = _transferringTextures.erase(_textureIterator); } +#ifdef THREADED_TEXTURE_TRANSFER if (!_transferringTextures.empty()) { // Don't saturate the GPU clientWait(); @@ -166,8 +165,7 @@ bool GLTextureTransferHelper::process() { // Don't saturate the CPU QThread::msleep(1); } -#else - QThread::msleep(1); #endif + return true; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h index d2207df7e6..a880f42394 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h @@ -18,7 +18,7 @@ #include "GLShared.h" #ifdef Q_OS_WIN -#define THREADED_TEXTURE_TRANSFER +//#define THREADED_TEXTURE_TRANSFER #endif namespace gpu { namespace gl { @@ -33,7 +33,6 @@ public: ~GLTextureTransferHelper(); void transferTexture(const gpu::TexturePointer& texturePointer); -protected: void setup() override; void shutdown() override; bool process() override; @@ -41,6 +40,11 @@ protected: private: #ifdef THREADED_TEXTURE_TRANSFER ::gl::OffscreenContext _context; + // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread + GLuint _drawRenderbuffer { 0 }; + GLuint _drawFramebuffer { 0 }; + GLuint _readFramebuffer { 0 }; +#endif // A mutex for protecting items access on the render and transfer threads Mutex _mutex; // Textures that have been submitted for transfer @@ -50,11 +54,6 @@ private: TextureList _transferringTextures; TextureListIterator _textureIterator; - // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread - GLuint _drawRenderbuffer { 0 }; - GLuint _drawFramebuffer { 0 }; - GLuint _readFramebuffer { 0 }; -#endif }; } } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index ac1f4fddca..852af02287 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -26,7 +26,7 @@ using namespace gpu::gl; using namespace gpu::gl45; static const QString DEBUG_FLAG("HIFI_ENABLE_SPARSE_TEXTURES"); -static bool enableSparseTextures = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); +static bool enableSparseTextures = true; // QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); // Allocate 1 MB of buffer space for paged transfers #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) From 70ec9783fa6d48a519b0ecd8ef1f272d150c7068 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Sep 2016 11:07:15 -0700 Subject: [PATCH 26/59] Only use sparse allocation for supported sizes / formats --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 5 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 103 ++++++++++++++---- 2 files changed, 83 insertions(+), 25 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index c78e9888ca..9589c0dc7e 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -42,7 +42,10 @@ public: GLuint _maxSparseLevel { 0 }; uint32_t _maxPages { 0 }; uint32_t _pageBytes { 0 }; + bool _sparse { false }; + GLint _pageDimensionsIndex { 0 }; SparseInfo(GL45Texture& texture); + void maybeMakeSparse(); void update(); uvec3 getPageCounts(const uvec3& dimensions) const; uint32_t getPageCount(const uvec3& dimensions) const; @@ -86,8 +89,6 @@ public: TransferState _transferState; uint32_t _allocatedPages { 0 }; uint32_t _lastMipAllocatedPages { 0 }; - bool _sparse { false }; - friend class GL45Backend; }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 852af02287..383e3b7c38 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -25,8 +25,8 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -static const QString DEBUG_FLAG("HIFI_ENABLE_SPARSE_TEXTURES"); -static bool enableSparseTextures = true; // QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); +static const QString DEBUG_FLAG("HIFI_DISABLE_SPARSE_TEXTURES"); +static bool enableSparseTextures = !QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); // Allocate 1 MB of buffer space for paged transfers #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) @@ -35,6 +35,51 @@ using GL45Texture = GL45Backend::GL45Texture; static std::map> texturesByMipCounts; static Mutex texturesByMipCountsMutex; +using TextureTypeFormat = std::pair; +std::map> sparsePageDimensionsByFormat; +Mutex sparsePageDimensionsByFormatMutex; + + +static std::vector getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) { + { + Lock lock(sparsePageDimensionsByFormatMutex); + if (sparsePageDimensionsByFormat.count(typeFormat)) { + return sparsePageDimensionsByFormat[typeFormat]; + } + } + GLint count = 0; + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &count); + + std::vector result; + if (count > 0) { + std::vector x, y, z; + x.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &x[0]); + y.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &y[0]); + z.resize(count); + glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &z[0]); + + result.resize(count); + for (GLint i = 0; i < count; ++i) { + result[i] = uvec3(x[i], y[i], z[i]); + } + qDebug() << "Got " << count << " page sizes"; + } + + { + Lock lock(sparsePageDimensionsByFormatMutex); + if (0 == sparsePageDimensionsByFormat.count(typeFormat)) { + sparsePageDimensionsByFormat[typeFormat] = result; + } + } + + return result; +} + +static std::vector getPageDimensionsForFormat(GLenum target, GLenum format) { + return getPageDimensionsForFormat({ target, format }); +} GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texture, bool transfer) { return GL45Texture::sync(*this, texture, transfer); @@ -46,14 +91,31 @@ SparseInfo::SparseInfo(GL45Texture& texture) : _texture(texture) { } +void SparseInfo::maybeMakeSparse() { + const uvec3 dimensions = _texture._gpuObject.getDimensions(); + auto allowedPageDimensions = getPageDimensionsForFormat(_texture._target, _texture._internalFormat); + // In order to enable sparse the texture size must be an integer multiple of the page size + for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { + _pageDimensionsIndex = (uint32_t) i; + _pageDimensions = allowedPageDimensions[i]; + // Is this texture an integer multiple of page dimensions? + if (uvec3(0) == (dimensions % _pageDimensions)) { + _sparse = true; + break; + } + } + + if (_sparse) { + glTextureParameteri(_texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); + glTextureParameteri(_texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, _pageDimensionsIndex); + } else { + qDebug() << "Texture size " << dimensions.x << " x " << dimensions.y << " is not supported by any sparse page size"; + } +} + +// This can only be called after we've established our storage size void SparseInfo::update() { glGetTextureParameterIuiv(_texture._id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel); - GLenum internalFormat = gl::GLTexelFormat::evalGLTexelFormat(_texture._gpuObject.getTexelFormat(), _texture._gpuObject.getTexelFormat()).internalFormat; - ivec3 pageSize; - glGetInternalformativ(_texture._target, internalFormat, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &pageSize.x); - glGetInternalformativ(_texture._target, internalFormat, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &pageSize.y); - glGetInternalformativ(_texture._target, internalFormat, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &pageSize.z); - _pageDimensions = uvec3(pageSize); _pageBytes = _texture._gpuObject.getTexelFormat().getSize(); _pageBytes *= _pageDimensions.x * _pageDimensions.y * _pageDimensions.z; @@ -179,18 +241,13 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) { GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) { - if (enableSparseTextures && _transferrable && (_target != GL_TEXTURE_CUBE_MAP)) { - GLint pageSizesCount = 0; - glGetInternalformativ(_target, _internalFormat, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &pageSizesCount); - if (pageSizesCount > 0) { - _sparse = true; - glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); - } + if (enableSparseTextures && _transferrable) { + _sparseInfo.maybeMakeSparse(); } } GL45Texture::~GL45Texture() { - if (_sparse) { + if (_sparseInfo._sparse) { auto mipLevels = usedMipLevels(); { @@ -270,7 +327,7 @@ bool GL45Texture::continueTransfer() { uvec3 offset = _transferState._mipOffset; // FIXME we should be using the DSA for all of this - if (_sparse && _transferState._mipLevel <= _sparseInfo._maxSparseLevel) { + if (_sparseInfo._sparse && _transferState._mipLevel <= _sparseInfo._maxSparseLevel) { if (_allocatedPages > _sparseInfo._maxPages) { qDebug() << "Exceeded max page allocation!"; } @@ -306,7 +363,7 @@ bool GL45Texture::continueTransfer() { serverWait(); auto currentMip = _transferState._mipLevel; auto result = _transferState.increment(); - if (_sparse && _transferState._mipLevel != currentMip && currentMip <= _sparseInfo._maxSparseLevel) { + if (_sparseInfo._sparse && _transferState._mipLevel != currentMip && currentMip <= _sparseInfo._maxSparseLevel) { auto mipDimensions = _gpuObject.evalMipDimensions(currentMip); auto mipExpectedPages = _sparseInfo.getPageCount(mipDimensions); auto newPages = _allocatedPages - _lastMipAllocatedPages; @@ -349,7 +406,7 @@ void GL45Texture::syncSampler() const { void GL45Texture::postTransfer() { Parent::postTransfer(); - if (_sparse) { + if (_sparseInfo._sparse) { auto mipLevels = usedMipLevels(); if (mipLevels > 1 && _minMip < _sparseInfo._maxSparseLevel) { Lock lock(texturesByMipCountsMutex); @@ -359,7 +416,7 @@ void GL45Texture::postTransfer() { } void GL45Texture::stripToMip(uint16_t newMinMip) { - if (!_sparse) { + if (!_sparseInfo._sparse) { return; } @@ -412,14 +469,14 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { // Re-insert into the texture-by-mips map if appropriate mipLevels = usedMipLevels(); - if (_sparse && mipLevels > 1 && _minMip < _sparseInfo._maxSparseLevel) { + if (_sparseInfo._sparse && mipLevels > 1 && _minMip < _sparseInfo._maxSparseLevel) { Lock lock(texturesByMipCountsMutex); texturesByMipCounts[mipLevels].insert(this); } } void GL45Texture::updateMips() { - if (!_sparse) { + if (!_sparseInfo._sparse) { return; } auto newMinMip = std::min(_gpuObject.minMip(), _sparseInfo._maxSparseLevel); @@ -429,7 +486,7 @@ void GL45Texture::updateMips() { } void GL45Texture::derez() { - assert(_sparse); + assert(_sparseInfo._sparse); assert(_minMip < _sparseInfo._maxSparseLevel); assert(_minMip < _maxMip); assert(_transferrable); From 26a06b29145689d619b55b84d765f80fc439e486 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Sep 2016 12:40:32 -0700 Subject: [PATCH 27/59] Removing invalid comments --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 383e3b7c38..b6eae6e611 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -133,8 +133,6 @@ uvec3 SparseInfo::getPageCounts(const uvec3& dimensions) const { auto result = (dimensions / _pageDimensions) + glm::clamp(dimensions % _pageDimensions, glm::uvec3(0), glm::uvec3(1)); return result; - - } uint32_t SparseInfo::getPageCount(const uvec3& dimensions) const { @@ -323,17 +321,13 @@ bool GL45Texture::continueTransfer() { if (buffer.empty()) { buffer.resize(DEFAULT_PAGE_BUFFER_SIZE); } - uvec3 pageSize = _transferState.currentPageSize(); - uvec3 offset = _transferState._mipOffset; + const uvec3 pageSize = _transferState.currentPageSize(); + const uvec3& offset = _transferState._mipOffset; - // FIXME we should be using the DSA for all of this if (_sparseInfo._sparse && _transferState._mipLevel <= _sparseInfo._maxSparseLevel) { if (_allocatedPages > _sparseInfo._maxPages) { - qDebug() << "Exceeded max page allocation!"; + qWarning() << "Exceeded max page allocation!"; } - //glBindTexture(_target, _id); - // FIXME we should be using glTexturePageCommitmentEXT, but for some reason it causes out of memory errors. - // Either I'm not understanding how it should work or there's a driver bug. glTexturePageCommitmentEXT(_id, _transferState._mipLevel, offset.x, offset.y, _transferState._face, pageSize.x, pageSize.y, pageSize.z, From 12de36a534f60158759ec0e024c978727084f8ed Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Sep 2016 14:07:27 -0700 Subject: [PATCH 28/59] Check the total GPU memory usage from the card as well as our computed memory usage --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 4 ++++ libraries/gpu-gl/src/gpu/gl/GLShared.cpp | 26 +++++++++++++++++++++++ libraries/gpu-gl/src/gpu/gl/GLShared.h | 1 + libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 14 ++++++++++-- 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 1385a56f65..8753db8081 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -39,6 +39,10 @@ static GLBackend* INSTANCE{ nullptr }; static const char* GL_BACKEND_PROPERTY_NAME = "com.highfidelity.gl.backend"; BackendPointer GLBackend::createBackend() { + // The ATI memory info extension only exposes 'free memory' so we want to force it to + // cache the value as early as possible + getDedicatedMemory(); + // FIXME provide a mechanism to override the backend for testing // Where the gpuContext is initialized and where the TRUE Backend is created and assigned auto version = QOpenGLContextWrapper::currentContextVersion(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp index d59be0d9de..af03da1931 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp @@ -60,6 +60,32 @@ bool checkGLErrorDebug(const char* name) { #endif } +gpu::Size getFreeDedicatedMemory() { + Size result { 0 }; + static bool nvidiaMemorySupported { true }; + static bool atiMemorySupported { true }; + if (nvidiaMemorySupported) { + + GLint nvGpuMemory { 0 }; + glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &nvGpuMemory); + if (GL_NO_ERROR == glGetError()) { + result = KB_TO_BYTES(nvGpuMemory); + } else { + nvidiaMemorySupported = false; + } + } else if (atiMemorySupported) { + GLint atiGpuMemory[4]; + // not really total memory, but close enough if called early enough in the application lifecycle + glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, atiGpuMemory); + if (GL_NO_ERROR == glGetError()) { + result = KB_TO_BYTES(atiGpuMemory[0]); + } else { + atiMemorySupported = false; + } + } + return result; +} + gpu::Size getDedicatedMemory() { static Size dedicatedMemory { 0 }; static std::once_flag once; diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.h b/libraries/gpu-gl/src/gpu/gl/GLShared.h index 7ec6deeb9d..b5dece7cf4 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.h @@ -25,6 +25,7 @@ void serverWait(); void clientWait(); gpu::Size getDedicatedMemory(); +gpu::Size getFreeDedicatedMemory(); ComparisonFunction comparisonFuncFromGL(GLenum func); State::StencilOp stencilOpFromGL(GLenum stencilOp); State::BlendOp blendOpFromGL(GLenum blendOp); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index a279bec11c..fc50c69538 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -92,6 +92,7 @@ const std::vector& GLTexture::getFaceTargets(GLenum target) { return faceTargets; } +#define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f float GLTexture::getMemoryPressure() { // Check for an explicit memory limit auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage(); @@ -100,10 +101,19 @@ float GLTexture::getMemoryPressure() { if (!availableTextureMemory) { auto totalGpuMemory = getDedicatedMemory(); - // If no limit has been explicitly set, and the dedicated memory can't be determined, - // just use a fallback fixed value of 256 MB if (!totalGpuMemory) { + // If we can't query the dedicated memory just use a fallback fixed value of 256 MB totalGpuMemory = MB_TO_BYTES(DEFAULT_MAX_MEMORY_MB); + } else { + // Check the global free GPU memory + auto freeGpuMemory = getFreeDedicatedMemory(); + if (freeGpuMemory) { + auto freePercentage = (float)freeGpuMemory / (float)totalGpuMemory; + if (freePercentage < MIN_FREE_GPU_MEMORY_PERCENTAGE) { + qDebug() << "Exceeded max GPU memory"; + return 2.0; + } + } } // Allow 75% of all available GPU memory to be consumed by textures From 50233f6ffb4ef0ba4cf5ffdd7bc75103bc93dd8a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Sep 2016 14:29:02 -0700 Subject: [PATCH 29/59] Don't re-report texture memory usage until the returned value changes --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index fc50c69538..3a45f55646 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -93,6 +93,7 @@ const std::vector& GLTexture::getFaceTargets(GLenum target) { } #define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f + float GLTexture::getMemoryPressure() { // Check for an explicit memory limit auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage(); @@ -107,8 +108,10 @@ float GLTexture::getMemoryPressure() { } else { // Check the global free GPU memory auto freeGpuMemory = getFreeDedicatedMemory(); - if (freeGpuMemory) { - auto freePercentage = (float)freeGpuMemory / (float)totalGpuMemory; + static gpu::Size lastFreeGpuMemory = 0; + auto freePercentage = (float)freeGpuMemory / (float)totalGpuMemory; + if (freeGpuMemory != lastFreeGpuMemory) { + lastFreeGpuMemory = freeGpuMemory; if (freePercentage < MIN_FREE_GPU_MEMORY_PERCENTAGE) { qDebug() << "Exceeded max GPU memory"; return 2.0; From 74481b3c1fefe940da669ce56d66651d400263a5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Sep 2016 15:45:47 -0700 Subject: [PATCH 30/59] Re-enable threaded texture transfer --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 16 +++++++++------- libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 3a45f55646..4796d0497e 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -108,13 +108,15 @@ float GLTexture::getMemoryPressure() { } else { // Check the global free GPU memory auto freeGpuMemory = getFreeDedicatedMemory(); - static gpu::Size lastFreeGpuMemory = 0; - auto freePercentage = (float)freeGpuMemory / (float)totalGpuMemory; - if (freeGpuMemory != lastFreeGpuMemory) { - lastFreeGpuMemory = freeGpuMemory; - if (freePercentage < MIN_FREE_GPU_MEMORY_PERCENTAGE) { - qDebug() << "Exceeded max GPU memory"; - return 2.0; + if (freeGpuMemory) { + static gpu::Size lastFreeGpuMemory = 0; + auto freePercentage = (float)freeGpuMemory / (float)totalGpuMemory; + if (freeGpuMemory != lastFreeGpuMemory) { + lastFreeGpuMemory = freeGpuMemory; + if (freePercentage < MIN_FREE_GPU_MEMORY_PERCENTAGE) { + qDebug() << "Exceeded max GPU memory"; + return 2.0; + } } } } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h index a880f42394..fe7414e370 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h @@ -18,7 +18,7 @@ #include "GLShared.h" #ifdef Q_OS_WIN -//#define THREADED_TEXTURE_TRANSFER +#define THREADED_TEXTURE_TRANSFER #endif namespace gpu { namespace gl { From 1ff91219a8d7b726289f880af62c4212512156ed Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 16 Sep 2016 14:36:24 -0700 Subject: [PATCH 31/59] Fix strange artifacts in skybox --- .../src/gpu/gl45/GL45BackendTexture.cpp | 20 ++++++++++++++++++- libraries/gpu/src/gpu/Texture.h | 4 ++++ libraries/model/src/model/TextureMap.cpp | 18 +++++++++++------ libraries/model/src/model/TextureMap.h | 2 +- .../render-utils/src/SubsurfaceScattering.cpp | 3 +++ 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index b6eae6e611..d967a1a7a2 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -92,6 +92,11 @@ SparseInfo::SparseInfo(GL45Texture& texture) } void SparseInfo::maybeMakeSparse() { + // Don't enable sparse for objects with explicitly managed mip levels + if (!_texture._gpuObject.isAutogenerateMips()) { + qCDebug(gpugl45logging) << "Don't enable sparse texture for explicitly generated mipmaps on texture " << _texture._gpuObject.source().c_str(); + return; + } const uvec3 dimensions = _texture._gpuObject.getDimensions(); auto allowedPageDimensions = getPageDimensionsForFormat(_texture._target, _texture._internalFormat); // In order to enable sparse the texture size must be an integer multiple of the page size @@ -100,6 +105,7 @@ void SparseInfo::maybeMakeSparse() { _pageDimensions = allowedPageDimensions[i]; // Is this texture an integer multiple of page dimensions? if (uvec3(0) == (dimensions % _pageDimensions)) { + qCDebug(gpugl45logging) << "Enabling sparse for texture " << _texture._gpuObject.source().c_str(); _sparse = true; break; } @@ -109,12 +115,16 @@ void SparseInfo::maybeMakeSparse() { glTextureParameteri(_texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); glTextureParameteri(_texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, _pageDimensionsIndex); } else { - qDebug() << "Texture size " << dimensions.x << " x " << dimensions.y << " is not supported by any sparse page size"; + qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y << + " is not supported by any sparse page size for texture" << _texture._gpuObject.source().c_str(); } } // This can only be called after we've established our storage size void SparseInfo::update() { + if (!_sparse) { + return; + } glGetTextureParameterIuiv(_texture._id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel); _pageBytes = _texture._gpuObject.getTexelFormat().getSize(); _pageBytes *= _pageDimensions.x * _pageDimensions.y * _pageDimensions.z; @@ -282,6 +292,7 @@ void GL45Texture::withPreservedTexture(std::function f) const { } void GL45Texture::generateMips() const { + qDebug() << "Generating mipmaps for " << _gpuObject.source().c_str(); glGenerateTextureMipmap(_id); (void)CHECK_GL_ERROR(); } @@ -435,6 +446,13 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { } } + // If we weren't generating mips before, we need to now that we're stripping down mip levels. + if (!_gpuObject.isAutogenerateMips()) { + qDebug() << "Force mip generation for texture"; + glGenerateTextureMipmap(_id); + } + + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { auto mipDimensions = _gpuObject.evalMipDimensions(mip); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index a7ac472922..ae1afcafcb 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -389,6 +389,8 @@ public: uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; } + const std::string& source() const { return _source; } + void setSource(const std::string& source) { _source = source; } bool setMinMip(uint16 newMinMip); bool incremementMinMip(uint16 count = 1); @@ -450,6 +452,8 @@ public: const GPUObjectPointer gpuObject {}; protected: + // Not strictly necessary, but incredibly useful for debugging + std::string _source; std::unique_ptr< Storage > _storage; Stamp _stamp = 0; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 9345124d54..30f176b5a6 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -176,7 +176,7 @@ void generateFaceMips(gpu::Texture* texture, QImage& image, gpu::Element formatM #endif } -gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, bool isLinear, bool doCompress, bool generateMips) { +gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips) { bool validAlpha = false; bool alphaAsMask = true; QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask); @@ -189,7 +189,7 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); - + theTexture->setSource(srcImageName); auto usage = gpu::Texture::Usage::Builder().withColor(); if (validAlpha) { usage.withAlpha(); @@ -210,20 +210,20 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag } gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureColorFromImage(srcImage, false, false, true); + return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true); } gpu::Texture* TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureColorFromImage(srcImage, false, true, true); + return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true); } gpu::Texture* TextureUsage::createEmissiveTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureColorFromImage(srcImage, false, true, true); + return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true); } gpu::Texture* TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureColorFromImage(srcImage, false, true, true); + return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true); } @@ -241,6 +241,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip); } @@ -324,6 +325,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip); } @@ -355,6 +357,7 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip); @@ -392,6 +395,7 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip); @@ -426,6 +430,7 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture->setSource(srcImageName); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); generateMips(theTexture, image, formatMip); @@ -737,6 +742,7 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm // If the 6 faces have been created go on and define the true Texture if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) { theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); + theTexture->setSource(srcImageName); int f = 0; for (auto& face : faces) { theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f); diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index ac35db2f03..220ee57a97 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -47,7 +47,7 @@ public: static const QImage process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask); static void defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip, const QImage& srcImage, bool isLinear, bool doCompress); - static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, bool isLinear, bool doCompress, bool generateMips); + static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips); static gpu::Texture* processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance); }; diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index f1aec66433..3e4ec50dee 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -415,6 +415,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringProfile(Rend // const auto pixelFormat = gpu::Element::COLOR_SRGBA_32; const auto pixelFormat = gpu::Element::COLOR_R11G11B10; auto profileMap = gpu::TexturePointer(gpu::Texture::create2D(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + profileMap->setSource("Generated Scattering Profile"); diffuseProfileGPU(profileMap, args); return profileMap; } @@ -426,6 +427,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin const auto pixelFormat = gpu::Element::COLOR_R11G11B10; auto scatteringLUT = gpu::TexturePointer(gpu::Texture::create2D(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); //diffuseScatter(scatteringLUT); + scatteringLUT->setSource("Generated pre-integrated scattering"); diffuseScatterGPU(profile, scatteringLUT, args); return scatteringLUT; } @@ -433,6 +435,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringSpecularBeckmann(RenderArgs* args) { const int SPECULAR_RESOLUTION = 256; auto beckmannMap = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32 /*gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RGB)*/, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + beckmannMap->setSource("Generated beckmannMap"); computeSpecularBeckmannGPU(beckmannMap, args); return beckmannMap; } From 64cb45240d387874a304a137e3fca5df18cdf43d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Sep 2016 10:20:51 -0700 Subject: [PATCH 32/59] Fix invalid page size for non-sparse textures --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 9589c0dc7e..0f61cd1ab9 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -30,6 +30,7 @@ public: class GL45Texture : public GLTexture { using Parent = GLTexture; static GLuint allocate(const Texture& texture); + static const uint32_t DEFAULT_PAGE_DIMENSION = 128; public: GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable); ~GL45Texture(); @@ -38,7 +39,7 @@ public: struct SparseInfo { GL45Texture& _texture; - uvec3 _pageDimensions; + uvec3 _pageDimensions { DEFAULT_PAGE_DIMENSION }; GLuint _maxSparseLevel { 0 }; uint32_t _maxPages { 0 }; uint32_t _pageBytes { 0 }; From 5734332150886a2c4701e4062a807b2867807d0c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Sep 2016 11:29:27 -0700 Subject: [PATCH 33/59] Fix sparse texture deallocation --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 16 ++++ libraries/gpu-gl/src/gpu/gl/GLBackend.h | 2 + libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 1 - .../src/gpu/gl45/GL45BackendTexture.cpp | 79 ++++++++++--------- 4 files changed, 60 insertions(+), 38 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 8753db8081..72a953c5f3 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -593,7 +593,23 @@ void GLBackend::releaseQuery(GLuint id) const { _queriesTrash.push_back(id); } +void GLBackend::releaseLambda(const std::function lambda) const { + Lock lock(_trashMutex); + _lambdasTrash.push_back(lambda); +} + void GLBackend::recycle() const { + { + std::list> lamdbasTrash; + { + Lock lock(_trashMutex); + std::swap(_lambdasTrash, lamdbasTrash); + } + for (auto lambda : lamdbasTrash) { + lambda(); + } + } + { std::vector ids; std::list> buffersTrash; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 29e66da020..545ae6cbdc 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -175,6 +175,7 @@ public: virtual void releaseShader(GLuint id) const; virtual void releaseProgram(GLuint id) const; virtual void releaseQuery(GLuint id) const; + virtual void releaseLambda(const std::function lambda) const; protected: @@ -197,6 +198,7 @@ protected: mutable std::list _shadersTrash; mutable std::list _programsTrash; mutable std::list _queriesTrash; + mutable std::list> _lambdasTrash; void renderPassTransfer(const Batch& batch); void renderPassDraw(const Batch& batch); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 4796d0497e..c2d6b3eb54 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -155,7 +155,6 @@ GLTexture::~GLTexture() { auto backend = _backend.lock(); if (backend) { backend->releaseTexture(_id, _size); - backend->recycle(); } } Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index d967a1a7a2..b369d6d819 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -30,6 +30,7 @@ static bool enableSparseTextures = !QProcessEnvironment::systemEnvironment().con // Allocate 1 MB of buffer space for paged transfers #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) +#define DEFAULT_GL_PIXEL_ALIGNMENT 4 using GL45Texture = GL45Backend::GL45Texture; @@ -39,7 +40,6 @@ using TextureTypeFormat = std::pair; std::map> sparsePageDimensionsByFormat; Mutex sparsePageDimensionsByFormatMutex; - static std::vector getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) { { Lock lock(sparsePageDimensionsByFormatMutex); @@ -64,7 +64,7 @@ static std::vector getPageDimensionsForFormat(const TextureTypeFormat& ty for (GLint i = 0; i < count; ++i) { result[i] = uvec3(x[i], y[i], z[i]); } - qDebug() << "Got " << count << " page sizes"; + qCDebug(gpugl45logging) << "Got " << count << " page sizes"; } { @@ -212,7 +212,6 @@ bool TransferState::increment() { return false; } -#define DEFAULT_GL_PIXEL_ALIGNMENT 4 void TransferState::populatePage(std::vector& buffer) { uvec3 pageSize = currentPageSize(); auto bytesPerPageLine = _bytesPerPixel * pageSize.x; @@ -254,35 +253,41 @@ GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& } } +// Destructors get called on the main thread, potentially without a context active. We need to queue the +// deallocation of the sparse pages for this content. GL45Texture::~GL45Texture() { - if (_sparseInfo._sparse) { - auto mipLevels = usedMipLevels(); - { - Lock lock(texturesByMipCountsMutex); - if (texturesByMipCounts.count(mipLevels)) { - auto& textures = texturesByMipCounts[mipLevels]; - textures.erase(this); - if (textures.empty()) { - texturesByMipCounts.erase(mipLevels); + if (_sparseInfo._sparse) { + auto backend = _backend.lock(); + if (backend) { + auto id = _id; + auto mipLevels = usedMipLevels(); + { + Lock lock(texturesByMipCountsMutex); + if (texturesByMipCounts.count(mipLevels)) { + auto& textures = texturesByMipCounts[mipLevels]; + textures.erase(this); + if (textures.empty()) { + texturesByMipCounts.erase(mipLevels); + } } } - } - auto maxSparseMip = std::min(_maxMip, _sparseInfo._maxSparseLevel); - uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { - auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); - auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions); - for (uint8_t face = 0; face < maxFace; ++face) { - glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, mipDimensions.x, mipDimensions.y, mipDimensions.z, GL_FALSE); + auto maxSparseMip = std::min(_maxMip, _sparseInfo._maxSparseLevel); + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { + auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); + backend->releaseLambda([=] { + glTexturePageCommitmentEXT(id, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); + }); + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; assert(deallocatedPages <= _allocatedPages); _allocatedPages -= deallocatedPages; } - } - if (0 != _allocatedPages) { - qWarning() << "Allocated pages remaining " << _id << " " << _allocatedPages; + if (0 != _allocatedPages) { + qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _allocatedPages; + } } } } @@ -292,7 +297,7 @@ void GL45Texture::withPreservedTexture(std::function f) const { } void GL45Texture::generateMips() const { - qDebug() << "Generating mipmaps for " << _gpuObject.source().c_str(); + qCDebug(gpugl45logging) << "Generating mipmaps for " << _gpuObject.source().c_str(); glGenerateTextureMipmap(_id); (void)CHECK_GL_ERROR(); } @@ -337,7 +342,7 @@ bool GL45Texture::continueTransfer() { if (_sparseInfo._sparse && _transferState._mipLevel <= _sparseInfo._maxSparseLevel) { if (_allocatedPages > _sparseInfo._maxPages) { - qWarning() << "Exceeded max page allocation!"; + qCWarning(gpugl45logging) << "Exceeded max page allocation!"; } glTexturePageCommitmentEXT(_id, _transferState._mipLevel, offset.x, offset.y, _transferState._face, @@ -373,7 +378,7 @@ bool GL45Texture::continueTransfer() { auto mipExpectedPages = _sparseInfo.getPageCount(mipDimensions); auto newPages = _allocatedPages - _lastMipAllocatedPages; if (newPages != mipExpectedPages) { - qWarning() << "Unexpected page allocation size... " << newPages << " " << mipExpectedPages; + qCWarning(gpugl45logging) << "Unexpected page allocation size... " << newPages << " " << mipExpectedPages; } _lastMipAllocatedPages = _allocatedPages; } @@ -426,12 +431,12 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { } if (newMinMip < _minMip) { - qWarning() << "Cannot decrease the min mip"; + qCWarning(gpugl45logging) << "Cannot decrease the min mip"; return; } if (newMinMip > _sparseInfo._maxSparseLevel) { - qWarning() << "Cannot increase the min mip into the mip tail"; + qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail"; return; } @@ -448,7 +453,7 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { // If we weren't generating mips before, we need to now that we're stripping down mip levels. if (!_gpuObject.isAutogenerateMips()) { - qDebug() << "Force mip generation for texture"; + qCDebug(gpugl45logging) << "Force mip generation for texture"; glGenerateTextureMipmap(_id); } @@ -474,9 +479,9 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { updateSize(); size_t newSize = _size; if (newSize > oldSize) { - qDebug() << "WTF"; - qDebug() << "\told size " << oldSize; - qDebug() << "\tnew size " << newSize; + qCDebug(gpugl45logging) << "WTF"; + qCDebug(gpugl45logging) << "\told size " << oldSize; + qCDebug(gpugl45logging) << "\tnew size " << newSize; } // Re-insert into the texture-by-mips map if appropriate @@ -512,18 +517,18 @@ void GL45Backend::derezTextures() const { Lock lock(texturesByMipCountsMutex); if (texturesByMipCounts.empty()) { - qDebug() << "No available textures to derez"; + qCDebug(gpugl45logging) << "No available textures to derez"; return; } auto mipLevel = texturesByMipCounts.rbegin()->first; if (mipLevel <= 1) { - qDebug() << "Max mip levels " << mipLevel; + qCDebug(gpugl45logging) << "Max mip levels " << mipLevel; return; } - qDebug() << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage(); - qDebug() << "Used texture memory " << Context::getTextureGPUMemoryUsage(); + qCDebug(gpugl45logging) << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage(); + qCDebug(gpugl45logging) << "Used texture memory " << Context::getTextureGPUMemoryUsage(); GL45Texture* targetTexture = nullptr; { @@ -533,5 +538,5 @@ void GL45Backend::derezTextures() const { } lock.unlock(); targetTexture->derez(); - qDebug() << "New Used texture memory " << Context::getTextureGPUMemoryUsage(); + qCDebug(gpugl45logging) << "New Used texture memory " << Context::getTextureGPUMemoryUsage(); } From 88bbb2faa8e074d2e9e90b13409ce79b341b978d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Sep 2016 11:52:19 -0700 Subject: [PATCH 34/59] Fix cubemap mips by overcommiting --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index b369d6d819..f6d8110b3c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -126,6 +126,11 @@ void SparseInfo::update() { return; } glGetTextureParameterIuiv(_texture._id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel); + // For some reason the long mip tail isn't working properly with cubemaps unless + // I extend one more level + if (GL_TEXTURE_CUBE_MAP == _texture._target) { + ++_maxSparseLevel; + } _pageBytes = _texture._gpuObject.getTexelFormat().getSize(); _pageBytes *= _pageDimensions.x * _pageDimensions.y * _pageDimensions.z; @@ -253,10 +258,7 @@ GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& } } -// Destructors get called on the main thread, potentially without a context active. We need to queue the -// deallocation of the sparse pages for this content. GL45Texture::~GL45Texture() { - if (_sparseInfo._sparse) { auto backend = _backend.lock(); if (backend) { @@ -277,6 +279,8 @@ GL45Texture::~GL45Texture() { uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); + // Destructors get called on the main thread, potentially without a context active. + // We need to queue the deallocation of the sparse pages for this content. backend->releaseLambda([=] { glTexturePageCommitmentEXT(id, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); }); From ffbddd2d0f94d3cc74d63226fce5623a28018102 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Sep 2016 13:57:15 -0700 Subject: [PATCH 35/59] Disable sparse textures on cubemaps --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index f6d8110b3c..79f4615317 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -97,6 +97,12 @@ void SparseInfo::maybeMakeSparse() { qCDebug(gpugl45logging) << "Don't enable sparse texture for explicitly generated mipmaps on texture " << _texture._gpuObject.source().c_str(); return; } + + if (GL_TEXTURE_CUBE_MAP == _texture._target) { + qCDebug(gpugl45logging) << "Don't enable sparse texture for cubemaps " << _texture._gpuObject.source().c_str(); + return; + } + const uvec3 dimensions = _texture._gpuObject.getDimensions(); auto allowedPageDimensions = getPageDimensionsForFormat(_texture._target, _texture._internalFormat); // In order to enable sparse the texture size must be an integer multiple of the page size From fa23fa7b2de407aced32e3f304ed03e820ab41d3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Sep 2016 14:36:37 -0700 Subject: [PATCH 36/59] Working on cubemap mip bug --- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 3 ++- .../gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 10 ---------- libraries/model/src/model/skybox.slf | 2 +- tests/render-perf/src/Camera.hpp | 18 ++++++++++++++---- tests/render-perf/src/main.cpp | 7 ++++--- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 0f61cd1ab9..b684a9b8d2 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -31,6 +31,7 @@ public: using Parent = GLTexture; static GLuint allocate(const Texture& texture); static const uint32_t DEFAULT_PAGE_DIMENSION = 128; + static const uint32_t DEFAULT_MAX_SPARSE_LEVEL = 0xFFFF; public: GL45Texture(const std::weak_ptr& backend, const Texture& texture, bool transferrable); ~GL45Texture(); @@ -40,7 +41,7 @@ public: struct SparseInfo { GL45Texture& _texture; uvec3 _pageDimensions { DEFAULT_PAGE_DIMENSION }; - GLuint _maxSparseLevel { 0 }; + GLuint _maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL }; uint32_t _maxPages { 0 }; uint32_t _pageBytes { 0 }; bool _sparse { false }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 79f4615317..09c6003ced 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -98,11 +98,6 @@ void SparseInfo::maybeMakeSparse() { return; } - if (GL_TEXTURE_CUBE_MAP == _texture._target) { - qCDebug(gpugl45logging) << "Don't enable sparse texture for cubemaps " << _texture._gpuObject.source().c_str(); - return; - } - const uvec3 dimensions = _texture._gpuObject.getDimensions(); auto allowedPageDimensions = getPageDimensionsForFormat(_texture._target, _texture._internalFormat); // In order to enable sparse the texture size must be an integer multiple of the page size @@ -132,11 +127,6 @@ void SparseInfo::update() { return; } glGetTextureParameterIuiv(_texture._id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel); - // For some reason the long mip tail isn't working properly with cubemaps unless - // I extend one more level - if (GL_TEXTURE_CUBE_MAP == _texture._target) { - ++_maxSparseLevel; - } _pageBytes = _texture._gpuObject.getTexelFormat().getSize(); _pageBytes *= _pageDimensions.x * _pageDimensions.y * _pageDimensions.z; diff --git a/libraries/model/src/model/skybox.slf b/libraries/model/src/model/skybox.slf index 7b25e36af7..c0770525f7 100755 --- a/libraries/model/src/model/skybox.slf +++ b/libraries/model/src/model/skybox.slf @@ -48,7 +48,7 @@ void main(void) { // blend is only set if there is a cubemap if (skybox.color.a > 0.0) { - color = texture(cubeMap, coord).rgb; + color = textureLod(cubeMap, coord, 0).rgb; if (skybox.color.a < 1.0) { color *= skybox.color.rgb; } diff --git a/tests/render-perf/src/Camera.hpp b/tests/render-perf/src/Camera.hpp index ebfcfb9e32..6e1d95bfe1 100644 --- a/tests/render-perf/src/Camera.hpp +++ b/tests/render-perf/src/Camera.hpp @@ -14,9 +14,10 @@ protected: public: glm::quat getOrientation() const { - return glm::angleAxis(yaw, Vectors::UP); + return glm::angleAxis(yawPitch.x, Vectors::UP) * glm::angleAxis(yawPitch.y, Vectors::RIGHT); } - float yaw { 0 }; + + vec2 yawPitch { 0 }; glm::vec3 position; float rotationSpeed { 1.0f }; float movementSpeed { 1.0f }; @@ -76,7 +77,12 @@ public: }; void rotate(const float delta) { - yaw += delta; + yawPitch.x += delta; + updateViewMatrix(); + } + + void rotate(const glm::vec2& delta) { + yawPitch += delta; updateViewMatrix(); } @@ -84,7 +90,11 @@ public: glm::vec3 f = rotation * Vectors::UNIT_NEG_Z; f.y = 0; f = glm::normalize(f); - yaw = angleBetween(Vectors::UNIT_NEG_Z, f); + yawPitch.x = angleBetween(Vectors::UNIT_NEG_Z, f); + f = rotation * Vectors::UNIT_NEG_Z; + f.x = 0; + f = glm::normalize(f); + yawPitch.y = angleBetween(Vectors::UNIT_NEG_Z, f); updateViewMatrix(); } diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index bbe1c33bb1..605ccf3787 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -188,7 +188,8 @@ public: if (buttons & Qt::RightButton) { dolly(delta.y * 0.01f); } else if (buttons & Qt::LeftButton) { - rotate(delta.x * -0.01f); + //rotate(delta.x * -0.01f); + rotate(delta * -0.01f); } else if (buttons & Qt::MiddleButton) { delta.y *= -1.0f; translate(delta * -0.01f); @@ -367,7 +368,7 @@ public: sortedHighFrames.sort(); for (const auto& t : sortedHighFrames) { - qDebug() << "Long frame " << t; + //qDebug() << "Long frame " << t; } } @@ -990,7 +991,7 @@ private: } void resetPosition() { - _camera.yaw = 0; + _camera.yawPitch = vec3(0); _camera.setPosition(vec3()); } From 0953027ef349c19245bbe8cac68cc5a380172457 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Sep 2016 15:42:26 -0700 Subject: [PATCH 37/59] Identify pre-generated textures --- libraries/gpu/src/gpu/Framebuffer.cpp | 6 ++++-- .../model-networking/src/model-networking/TextureCache.cpp | 4 ++++ libraries/render-utils/src/AntialiasingEffect.cpp | 1 + libraries/render-utils/src/DeferredFramebuffer.cpp | 6 +++++- libraries/render-utils/src/DeferredLightingEffect.cpp | 2 ++ libraries/render-utils/src/SurfaceGeometryPass.cpp | 6 ++++++ tests/render-perf/src/main.cpp | 2 +- 7 files changed, 23 insertions(+), 4 deletions(-) diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index beb0334208..20e55b9ef9 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -32,6 +32,7 @@ Framebuffer* Framebuffer::create( const Format& colorBufferFormat, uint16 width, auto framebuffer = Framebuffer::create(); auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + colorTexture->setSource("Framebuffer::colorTexture"); framebuffer->setRenderBuffer(0, colorTexture); @@ -42,8 +43,9 @@ Framebuffer* Framebuffer::create( const Format& colorBufferFormat, const Format& auto framebuffer = Framebuffer::create(); auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + colorTexture->setSource("Framebuffer::colorTexture"); auto depthTexture = TexturePointer(Texture::create2D(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); - + depthTexture->setSource("Framebuffer::depthTexture"); framebuffer->setRenderBuffer(0, colorTexture); framebuffer->setDepthStencilBuffer(depthTexture, depthStencilBufferFormat); @@ -55,7 +57,7 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) { auto depthFormat = Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); // Depth32 texel format auto depthTexture = TexturePointer(Texture::create2D(depthFormat, width, width)); - + depthTexture->setSource("Framebuffer::shadowMap"); Sampler::Desc samplerDesc; samplerDesc._borderColor = glm::vec4(1.0f); samplerDesc._wrapModeU = Sampler::WRAP_BORDER; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 5bb983c7f9..b4803a7bcd 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -117,6 +117,7 @@ const unsigned char OPAQUE_BLACK[] = { 0x00, 0x00, 0x00, 0xFF }; const gpu::TexturePointer& TextureCache::getWhiteTexture() { if (!_whiteTexture) { _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _whiteTexture->setSource("TextureCache::_whiteTexture"); _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE); } return _whiteTexture; @@ -125,6 +126,7 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() { const gpu::TexturePointer& TextureCache::getGrayTexture() { if (!_grayTexture) { _grayTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _grayTexture->setSource("TextureCache::_grayTexture"); _grayTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_GRAY); } return _grayTexture; @@ -133,6 +135,7 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() { const gpu::TexturePointer& TextureCache::getBlueTexture() { if (!_blueTexture) { _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _blueTexture->setSource("TextureCache::_blueTexture"); _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE); } return _blueTexture; @@ -141,6 +144,7 @@ const gpu::TexturePointer& TextureCache::getBlueTexture() { const gpu::TexturePointer& TextureCache::getBlackTexture() { if (!_blackTexture) { _blackTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1)); + _blackTexture->setSource("TextureCache::_blackTexture"); _blackTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK); } return _blackTexture; diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index b7995c1b47..df9280c187 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -56,6 +56,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get()->getLightingTexture()->getTexelFormat(); auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); _antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); + _antialiasingTexture->setSource("Antialiasing::_antialiasingTexture"); _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); // Good to go add the brand new pipeline diff --git a/libraries/render-utils/src/DeferredFramebuffer.cpp b/libraries/render-utils/src/DeferredFramebuffer.cpp index 32f91f83d8..067687f9ef 100644 --- a/libraries/render-utils/src/DeferredFramebuffer.cpp +++ b/libraries/render-utils/src/DeferredFramebuffer.cpp @@ -54,9 +54,11 @@ void DeferredFramebuffer::allocate() { auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); _deferredColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - + _deferredColorTexture->setSource("DeferredFramebuffer::_deferredColorTexture"); _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(linearFormat, width, height, defaultSampler)); + _deferredNormalTexture->setSource("DeferredFramebuffer::_deferredNormalTexture"); _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); + _deferredSpecularTexture->setSource("DeferredFramebuffer::_deferredSpecularTexture"); _deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture); _deferredFramebuffer->setRenderBuffer(1, _deferredNormalTexture); @@ -67,6 +69,7 @@ void DeferredFramebuffer::allocate() { auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format if (!_primaryDepthTexture) { _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler)); + _primaryDepthTexture->setSource("DeferredFramebuffer::_primaryDepthTexture"); } _deferredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); @@ -77,6 +80,7 @@ void DeferredFramebuffer::allocate() { auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); _lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler)); + _lightingTexture->setSource("DeferredFramebuffer::_lightingTexture"); _lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _lightingFramebuffer->setRenderBuffer(0, _lightingTexture); _lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 8e83f737ea..ae1c3dafb8 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -345,6 +345,7 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, defaultSampler)); + primaryColorTexture->setSource("PreparePrimaryFramebuffer::primaryColorTexture"); _primaryFramebuffer->setRenderBuffer(0, primaryColorTexture); @@ -352,6 +353,7 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, defaultSampler)); + primaryDepthTexture->setSource("PreparePrimaryFramebuffer::primaryDepthTexture"); _primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 1957f8456a..e43afd94a9 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -74,6 +74,7 @@ void LinearDepthFramebuffer::allocate() { // For Linear Depth: _linearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _linearDepthTexture->setSource("LinearDepthFramebuffer::_linearDepthTexture"); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); @@ -81,10 +82,12 @@ void LinearDepthFramebuffer::allocate() { // For Downsampling: _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _halfLinearDepthTexture->setSource("LinearDepthFramebuffer::_halfLinearDepthTexture"); _halfLinearDepthTexture->autoGenerateMips(5); _halfNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _halfNormalTexture->setSource("LinearDepthFramebuffer::_halfNormalTexture"); _downsampleFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _downsampleFramebuffer->setRenderBuffer(0, _halfLinearDepthTexture); @@ -301,14 +304,17 @@ void SurfaceGeometryFramebuffer::allocate() { auto height = _frameSize.y; _curvatureTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _curvatureTexture->setSource("SurfaceGeometryFramebuffer::_curvatureTexture"); _curvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _curvatureFramebuffer->setRenderBuffer(0, _curvatureTexture); _lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _lowCurvatureTexture->setSource("SurfaceGeometryFramebuffer::_lowCurvatureTexture"); _lowCurvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _lowCurvatureFramebuffer->setRenderBuffer(0, _lowCurvatureTexture); _blurringTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _blurringTexture->setSource("SurfaceGeometryFramebuffer::_blurringTexture"); _blurringFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _blurringFramebuffer->setRenderBuffer(0, _blurringTexture); } diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 605ccf3787..238972dc00 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -368,7 +368,7 @@ public: sortedHighFrames.sort(); for (const auto& t : sortedHighFrames) { - //qDebug() << "Long frame " << t; + qDebug() << "Long frame " << t; } } From 82db3d5d55b32e00e406a58e52db5df503a0f4a7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Sep 2016 16:47:30 -0700 Subject: [PATCH 38/59] Disable support for framebuffer resizing --- libraries/gpu/src/gpu/Framebuffer.cpp | 12 ++++---- libraries/gpu/src/gpu/Framebuffer.h | 2 +- .../render-utils/src/AntialiasingEffect.cpp | 28 ++++++++++--------- .../src/DeferredLightingEffect.cpp | 10 +++++-- libraries/render/src/render/BlurTask.cpp | 24 ++++++---------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index 20e55b9ef9..8a97c0d748 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -115,29 +115,31 @@ void Framebuffer::updateSize(const TexturePointer& texture) { } } +#if 0 void Framebuffer::resize(uint16 width, uint16 height, uint16 numSamples) { if (width && height && numSamples && !isEmpty() && !isSwapchain()) { if ((width != _width) || (height != _height) || (numSamples != _numSamples)) { + _numSamples = numSamples; for (uint32 i = 0; i < _renderBuffers.size(); ++i) { if (_renderBuffers[i]) { - _renderBuffers[i]._texture->resize2D(width, height, numSamples); - _numSamples = _renderBuffers[i]._texture->getNumSamples(); + _renderBuffers[i]._texture->resize2D(width, height, _numSamples); + assert(_renderBuffers[i]._texture->getNumSamples() == _numSamples); ++_colorStamps[i]; } } if (_depthStencilBuffer) { - _depthStencilBuffer._texture->resize2D(width, height, numSamples); - _numSamples = _depthStencilBuffer._texture->getNumSamples(); + _depthStencilBuffer._texture->resize2D(width, height, _numSamples); + assert(_depthStencilBuffer._texture->getNumSamples() == _numSamples); ++_depthStamp; } _width = width; _height = height; - // _numSamples = numSamples; } } } +#endif uint16 Framebuffer::getWidth() const { if (isSwapchain()) { diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index 6fa6367c7d..f357cfc3f8 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -131,7 +131,7 @@ public: float getAspectRatio() const { return getWidth() / (float) getHeight() ; } // If not a swapchain canvas, resize can resize all the render buffers and depth stencil attached in one call - void resize( uint16 width, uint16 height, uint16 samples = 1 ); + //void resize( uint16 width, uint16 height, uint16 samples = 1 ); static const uint32 MAX_NUM_RENDER_BUFFERS = 8; static uint32 getMaxNumRenderBuffers() { return MAX_NUM_RENDER_BUFFERS; } diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index df9280c187..8147b06846 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -34,7 +34,21 @@ Antialiasing::Antialiasing() { const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { int width = DependencyManager::get()->getFrameBufferSize().width(); int height = DependencyManager::get()->getFrameBufferSize().height(); - + + if (_antialiasingBuffer && _antialiasingBuffer->getSize() != uvec2(width, height)) { + _antialiasingBuffer.reset(); + } + + if (!_antialiasingBuffer) { + // Link the antialiasing FBO to texture + _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get()->getLightingTexture()->getTexelFormat(); + auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); + _antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); + _antialiasingTexture->setSource("Antialiasing::_antialiasingTexture"); + _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); + } + if (!_antialiasingPipeline) { auto vs = gpu::Shader::createVertex(std::string(fxaa_vert)); auto ps = gpu::Shader::createPixel(std::string(fxaa_frag)); @@ -51,22 +65,10 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { state->setDepthTest(false, false, gpu::LESS_EQUAL); - // Link the antialiasing FBO to texture - _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); - auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get()->getLightingTexture()->getTexelFormat(); - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); - _antialiasingTexture->setSource("Antialiasing::_antialiasingTexture"); - _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); - // Good to go add the brand new pipeline _antialiasingPipeline = gpu::Pipeline::create(program, state); } - if (width != _antialiasingBuffer->getWidth() || height != _antialiasingBuffer->getHeight()) { - _antialiasingBuffer->resize(width, height); - } - return _antialiasingPipeline; } diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index ae1c3dafb8..808d765dd7 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -337,7 +337,13 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con auto framebufferCache = DependencyManager::get(); auto framebufferSize = framebufferCache->getFrameBufferSize(); - glm::ivec2 frameSize(framebufferSize.width(), framebufferSize.height()); + glm::uvec2 frameSize(framebufferSize.width(), framebufferSize.height()); + + // Resizing framebuffers instead of re-building them seems to cause issues with threaded + // rendering + if (_primaryFramebuffer && _primaryFramebuffer->getSize() != frameSize) { + _primaryFramebuffer.reset(); + } if (!_primaryFramebuffer) { _primaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); @@ -356,9 +362,7 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con primaryDepthTexture->setSource("PreparePrimaryFramebuffer::primaryDepthTexture"); _primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat); - } - _primaryFramebuffer->resize(frameSize.x, frameSize.y); primaryFramebuffer = _primaryFramebuffer; } diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 3849adf588..144e1e0058 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -96,6 +96,9 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra if (!sourceFramebuffer) { return false; } + if (_blurredFramebuffer && _blurredFramebuffer->getSize() != sourceFramebuffer->getSize()) { + _blurredFramebuffer.reset(); + } if (!_blurredFramebuffer) { _blurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); @@ -107,21 +110,17 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT); auto blurringTarget = gpu::TexturePointer(gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), blurringSampler)); _blurredFramebuffer->setRenderBuffer(0, blurringTarget); - } else { - // it would be easier to just call resize on the bluredFramebuffer and let it work if needed but the source might loose it's depth buffer when doing so - if ((_blurredFramebuffer->getWidth() != sourceFramebuffer->getWidth()) || (_blurredFramebuffer->getHeight() != sourceFramebuffer->getHeight())) { - _blurredFramebuffer->resize(sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), sourceFramebuffer->getNumSamples()); - //if (sourceFramebuffer->hasDepthStencil()) { - // _blurredFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat()); - //} - } - } + } blurringResources.sourceTexture = sourceFramebuffer->getRenderBuffer(0); blurringResources.blurringFramebuffer = _blurredFramebuffer; blurringResources.blurringTexture = _blurredFramebuffer->getRenderBuffer(0); if (_generateOutputFramebuffer) { + if (_outputFramebuffer && _outputFramebuffer->getSize() != sourceFramebuffer->getSize()) { + _outputFramebuffer.reset(); + } + // The job output the blur result in a new Framebuffer spawning here. // Let s make sure it s ready for this if (!_outputFramebuffer) { @@ -134,13 +133,6 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT); auto blurringTarget = gpu::TexturePointer(gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), blurringSampler)); _outputFramebuffer->setRenderBuffer(0, blurringTarget); - } else { - if ((_outputFramebuffer->getWidth() != sourceFramebuffer->getWidth()) || (_outputFramebuffer->getHeight() != sourceFramebuffer->getHeight())) { - _outputFramebuffer->resize(sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), sourceFramebuffer->getNumSamples()); - /* if (sourceFramebuffer->hasDepthStencil()) { - _outputFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat()); - }*/ - } } // Should be good to use the output Framebuffer as final From f33d3a3b36169c671330c43434e28134bd30467d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Sep 2016 13:24:03 -0700 Subject: [PATCH 39/59] Migrate logging functionality to shared library --- interface/src/Application.h | 2 +- interface/src/ui/LogDialog.h | 2 +- interface/src/ui/Snapshot.cpp | 2 +- libraries/networking/src/HifiSockAddr.cpp | 30 ------------- libraries/networking/src/HifiSockAddr.h | 3 -- libraries/networking/src/LimitedNodeList.cpp | 1 + .../src/shared}/AbstractLoggerInterface.h | 0 .../shared/src/shared}/FileLogger.cpp | 9 ++-- .../shared/src/shared}/FileLogger.h | 2 +- .../shared/src/shared}/FileUtils.cpp | 0 .../shared/src/shared}/FileUtils.h | 0 libraries/shared/src/shared/NetworkUtils.cpp | 42 +++++++++++++++++++ libraries/shared/src/shared/NetworkUtils.h | 17 ++++++++ tests/render-perf/src/main.cpp | 22 ++++++---- 14 files changed, 83 insertions(+), 49 deletions(-) rename {interface/src => libraries/shared/src/shared}/AbstractLoggerInterface.h (100%) rename {interface/src => libraries/shared/src/shared}/FileLogger.cpp (97%) rename {interface/src => libraries/shared/src/shared}/FileLogger.h (97%) rename {interface/src => libraries/shared/src/shared}/FileUtils.cpp (100%) rename {interface/src => libraries/shared/src/shared}/FileUtils.h (100%) create mode 100644 libraries/shared/src/shared/NetworkUtils.cpp create mode 100644 libraries/shared/src/shared/NetworkUtils.h diff --git a/interface/src/Application.h b/interface/src/Application.h index 4c52ff8526..2dc60c2438 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -44,12 +44,12 @@ #include #include #include +#include #include "avatar/MyAvatar.h" #include "Bookmarks.h" #include "Camera.h" #include "ConnectionMonitor.h" -#include "FileLogger.h" #include "gpu/Context.h" #include "Menu.h" #include "octree/OctreePacketProcessor.h" diff --git a/interface/src/ui/LogDialog.h b/interface/src/ui/LogDialog.h index c38cf84f00..1493a43f01 100644 --- a/interface/src/ui/LogDialog.h +++ b/interface/src/ui/LogDialog.h @@ -20,7 +20,7 @@ #include #include -#include "AbstractLoggerInterface.h" +#include class KeywordHighlighter : public QSyntaxHighlighter { Q_OBJECT diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index c2fcafb2f3..a0ee260bcc 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index bde0aca7b7..d6981f420d 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -124,36 +124,6 @@ QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr) { return dataStream; } -QHostAddress getGuessedLocalAddress() { - - QHostAddress localAddress; - - foreach(const QNetworkInterface &networkInterface, QNetworkInterface::allInterfaces()) { - if (networkInterface.flags() & QNetworkInterface::IsUp - && networkInterface.flags() & QNetworkInterface::IsRunning - && networkInterface.flags() & ~QNetworkInterface::IsLoopBack) { - // we've decided that this is the active NIC - // enumerate it's addresses to grab the IPv4 address - foreach(const QNetworkAddressEntry &entry, networkInterface.addressEntries()) { - // make sure it's an IPv4 address that isn't the loopback - if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) { - - // set our localAddress and break out - localAddress = entry.ip(); - break; - } - } - } - - if (!localAddress.isNull()) { - break; - } - } - - // return the looked up local address - return localAddress; -} - uint qHash(const HifiSockAddr& key, uint seed) { // use the existing QHostAddress and quint16 hash functions to get our hash return qHash(key.getAddress(), seed) ^ qHash(key.getPort(), seed); diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 063a41a202..c4ff8cb246 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -91,9 +91,6 @@ namespace std { }; } - -QHostAddress getGuessedLocalAddress(); - Q_DECLARE_METATYPE(HifiSockAddr); #endif // hifi_HifiSockAddr_h diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index ec4b2c3573..ce555315e8 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include diff --git a/interface/src/AbstractLoggerInterface.h b/libraries/shared/src/shared/AbstractLoggerInterface.h similarity index 100% rename from interface/src/AbstractLoggerInterface.h rename to libraries/shared/src/shared/AbstractLoggerInterface.h diff --git a/interface/src/FileLogger.cpp b/libraries/shared/src/shared/FileLogger.cpp similarity index 97% rename from interface/src/FileLogger.cpp rename to libraries/shared/src/shared/FileLogger.cpp index 754fa7f474..ef3436a8d7 100644 --- a/interface/src/FileLogger.cpp +++ b/libraries/shared/src/shared/FileLogger.cpp @@ -15,11 +15,12 @@ #include #include -#include -#include -#include +#include "FileUtils.h" +#include "NetworkUtils.h" + +#include "../NumericalConstants.h" +#include "../SharedUtil.h" -#include "HifiSockAddr.h" static const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt"; static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; diff --git a/interface/src/FileLogger.h b/libraries/shared/src/shared/FileLogger.h similarity index 97% rename from interface/src/FileLogger.h rename to libraries/shared/src/shared/FileLogger.h index 950590e789..697b96c6d8 100644 --- a/interface/src/FileLogger.h +++ b/libraries/shared/src/shared/FileLogger.h @@ -13,7 +13,7 @@ #define hifi_FileLogger_h #include "AbstractLoggerInterface.h" -#include +#include "../GenericQueueThread.h" #include diff --git a/interface/src/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp similarity index 100% rename from interface/src/FileUtils.cpp rename to libraries/shared/src/shared/FileUtils.cpp diff --git a/interface/src/FileUtils.h b/libraries/shared/src/shared/FileUtils.h similarity index 100% rename from interface/src/FileUtils.h rename to libraries/shared/src/shared/FileUtils.h diff --git a/libraries/shared/src/shared/NetworkUtils.cpp b/libraries/shared/src/shared/NetworkUtils.cpp new file mode 100644 index 0000000000..50356d30fc --- /dev/null +++ b/libraries/shared/src/shared/NetworkUtils.cpp @@ -0,0 +1,42 @@ +// +// Created by Bradley Austin Davis on 2016/09/20 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "NetworkUtils.h" +#include + +QHostAddress getGuessedLocalAddress() { + + QHostAddress localAddress; + + foreach(const QNetworkInterface &networkInterface, QNetworkInterface::allInterfaces()) { + if (networkInterface.flags() & QNetworkInterface::IsUp + && networkInterface.flags() & QNetworkInterface::IsRunning + && networkInterface.flags() & ~QNetworkInterface::IsLoopBack) { + // we've decided that this is the active NIC + // enumerate it's addresses to grab the IPv4 address + foreach(const QNetworkAddressEntry &entry, networkInterface.addressEntries()) { + // make sure it's an IPv4 address that isn't the loopback + if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) { + + // set our localAddress and break out + localAddress = entry.ip(); + break; + } + } + } + + if (!localAddress.isNull()) { + break; + } + } + + // return the looked up local address + return localAddress; +} + + diff --git a/libraries/shared/src/shared/NetworkUtils.h b/libraries/shared/src/shared/NetworkUtils.h new file mode 100644 index 0000000000..b881441004 --- /dev/null +++ b/libraries/shared/src/shared/NetworkUtils.h @@ -0,0 +1,17 @@ +// +// Created by Bradley Austin Davis on 2016/09/20 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_shared_NetworkUtils_h +#define hifi_shared_NetworkUtils_h + +#include + +QHostAddress getGuessedLocalAddress(); + +#endif // hifi_shared_NetworkUtils_h diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 238972dc00..4745529f19 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -32,12 +32,11 @@ #include +#include +#include +#include #include -//#include -//#include -//#include - #include #include #include @@ -149,6 +148,7 @@ public: }; #else + class QWindowCamera : public Camera { Key forKey(int key) { switch (key) { @@ -417,6 +417,7 @@ public: }; render::ItemID BackgroundRenderData::_item = 0; +QSharedPointer logger; namespace render { template <> const ItemKey payloadGetKey(const BackgroundRenderData::Pointer& stuff) { @@ -1069,12 +1070,14 @@ private: bool QTestWindow::_cullingEnabled = true; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - if (!message.isEmpty()) { + QString logMessage = LogHandler::getInstance().printMessage((LogMsgType)type, context, message); + + if (!logMessage.isEmpty()) { #ifdef Q_OS_WIN - OutputDebugStringA(message.toLocal8Bit().constData()); + OutputDebugStringA(logMessage.toLocal8Bit().constData()); OutputDebugStringA("\n"); #endif - std::cout << message.toLocal8Bit().constData() << std::endl; + logger->addMessage(qPrintable(logMessage + "\n")); } } @@ -1082,11 +1085,14 @@ const char * LOG_FILTER_RULES = R"V0G0N( hifi.gpu=true )V0G0N"; + int main(int argc, char** argv) { - QApplication app(argc, argv); + QGuiApplication app(argc, argv); QCoreApplication::setApplicationName("RenderPerf"); QCoreApplication::setOrganizationName("High Fidelity"); QCoreApplication::setOrganizationDomain("highfidelity.com"); + logger.reset(new FileLogger()); + qInstallMessageHandler(messageHandler); QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow::setup(); From 2a058fa4196352a19a00d57274c29e307a6e2727 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Sep 2016 15:44:55 -0700 Subject: [PATCH 40/59] Add file logging to perf tool --- libraries/shared/src/shared/FileUtils.cpp | 25 +++- libraries/shared/src/shared/FileUtils.h | 3 +- tests/render-perf/src/main.cpp | 144 +++++++++++++--------- 3 files changed, 110 insertions(+), 62 deletions(-) diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp index a89a873bea..8c962dfd6d 100644 --- a/libraries/shared/src/shared/FileUtils.cpp +++ b/libraries/shared/src/shared/FileUtils.cpp @@ -9,14 +9,29 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include -#include -#include #include "FileUtils.h" +#include +#include +#include +#include +#include +#include +#include + + +QString FileUtils::readFile(const QString& filename) { + QFile file(filename); + file.open(QFile::Text | QFile::ReadOnly); + QString result; + result.append(QTextStream(&file).readAll()); + return result; +} + +QStringList FileUtils::readLines(const QString& filename, QString::SplitBehavior splitBehavior) { + return readFile(filename).split(QRegularExpression("[\\r\\n]"), QString::SkipEmptyParts); +} void FileUtils::locateFile(QString filePath) { diff --git a/libraries/shared/src/shared/FileUtils.h b/libraries/shared/src/shared/FileUtils.h index d2b30595a7..4f2c1b7af5 100644 --- a/libraries/shared/src/shared/FileUtils.h +++ b/libraries/shared/src/shared/FileUtils.h @@ -19,7 +19,8 @@ class FileUtils { public: static void locateFile(QString fileName); static QString standardPath(QString subfolder); - + static QString readFile(const QString& filename); + static QStringList readLines(const QString& filename, QString::SplitBehavior splitBehavior = QString::KeepEmptyParts); }; #endif // hifi_FileUtils_h diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 4745529f19..bf1ec283cf 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include @@ -98,57 +100,6 @@ public: } }; -#if 0 -class GlfwCamera : public Camera { - Key forKey(int key) { - switch (key) { - case GLFW_KEY_W: return FORWARD; - case GLFW_KEY_S: return BACK; - case GLFW_KEY_A: return LEFT; - case GLFW_KEY_D: return RIGHT; - case GLFW_KEY_E: return UP; - case GLFW_KEY_C: return DOWN; - case GLFW_MOUSE_BUTTON_LEFT: return MLEFT; - case GLFW_MOUSE_BUTTON_RIGHT: return MRIGHT; - case GLFW_MOUSE_BUTTON_MIDDLE: return MMIDDLE; - default: break; - } - return INVALID; - } - - vec2 _lastMouse; -public: - void keyHandler(int key, int scancode, int action, int mods) { - Key k = forKey(key); - if (k == INVALID) { - return; - } - if (action == GLFW_PRESS) { - keys.set(k); - } else if (action == GLFW_RELEASE) { - keys.reset(k); - } - } - - //static void MouseMoveHandler(GLFWwindow* window, double posx, double posy); - //static void MouseScrollHandler(GLFWwindow* window, double xoffset, double yoffset); - void onMouseMove(double posx, double posy) { - vec2 mouse = vec2(posx, posy); - vec2 delta = mouse - _lastMouse; - if (keys.at(Key::MRIGHT)) { - dolly(delta.y * 0.01f); - } else if (keys.at(Key::MLEFT)) { - rotate(delta.x * -0.01f); - } else if (keys.at(Key::MMIDDLE)) { - delta.y *= -1.0f; - translate(delta * -0.01f); - } - _lastMouse = mouse; - } - -}; -#else - class QWindowCamera : public Camera { Key forKey(int key) { switch (key) { @@ -198,7 +149,6 @@ public: _lastMouse = mouse; } }; -#endif static QString toHumanSize(size_t size, size_t maxUnit = std::numeric_limits::max()) { static const std::vector SUFFIXES{ { "B", "KB", "MB", "GB", "TB", "PB" } }; @@ -241,7 +191,7 @@ public: std::mutex _mutex; std::shared_ptr _backend; std::vector _frameTimes; - size_t _frameIndex; + size_t _frameIndex { 0 }; std::mutex _frameLock; std::queue _pendingFrames; gpu::FramePointer _activeFrame; @@ -253,7 +203,6 @@ public: _pendingFrames.push(frame); } - void initialize(QWindow* window, gl::Context& initContext) { setObjectName("RenderThread"); _context.setWindow(window); @@ -372,7 +321,6 @@ public: } } - bool process() override { std::queue pendingFrames; { @@ -590,6 +538,16 @@ public: DependencyManager::destroy(); } + void loadCommands(const QString& filename) { + QFileInfo fileInfo(filename); + if (!fileInfo.exists()) { + return; + } + _commandPath = fileInfo.absolutePath(); + _commands = FileUtils::readLines(filename); + _commandIndex = 0; + } + protected: bool eventFilter(QObject *obj, QEvent *event) override { @@ -780,10 +738,76 @@ private: #endif } + void runCommand(const QString& command) { + QStringList commandParams = command.split(QRegularExpression(QString("\\s"))); + QString verb = commandParams[0].toLower(); + if (verb == "loop") { + if (commandParams.length() > 1) { + int maxLoops = commandParams[1].toInt(); + if (maxLoops > ++_commandLoops) { + qDebug() << "Exceeded loop count"; + return; + } + } + _commandIndex = 0; + } else if (verb == "wait") { + if (commandParams.length() < 2) { + qDebug() << "No wait time specified"; + return; + } + int seconds = commandParams[1].toInt(); + _nextCommandTime = usecTimestampNow() + seconds * USECS_PER_SECOND; + } else if (verb == "load") { + if (commandParams.length() < 2) { + qDebug() << "No load file specified"; + return; + } + QString file = commandParams[1]; + if (QFileInfo(file).isRelative()) { + file = _commandPath + "/" + file; + } + if (!QFileInfo(file).exists()) { + qDebug() << "Cannot find scene file " + file; + return; + } + + importScene(file); + } else if (verb == "go") { + if (commandParams.length() < 2) { + qDebug() << "No destination specified for go command"; + return; + } + parsePath(commandParams[1]); + } else { + qDebug() << "Unknown command " << command; + } + } + + void runNextCommand(quint64 now) { + if (_commands.empty()) { + return; + } + + if (_commandIndex >= _commands.size()) { + _commands.clear(); + return; + } + + if (now < _nextCommandTime) { + return; + } + + _nextCommandTime = 0; + QString command = _commands[_commandIndex++]; + runCommand(command); + } + void update() { auto now = usecTimestampNow(); static auto last = now; + runNextCommand(now); + float delta = now - last; // Update the camera _camera.update(delta / USECS_PER_SECOND); @@ -936,7 +960,6 @@ private: QString atpUrl = QUrl::fromLocalFile(atpPath).toString(); ResourceManager::setUrlPrefixOverride("atp:/", atpUrl + "/"); } - _settings.setValue(LAST_SCENE_KEY, fileName); _octree->clear(); _octree->getTree()->readFromURL(fileName); } @@ -955,6 +978,7 @@ private: if (fileName.isNull()) { return; } + _settings.setValue(LAST_SCENE_KEY, fileName); importScene(fileName); } @@ -1054,6 +1078,13 @@ private: model::SunSkyStage _sunSkyStage; model::LightPointer _globalLight { std::make_shared() }; bool _ready { false }; + + QStringList _commands; + QString _commandPath; + int _commandLoops { 0 }; + int _commandIndex { -1 }; + uint64_t _nextCommandTime { 0 }; + //TextOverlay* _textOverlay; static bool _cullingEnabled; @@ -1097,6 +1128,7 @@ int main(int argc, char** argv) { QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow::setup(); QTestWindow window; + window.loadCommands("C:/Users/bdavis/Git/dreaming/exports/commands.txt"); app.exec(); return 0; } From d382305174b1d4d2d30042b1a68c94c1f61eb8c7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Sep 2016 15:46:04 -0700 Subject: [PATCH 41/59] Add fence tracking and max values tracking to gpu::Context --- libraries/gpu/src/gpu/Context.cpp | 46 +++++++++++++++++++++++++------ libraries/gpu/src/gpu/Context.h | 7 ++++- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 68a6be1fe5..af3552911c 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -10,6 +10,7 @@ // #include "Context.h" #include "Frame.h" +#include "GPULogging.h" using namespace gpu; Context::CreateBackend Context::_createBackendCallback = nullptr; @@ -161,8 +162,9 @@ Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const S } // Counters for Buffer and Texture usage in GPU/Context -std::atomic Context::_bufferGPUCount{ 0 }; -std::atomic Context::_bufferGPUMemoryUsage{ 0 }; +std::atomic Context::_fenceCount { 0 }; +std::atomic Context::_bufferGPUCount { 0 }; +std::atomic Context::_bufferGPUMemoryUsage { 0 }; std::atomic Context::_textureGPUCount{ 0 }; std::atomic Context::_textureGPUMemoryUsage{ 0 }; @@ -170,10 +172,15 @@ std::atomic Context::_textureGPUVirtualMemoryUsage{ 0 }; std::atomic Context::_textureGPUTransferCount{ 0 }; void Context::incrementBufferGPUCount() { - _bufferGPUCount++; + static std::atomic max { 0 }; + auto total = ++_bufferGPUCount; + if (total > max.load()) { + max = total; + qCDebug(gpulogging) << "New max GPU buffers " << total; + } } void Context::decrementBufferGPUCount() { - _bufferGPUCount--; + --_bufferGPUCount; } void Context::updateBufferGPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { if (prevObjectSize == newObjectSize) { @@ -186,12 +193,30 @@ void Context::updateBufferGPUMemoryUsage(Size prevObjectSize, Size newObjectSize } } +void Context::incrementFenceCount() { + static std::atomic max { 0 }; + auto total = ++_fenceCount; + if (total > max.load()) { + max = total; + qCDebug(gpulogging) << "New max Fences " << total; + } +} +void Context::decrementFenceCount() { + --_fenceCount; +} + void Context::incrementTextureGPUCount() { - _textureGPUCount++; + static std::atomic max { 0 }; + auto total = ++_textureGPUCount; + if (total > max.load()) { + max = total; + qCDebug(gpulogging) << "New max GPU textures " << total; + } } void Context::decrementTextureGPUCount() { - _textureGPUCount--; + --_textureGPUCount; } + void Context::updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { if (prevObjectSize == newObjectSize) { return; @@ -215,10 +240,15 @@ void Context::updateTextureGPUVirtualMemoryUsage(Size prevObjectSize, Size newOb } void Context::incrementTextureGPUTransferCount() { - _textureGPUTransferCount++; + static std::atomic max { 0 }; + auto total = ++_textureGPUTransferCount; + if (total > max.load()) { + max = total; + qCDebug(gpulogging) << "New max GPU textures transfers" << total; + } } void Context::decrementTextureGPUTransferCount() { - _textureGPUTransferCount--; + --_textureGPUTransferCount; } uint32_t Context::getBufferGPUCount() { diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 42b81606db..4c701e170d 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -230,6 +230,9 @@ protected: static void decrementBufferGPUCount(); static void updateBufferGPUMemoryUsage(Size prevObjectSize, Size newObjectSize); + static void incrementFenceCount(); + static void decrementFenceCount(); + static void incrementTextureGPUCount(); static void decrementTextureGPUCount(); static void updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize); @@ -237,7 +240,9 @@ protected: static void incrementTextureGPUTransferCount(); static void decrementTextureGPUTransferCount(); - // Buffer and Texture Counters + // Buffer, Texture and Fence Counters + static std::atomic _fenceCount; + static std::atomic _bufferGPUCount; static std::atomic _bufferGPUMemoryUsage; From ee476a604866369fbcb20b3db1610638c6f61b2b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Sep 2016 19:45:50 -0700 Subject: [PATCH 42/59] GLTextures store the source for easier debugging --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 1 + libraries/gpu-gl/src/gpu/gl/GLTexture.h | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index c2d6b3eb54..16ee81592a 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -135,6 +135,7 @@ float GLTexture::getMemoryPressure() { // Create the texture and allocate storage GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : GLObject(backend, texture, id), + _source(texture.source()), _storageStamp(texture.getStamp()), _target(getGLTextureType(texture)), _internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())), diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index fa893ae52a..4375d0644f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -111,6 +111,7 @@ public: ~GLTexture(); const GLuint& _texture { _id }; + const std::string _source; const Stamp _storageStamp; const GLenum _target; const GLenum _internalFormat; From 255e9e6435ca8b1d90c4479b98069d44eb191a0d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Sep 2016 19:46:26 -0700 Subject: [PATCH 43/59] Add support for executing arbitrary commands on the texture transfer thread --- libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 15 +++++++++++++-- libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h | 5 +++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp index 4cb8e4fad1..7a709dfce3 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp @@ -97,12 +97,23 @@ void GLTextureTransferHelper::shutdown() { #endif } +void GLTextureTransferHelper::queueExecution(VoidLambda lambda) { + Lock lock(_mutex); + _pendingCommands.push_back(lambda); +} + bool GLTextureTransferHelper::process() { - // Take any new textures off the queue + // Take any new textures or commands off the queue + VoidLambdaList pendingCommands; TextureList newTransferTextures; { Lock lock(_mutex); newTransferTextures.swap(_pendingTextures); + pendingCommands.swap(_pendingCommands); + } + + for (auto command : pendingCommands) { + command(); } if (!newTransferTextures.empty()) { @@ -144,7 +155,7 @@ bool GLTextureTransferHelper::process() { gltexture->finishTransfer(); //glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, gltexture->_id, 0); //glBlitNamedFramebuffer(_readFramebuffer, _drawFramebuffer, 0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); - //clientWait(); + clientWait(); gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp(); gltexture->updateSize(); gltexture->setSyncState(gpu::gl::GLSyncState::Transferred); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h index fe7414e370..289aec40bb 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.h @@ -28,10 +28,13 @@ using TextureListIterator = TextureList::iterator; class GLTextureTransferHelper : public GenericThread { public: + using VoidLambda = std::function; + using VoidLambdaList = std::list; using Pointer = std::shared_ptr; GLTextureTransferHelper(); ~GLTextureTransferHelper(); void transferTexture(const gpu::TexturePointer& texturePointer); + void queueExecution(VoidLambda lambda); void setup() override; void shutdown() override; @@ -47,6 +50,8 @@ private: #endif // A mutex for protecting items access on the render and transfer threads Mutex _mutex; + // Commands that have been submitted for execution on the texture transfer thread + VoidLambdaList _pendingCommands; // Textures that have been submitted for transfer TextureList _pendingTextures; // Textures currently in the transfer process From 28bb82c8a34112add8e40fe5c75335d587a80a0b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Sep 2016 19:47:31 -0700 Subject: [PATCH 44/59] Force all sparse allocation and deallocation onto one thread / context --- .../src/gpu/gl45/GL45BackendTexture.cpp | 82 +++++++++++-------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 09c6003ced..886c7b8145 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -94,7 +94,7 @@ SparseInfo::SparseInfo(GL45Texture& texture) void SparseInfo::maybeMakeSparse() { // Don't enable sparse for objects with explicitly managed mip levels if (!_texture._gpuObject.isAutogenerateMips()) { - qCDebug(gpugl45logging) << "Don't enable sparse texture for explicitly generated mipmaps on texture " << _texture._gpuObject.source().c_str(); + qCDebug(gpugl45logging) << "Don't enable sparse texture for explicitly generated mipmaps on texture " << _texture._source.c_str(); return; } @@ -106,7 +106,7 @@ void SparseInfo::maybeMakeSparse() { _pageDimensions = allowedPageDimensions[i]; // Is this texture an integer multiple of page dimensions? if (uvec3(0) == (dimensions % _pageDimensions)) { - qCDebug(gpugl45logging) << "Enabling sparse for texture " << _texture._gpuObject.source().c_str(); + qCDebug(gpugl45logging) << "Enabling sparse for texture " << _texture._source.c_str(); _sparse = true; break; } @@ -117,7 +117,7 @@ void SparseInfo::maybeMakeSparse() { glTextureParameteri(_texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, _pageDimensionsIndex); } else { qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y << - " is not supported by any sparse page size for texture" << _texture._gpuObject.source().c_str(); + " is not supported by any sparse page size for texture" << _texture._source.c_str(); } } @@ -255,40 +255,56 @@ GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& } GL45Texture::~GL45Texture() { + qCDebug(gpugl45logging) << "Destroying texture " << _id << " from source " << _source.c_str(); if (_sparseInfo._sparse) { - auto backend = _backend.lock(); - if (backend) { - auto id = _id; + // Remove this texture from the candidate list of derezzable textures + { auto mipLevels = usedMipLevels(); - { - Lock lock(texturesByMipCountsMutex); - if (texturesByMipCounts.count(mipLevels)) { - auto& textures = texturesByMipCounts[mipLevels]; - textures.erase(this); - if (textures.empty()) { - texturesByMipCounts.erase(mipLevels); - } + Lock lock(texturesByMipCountsMutex); + if (texturesByMipCounts.count(mipLevels)) { + auto& textures = texturesByMipCounts[mipLevels]; + textures.erase(this); + if (textures.empty()) { + texturesByMipCounts.erase(mipLevels); } } - - auto maxSparseMip = std::min(_maxMip, _sparseInfo._maxSparseLevel); - uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { - auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); - // Destructors get called on the main thread, potentially without a context active. - // We need to queue the deallocation of the sparse pages for this content. - backend->releaseLambda([=] { - glTexturePageCommitmentEXT(id, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); - }); - auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; - assert(deallocatedPages <= _allocatedPages); - _allocatedPages -= deallocatedPages; - } - - if (0 != _allocatedPages) { - qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _allocatedPages; - } } + + // Experimenation suggests that allocating sparse textures on one context/thread and deallocating + // them on another is buggy. So for sparse textures we need to queue a lambda with the deallocation + // callls to the transfer thread + auto id = _id; + // Set the class _id to 0 so we don't try to double delete + const_cast(_id) = 0; + std::list> destructionFunctions; + + auto minMip = _minMip; + uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + auto maxSparseMip = std::min(_maxMip, _sparseInfo._maxSparseLevel); + for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { + auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); + destructionFunctions.push_back([id, maxFace, mipLevel, mipDimensions] { + glTexturePageCommitmentEXT(id, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); + }); + + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; + assert(deallocatedPages <= _allocatedPages); + _allocatedPages -= deallocatedPages; + } + + if (0 != _allocatedPages) { + qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _allocatedPages; + } + + auto size = _size; + _textureTransferHelper->queueExecution([id, size, destructionFunctions] { + for (auto function : destructionFunctions) { + function(); + } + glDeleteTextures(1, &id); + Backend::decrementTextureGPUCount(); + Backend::updateTextureGPUMemoryUsage(size, 0); + }); } } @@ -297,7 +313,7 @@ void GL45Texture::withPreservedTexture(std::function f) const { } void GL45Texture::generateMips() const { - qCDebug(gpugl45logging) << "Generating mipmaps for " << _gpuObject.source().c_str(); + qCDebug(gpugl45logging) << "Generating mipmaps for " << _source.c_str(); glGenerateTextureMipmap(_id); (void)CHECK_GL_ERROR(); } From 5dbaa671ca2c93df8393c63533fbee1cb63051af Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Sep 2016 19:48:19 -0700 Subject: [PATCH 45/59] Add some more debugging in render perf test --- tests/render-perf/src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index bf1ec283cf..1bc042d319 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -739,6 +739,7 @@ private: } void runCommand(const QString& command) { + qDebug() << "Running command: " << command; QStringList commandParams = command.split(QRegularExpression(QString("\\s"))); QString verb = commandParams[0].toLower(); if (verb == "loop") { From 6711b9c41792f436214af9dae8fb9fbb419b0fdf Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Sep 2016 19:48:49 -0700 Subject: [PATCH 46/59] Restore proper mip handling to skybox --- libraries/model/src/model/skybox.slf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model/src/model/skybox.slf b/libraries/model/src/model/skybox.slf index c0770525f7..7b25e36af7 100755 --- a/libraries/model/src/model/skybox.slf +++ b/libraries/model/src/model/skybox.slf @@ -48,7 +48,7 @@ void main(void) { // blend is only set if there is a cubemap if (skybox.color.a > 0.0) { - color = textureLod(cubeMap, coord, 0).rgb; + color = texture(cubeMap, coord).rgb; if (skybox.color.a < 1.0) { color *= skybox.color.rgb; } From 09a218b9fd0da9eb64659b802ea8473387521b14 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Sep 2016 22:51:48 -0700 Subject: [PATCH 47/59] Fix build after moving network function --- assignment-client/src/octree/OctreeServer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 48ffc2fdbc..3e36250a82 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include From cfa5376effded42351975103a5d187e4946a2392 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Sep 2016 07:25:44 -0700 Subject: [PATCH 48/59] Straight up bug --- libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 906144e013..f18962976c 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -165,7 +165,7 @@ void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { _uniform._buffers[slot] = uniformBuffer; (void) CHECK_GL_ERROR(); } else { - releaseResourceTexture(slot); + releaseUniformBuffer(slot); return; } } From c52de6c685ec9186748c7b850acc1eeac46f667a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Sep 2016 07:34:22 -0700 Subject: [PATCH 49/59] Fixing mip deallocation --- .../gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 886c7b8145..de61952a93 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -476,16 +476,15 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { + auto id = _id; auto mipDimensions = _gpuObject.evalMipDimensions(mip); - auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions); - for (uint8_t face = 0; face < maxFace; ++face) { - glTexturePageCommitmentEXT(_id, mip, - 0, 0, face, - mipDimensions.x, mipDimensions.y, mipDimensions.z, - GL_FALSE); - assert(deallocatedPages < _allocatedPages); - _allocatedPages -= deallocatedPages; - } + _textureTransferHelper->queueExecution([id, mip, mipDimensions, maxFace] { + glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); + }); + + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; + assert(deallocatedPages < _allocatedPages); + _allocatedPages -= deallocatedPages; } _minMip = newMinMip; From 2de7f62b9563839a8786776228fdb111c31b93da Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Sep 2016 07:36:58 -0700 Subject: [PATCH 50/59] Fixing loop logic --- tests/render-perf/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 1bc042d319..660ffd6994 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -745,7 +745,7 @@ private: if (verb == "loop") { if (commandParams.length() > 1) { int maxLoops = commandParams[1].toInt(); - if (maxLoops > ++_commandLoops) { + if (maxLoops < ++_commandLoops) { qDebug() << "Exceeded loop count"; return; } From bca9da48fc4acb5cb3534b0c6b3ca4a84831bee7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Sep 2016 07:43:12 -0700 Subject: [PATCH 51/59] Fix warnings --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index de61952a93..43aa257028 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -278,7 +278,6 @@ GL45Texture::~GL45Texture() { const_cast(_id) = 0; std::list> destructionFunctions; - auto minMip = _minMip; uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); auto maxSparseMip = std::min(_maxMip, _sparseInfo._maxSparseLevel); for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { From 2886c51a29a327cc11dffab70988ce2c8dc91771 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Sep 2016 11:01:51 -0700 Subject: [PATCH 52/59] Fixing use of widgets in test app --- tests/render-perf/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 660ffd6994..5ac82bdf29 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -1119,7 +1119,7 @@ hifi.gpu=true int main(int argc, char** argv) { - QGuiApplication app(argc, argv); + QApplication app(argc, argv); QCoreApplication::setApplicationName("RenderPerf"); QCoreApplication::setOrganizationName("High Fidelity"); QCoreApplication::setOrganizationDomain("highfidelity.com"); From 44a549317ece2086bb6d59faafe79462e748786e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Sep 2016 11:45:18 -0700 Subject: [PATCH 53/59] Reduce number of texture transfers per clientWait, process smaller textures first --- libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp index 7a709dfce3..84bf278939 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp @@ -102,6 +102,8 @@ void GLTextureTransferHelper::queueExecution(VoidLambda lambda) { _pendingCommands.push_back(lambda); } +#define MAX_TRANSFERS_PER_PASS 2 + bool GLTextureTransferHelper::process() { // Take any new textures or commands off the queue VoidLambdaList pendingCommands; @@ -126,6 +128,9 @@ bool GLTextureTransferHelper::process() { _transferringTextures.push_back(texturePointer); _textureIterator = _transferringTextures.begin(); } + _transferringTextures.sort([](const gpu::TexturePointer& a, const gpu::TexturePointer& b)->bool { + return a->getSize() < b->getSize(); + }); } // No transfers in progress, sleep @@ -144,7 +149,11 @@ bool GLTextureTransferHelper::process() { qDebug() << "Texture list " << _transferringTextures.size(); } - for (auto _textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) { + size_t transferCount = 0; + for (_textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) { + if (++transferCount > MAX_TRANSFERS_PER_PASS) { + break; + } auto texture = *_textureIterator; GLTexture* gltexture = Backend::getGPUObject(*texture); if (gltexture->continueTransfer()) { From 74a3d7fcb9f52b1d00de519d69c4ffdde192d167 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Sep 2016 15:02:02 -0700 Subject: [PATCH 54/59] Flush executed commands on the transfer thread --- libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp index 84bf278939..dbe022df2c 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp @@ -114,8 +114,11 @@ bool GLTextureTransferHelper::process() { pendingCommands.swap(_pendingCommands); } - for (auto command : pendingCommands) { - command(); + if (!pendingCommands.empty()) { + for (auto command : pendingCommands) { + command(); + } + glFlush(); } if (!newTransferTextures.empty()) { From ab8a67e84daec061ad52d3197205c137107f83ce Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Sep 2016 10:17:23 -0700 Subject: [PATCH 55/59] Code review comments --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 6 +- libraries/gpu-gl/src/gpu/gl/GLBackend.h | 4 +- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 9 +- .../gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 4 +- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 41 ++--- .../src/gpu/gl45/GL45BackendTexture.cpp | 167 +++++++++--------- libraries/gpu/src/gpu/Framebuffer.cpp | 26 --- libraries/gpu/src/gpu/Framebuffer.h | 3 - 8 files changed, 114 insertions(+), 146 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 72a953c5f3..f070dfe637 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -593,9 +593,9 @@ void GLBackend::releaseQuery(GLuint id) const { _queriesTrash.push_back(id); } -void GLBackend::releaseLambda(const std::function lambda) const { +void GLBackend::queueLambda(const std::function lambda) const { Lock lock(_trashMutex); - _lambdasTrash.push_back(lambda); + _lambdaQueue.push_back(lambda); } void GLBackend::recycle() const { @@ -603,7 +603,7 @@ void GLBackend::recycle() const { std::list> lamdbasTrash; { Lock lock(_trashMutex); - std::swap(_lambdasTrash, lamdbasTrash); + std::swap(_lambdaQueue, lamdbasTrash); } for (auto lambda : lamdbasTrash) { lambda(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 545ae6cbdc..af4851a1b0 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -175,7 +175,7 @@ public: virtual void releaseShader(GLuint id) const; virtual void releaseProgram(GLuint id) const; virtual void releaseQuery(GLuint id) const; - virtual void releaseLambda(const std::function lambda) const; + virtual void queueLambda(const std::function lambda) const; protected: @@ -198,7 +198,7 @@ protected: mutable std::list _shadersTrash; mutable std::list _programsTrash; mutable std::list _queriesTrash; - mutable std::list> _lambdasTrash; + mutable std::list> _lambdaQueue; void renderPassTransfer(const Batch& batch); void renderPassDraw(const Batch& batch); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 16ee81592a..94d645378b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -20,6 +20,8 @@ std::shared_ptr GLTexture::_textureTransferHelper; // FIXME placeholder for texture memory over-use #define DEFAULT_MAX_MEMORY_MB 256 +#define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f +#define OVER_MEMORY_PRESSURE 2.0f const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, @@ -92,7 +94,6 @@ const std::vector& GLTexture::getFaceTargets(GLenum target) { return faceTargets; } -#define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f float GLTexture::getMemoryPressure() { // Check for an explicit memory limit @@ -115,15 +116,15 @@ float GLTexture::getMemoryPressure() { lastFreeGpuMemory = freeGpuMemory; if (freePercentage < MIN_FREE_GPU_MEMORY_PERCENTAGE) { qDebug() << "Exceeded max GPU memory"; - return 2.0; + return OVER_MEMORY_PRESSURE; } } } } - // Allow 75% of all available GPU memory to be consumed by textures + // Allow 50% of all available GPU memory to be consumed by textures // FIXME overly conservative? - availableTextureMemory = (totalGpuMemory >> 2) * 3; + availableTextureMemory = (totalGpuMemory > 2); } // Return the consumed texture memory divided by the available texture memory. diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp index dbe022df2c..75af08d9a3 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp @@ -165,9 +165,9 @@ bool GLTextureTransferHelper::process() { } gltexture->finishTransfer(); - //glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, gltexture->_id, 0); - //glBlitNamedFramebuffer(_readFramebuffer, _drawFramebuffer, 0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); +#ifdef THREADED_TEXTURE_TRANSFER clientWait(); +#endif gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp(); gltexture->updateSize(); gltexture->setSyncState(gpu::gl::GLSyncState::Transferred); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index b684a9b8d2..f1c30b9382 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -39,39 +39,39 @@ public: void postTransfer() override; struct SparseInfo { - GL45Texture& _texture; - uvec3 _pageDimensions { DEFAULT_PAGE_DIMENSION }; - GLuint _maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL }; - uint32_t _maxPages { 0 }; - uint32_t _pageBytes { 0 }; - bool _sparse { false }; - GLint _pageDimensionsIndex { 0 }; SparseInfo(GL45Texture& texture); void maybeMakeSparse(); void update(); uvec3 getPageCounts(const uvec3& dimensions) const; uint32_t getPageCount(const uvec3& dimensions) const; + + GL45Texture& texture; + bool sparse { false }; + uvec3 pageDimensions { DEFAULT_PAGE_DIMENSION }; + GLuint maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL }; + uint32_t maxPages { 0 }; + uint32_t pageBytes { 0 }; + GLint pageDimensionsIndex { 0 }; }; struct TransferState { - GL45Texture& _texture; - GLTexelFormat _texelFormat; - uint8_t _face { 0 }; - uint16_t _mipLevel { 0 }; - uint32_t _bytesPerLine { 0 }; - uint32_t _bytesPerPixel { 0 }; - uint32_t _bytesPerPage { 0 }; - - uvec3 _mipDimensions; - uvec3 _mipOffset; - const uint8_t* _srcPointer { nullptr }; + TransferState(GL45Texture& texture); uvec3 currentPageSize() const; void updateMip(); void populatePage(std::vector& dest); bool increment(); - TransferState(GL45Texture& texture); - }; + GL45Texture& texture; + GLTexelFormat texelFormat; + uint8_t face { 0 }; + uint16_t mipLevel { 0 }; + uint32_t bytesPerLine { 0 }; + uint32_t bytesPerPixel { 0 }; + uint32_t bytesPerPage { 0 }; + uvec3 mipDimensions; + uvec3 mipOffset; + const uint8_t* srcPointer { nullptr }; + }; protected: void updateMips() override; void stripToMip(uint16_t newMinMip); @@ -87,6 +87,7 @@ public: void generateMips() const override; void withPreservedTexture(std::function f) const override; void derez(); + SparseInfo _sparseInfo; TransferState _transferState; uint32_t _allocatedPages { 0 }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 43aa257028..1b5013beb2 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -88,61 +88,61 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texture, bool transf using SparseInfo = GL45Backend::GL45Texture::SparseInfo; SparseInfo::SparseInfo(GL45Texture& texture) - : _texture(texture) { + : texture(texture) { } void SparseInfo::maybeMakeSparse() { // Don't enable sparse for objects with explicitly managed mip levels - if (!_texture._gpuObject.isAutogenerateMips()) { - qCDebug(gpugl45logging) << "Don't enable sparse texture for explicitly generated mipmaps on texture " << _texture._source.c_str(); + if (!texture._gpuObject.isAutogenerateMips()) { + qCDebug(gpugl45logging) << "Don't enable sparse texture for explicitly generated mipmaps on texture " << texture._source.c_str(); return; } - const uvec3 dimensions = _texture._gpuObject.getDimensions(); - auto allowedPageDimensions = getPageDimensionsForFormat(_texture._target, _texture._internalFormat); + const uvec3 dimensions = texture._gpuObject.getDimensions(); + auto allowedPageDimensions = getPageDimensionsForFormat(texture._target, texture._internalFormat); // In order to enable sparse the texture size must be an integer multiple of the page size for (size_t i = 0; i < allowedPageDimensions.size(); ++i) { - _pageDimensionsIndex = (uint32_t) i; - _pageDimensions = allowedPageDimensions[i]; + pageDimensionsIndex = (uint32_t) i; + pageDimensions = allowedPageDimensions[i]; // Is this texture an integer multiple of page dimensions? - if (uvec3(0) == (dimensions % _pageDimensions)) { - qCDebug(gpugl45logging) << "Enabling sparse for texture " << _texture._source.c_str(); - _sparse = true; + if (uvec3(0) == (dimensions % pageDimensions)) { + qCDebug(gpugl45logging) << "Enabling sparse for texture " << texture._source.c_str(); + sparse = true; break; } } - if (_sparse) { - glTextureParameteri(_texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); - glTextureParameteri(_texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, _pageDimensionsIndex); + if (sparse) { + glTextureParameteri(texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE); + glTextureParameteri(texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex); } else { qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y << - " is not supported by any sparse page size for texture" << _texture._source.c_str(); + " is not supported by any sparse page size for texture" << texture._source.c_str(); } } // This can only be called after we've established our storage size void SparseInfo::update() { - if (!_sparse) { + if (!sparse) { return; } - glGetTextureParameterIuiv(_texture._id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel); - _pageBytes = _texture._gpuObject.getTexelFormat().getSize(); - _pageBytes *= _pageDimensions.x * _pageDimensions.y * _pageDimensions.z; + glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel); + pageBytes = texture._gpuObject.getTexelFormat().getSize(); + pageBytes *= pageDimensions.x * pageDimensions.y * pageDimensions.z; - for (uint16_t mipLevel = 0; mipLevel <= _maxSparseLevel; ++mipLevel) { - auto mipDimensions = _texture._gpuObject.evalMipDimensions(mipLevel); + for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) { + auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel); auto mipPageCount = getPageCount(mipDimensions); - _maxPages += mipPageCount; + maxPages += mipPageCount; } - if (_texture._target == GL_TEXTURE_CUBE_MAP) { - _maxPages *= GLTexture::CUBE_NUM_FACES; + if (texture._target == GL_TEXTURE_CUBE_MAP) { + maxPages *= GLTexture::CUBE_NUM_FACES; } } uvec3 SparseInfo::getPageCounts(const uvec3& dimensions) const { - auto result = (dimensions / _pageDimensions) + - glm::clamp(dimensions % _pageDimensions, glm::uvec3(0), glm::uvec3(1)); + auto result = (dimensions / pageDimensions) + + glm::clamp(dimensions % pageDimensions, glm::uvec3(0), glm::uvec3(1)); return result; } @@ -153,59 +153,59 @@ uint32_t SparseInfo::getPageCount(const uvec3& dimensions) const { using TransferState = GL45Backend::GL45Texture::TransferState; -TransferState::TransferState(GL45Texture& texture) : _texture(texture) { +TransferState::TransferState(GL45Texture& texture) : texture(texture) { } void TransferState::updateMip() { - _mipDimensions = _texture._gpuObject.evalMipDimensions(_mipLevel); - _mipOffset = uvec3(); - if (!_texture._gpuObject.isStoredMipFaceAvailable(_mipLevel, _face)) { - _srcPointer = nullptr; + mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel); + mipOffset = uvec3(); + if (!texture._gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { + srcPointer = nullptr; return; } - auto mip = _texture._gpuObject.accessStoredMipFace(_mipLevel, _face); - _texelFormat = gl::GLTexelFormat::evalGLTexelFormat(_texture._gpuObject.getTexelFormat(), mip->getFormat()); - _srcPointer = mip->readData(); - _bytesPerLine = (uint32_t)mip->getSize() / _mipDimensions.y; - _bytesPerPixel = _bytesPerLine / _mipDimensions.x; + auto mip = texture._gpuObject.accessStoredMipFace(mipLevel, face); + texelFormat = gl::GLTexelFormat::evalGLTexelFormat(texture._gpuObject.getTexelFormat(), mip->getFormat()); + srcPointer = mip->readData(); + bytesPerLine = (uint32_t)mip->getSize() / mipDimensions.y; + bytesPerPixel = bytesPerLine / mipDimensions.x; } bool TransferState::increment() { - const SparseInfo& sparse = _texture._sparseInfo; - if ((_mipOffset.x + sparse._pageDimensions.x) < _mipDimensions.x) { - _mipOffset.x += sparse._pageDimensions.x; + const SparseInfo& sparse = texture._sparseInfo; + if ((mipOffset.x + sparse.pageDimensions.x) < mipDimensions.x) { + mipOffset.x += sparse.pageDimensions.x; return true; } - if ((_mipOffset.y + sparse._pageDimensions.y) < _mipDimensions.y) { - _mipOffset.x = 0; - _mipOffset.y += sparse._pageDimensions.y; + if ((mipOffset.y + sparse.pageDimensions.y) < mipDimensions.y) { + mipOffset.x = 0; + mipOffset.y += sparse.pageDimensions.y; return true; } - if (_mipOffset.z + sparse._pageDimensions.z < _mipDimensions.z) { - _mipOffset.x = 0; - _mipOffset.y = 0; - ++_mipOffset.z; + if (mipOffset.z + sparse.pageDimensions.z < mipDimensions.z) { + mipOffset.x = 0; + mipOffset.y = 0; + ++mipOffset.z; return true; } // Done with this mip?, move on to the next mip - if (_mipLevel + 1 < _texture.usedMipLevels()) { - _mipOffset = uvec3(0); - ++_mipLevel; + if (mipLevel + 1 < texture.usedMipLevels()) { + mipOffset = uvec3(0); + ++mipLevel; updateMip(); return true; } - uint8_t maxFace = (uint8_t)((_texture._target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - uint8_t nextFace = _face + 1; + uint8_t maxFace = (uint8_t)((texture._target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); + uint8_t nextFace = face + 1; // Done with this face? Move on to the next if (nextFace < maxFace) { - ++_face; - _mipOffset = uvec3(0); - _mipLevel = 0; + ++face; + mipOffset = uvec3(0); + mipLevel = 0; updateMip(); return true; } @@ -215,7 +215,7 @@ bool TransferState::increment() { void TransferState::populatePage(std::vector& buffer) { uvec3 pageSize = currentPageSize(); - auto bytesPerPageLine = _bytesPerPixel * pageSize.x; + auto bytesPerPageLine = bytesPerPixel * pageSize.x; if (0 != (bytesPerPageLine % DEFAULT_GL_PIXEL_ALIGNMENT)) { bytesPerPageLine += DEFAULT_GL_PIXEL_ALIGNMENT - (bytesPerPageLine % DEFAULT_GL_PIXEL_ALIGNMENT); assert(0 == (bytesPerPageLine % DEFAULT_GL_PIXEL_ALIGNMENT)); @@ -226,14 +226,14 @@ void TransferState::populatePage(std::vector& buffer) { } uint8_t* dst = &buffer[0]; for (uint32_t y = 0; y < pageSize.y; ++y) { - uint32_t srcOffset = (_bytesPerLine * (_mipOffset.y + y)) + (_bytesPerPixel * _mipOffset.x); + uint32_t srcOffset = (bytesPerLine * (mipOffset.y + y)) + (bytesPerPixel * mipOffset.x); uint32_t dstOffset = bytesPerPageLine * y; - memcpy(dst + dstOffset, _srcPointer + srcOffset, pageSize.x * _bytesPerPixel); + memcpy(dst + dstOffset, srcPointer + srcOffset, pageSize.x * bytesPerPixel); } } uvec3 TransferState::currentPageSize() const { - return glm::clamp(_mipDimensions - _mipOffset, uvec3(1), _texture._sparseInfo._pageDimensions); + return glm::clamp(mipDimensions - mipOffset, uvec3(1), texture._sparseInfo.pageDimensions); } GLuint GL45Texture::allocate(const Texture& texture) { @@ -256,7 +256,7 @@ GL45Texture::GL45Texture(const std::weak_ptr& backend, const Texture& GL45Texture::~GL45Texture() { qCDebug(gpugl45logging) << "Destroying texture " << _id << " from source " << _source.c_str(); - if (_sparseInfo._sparse) { + if (_sparseInfo.sparse) { // Remove this texture from the candidate list of derezzable textures { auto mipLevels = usedMipLevels(); @@ -279,7 +279,7 @@ GL45Texture::~GL45Texture() { std::list> destructionFunctions; uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - auto maxSparseMip = std::min(_maxMip, _sparseInfo._maxSparseLevel); + auto maxSparseMip = std::min(_maxMip, _sparseInfo.maxSparseLevel); for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) { auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel); destructionFunctions.push_back([id, maxFace, mipLevel, mipDimensions] { @@ -335,7 +335,7 @@ void GL45Texture::updateSize() const { } if (_transferrable) { - setSize(_allocatedPages * _sparseInfo._pageBytes); + setSize(_allocatedPages * _sparseInfo.pageBytes); } else { setSize(_virtualSize); } @@ -353,42 +353,42 @@ bool GL45Texture::continueTransfer() { buffer.resize(DEFAULT_PAGE_BUFFER_SIZE); } const uvec3 pageSize = _transferState.currentPageSize(); - const uvec3& offset = _transferState._mipOffset; + const uvec3& offset = _transferState.mipOffset; - if (_sparseInfo._sparse && _transferState._mipLevel <= _sparseInfo._maxSparseLevel) { - if (_allocatedPages > _sparseInfo._maxPages) { + if (_sparseInfo.sparse && _transferState.mipLevel <= _sparseInfo.maxSparseLevel) { + if (_allocatedPages > _sparseInfo.maxPages) { qCWarning(gpugl45logging) << "Exceeded max page allocation!"; } - glTexturePageCommitmentEXT(_id, _transferState._mipLevel, - offset.x, offset.y, _transferState._face, + glTexturePageCommitmentEXT(_id, _transferState.mipLevel, + offset.x, offset.y, _transferState.face, pageSize.x, pageSize.y, pageSize.z, GL_TRUE); ++_allocatedPages; } - if (_transferState._srcPointer) { + if (_transferState.srcPointer) { // Transfer the mip data _transferState.populatePage(buffer); if (GL_TEXTURE_2D == _target) { - glTextureSubImage2D(_id, _transferState._mipLevel, + glTextureSubImage2D(_id, _transferState.mipLevel, offset.x, offset.y, pageSize.x, pageSize.y, - _transferState._texelFormat.format, _transferState._texelFormat.type, &buffer[0]); + _transferState.texelFormat.format, _transferState.texelFormat.type, &buffer[0]); } else if (GL_TEXTURE_CUBE_MAP == _target) { - auto target = CUBE_FACE_LAYOUT[_transferState._face]; + auto target = CUBE_FACE_LAYOUT[_transferState.face]; // DSA ARB does not work on AMD, so use EXT // glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); - glTextureSubImage2DEXT(_id, target, _transferState._mipLevel, + glTextureSubImage2DEXT(_id, target, _transferState.mipLevel, offset.x, offset.y, pageSize.x, pageSize.y, - _transferState._texelFormat.format, _transferState._texelFormat.type, &buffer[0]); + _transferState.texelFormat.format, _transferState.texelFormat.type, &buffer[0]); } } serverWait(); - auto currentMip = _transferState._mipLevel; + auto currentMip = _transferState.mipLevel; auto result = _transferState.increment(); - if (_sparseInfo._sparse && _transferState._mipLevel != currentMip && currentMip <= _sparseInfo._maxSparseLevel) { + if (_sparseInfo.sparse && _transferState.mipLevel != currentMip && currentMip <= _sparseInfo.maxSparseLevel) { auto mipDimensions = _gpuObject.evalMipDimensions(currentMip); auto mipExpectedPages = _sparseInfo.getPageCount(mipDimensions); auto newPages = _allocatedPages - _lastMipAllocatedPages; @@ -431,9 +431,9 @@ void GL45Texture::syncSampler() const { void GL45Texture::postTransfer() { Parent::postTransfer(); - if (_sparseInfo._sparse) { + if (_sparseInfo.sparse) { auto mipLevels = usedMipLevels(); - if (mipLevels > 1 && _minMip < _sparseInfo._maxSparseLevel) { + if (mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) { Lock lock(texturesByMipCountsMutex); texturesByMipCounts[mipLevels].insert(this); } @@ -441,7 +441,7 @@ void GL45Texture::postTransfer() { } void GL45Texture::stripToMip(uint16_t newMinMip) { - if (!_sparseInfo._sparse) { + if (!_sparseInfo.sparse) { return; } @@ -450,7 +450,7 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { return; } - if (newMinMip > _sparseInfo._maxSparseLevel) { + if (newMinMip > _sparseInfo.maxSparseLevel) { qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail"; return; } @@ -491,26 +491,21 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { syncSampler(); size_t oldSize = _size; updateSize(); - size_t newSize = _size; - if (newSize > oldSize) { - qCDebug(gpugl45logging) << "WTF"; - qCDebug(gpugl45logging) << "\told size " << oldSize; - qCDebug(gpugl45logging) << "\tnew size " << newSize; - } + assert(_size > oldSize); // Re-insert into the texture-by-mips map if appropriate mipLevels = usedMipLevels(); - if (_sparseInfo._sparse && mipLevels > 1 && _minMip < _sparseInfo._maxSparseLevel) { + if (_sparseInfo.sparse && mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) { Lock lock(texturesByMipCountsMutex); texturesByMipCounts[mipLevels].insert(this); } } void GL45Texture::updateMips() { - if (!_sparseInfo._sparse) { + if (!_sparseInfo.sparse) { return; } - auto newMinMip = std::min(_gpuObject.minMip(), _sparseInfo._maxSparseLevel); + auto newMinMip = std::min(_gpuObject.minMip(), _sparseInfo.maxSparseLevel); if (_minMip < newMinMip) { stripToMip(newMinMip); } diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index 8a97c0d748..2cd39d49ae 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -115,32 +115,6 @@ void Framebuffer::updateSize(const TexturePointer& texture) { } } -#if 0 -void Framebuffer::resize(uint16 width, uint16 height, uint16 numSamples) { - if (width && height && numSamples && !isEmpty() && !isSwapchain()) { - if ((width != _width) || (height != _height) || (numSamples != _numSamples)) { - _numSamples = numSamples; - for (uint32 i = 0; i < _renderBuffers.size(); ++i) { - if (_renderBuffers[i]) { - _renderBuffers[i]._texture->resize2D(width, height, _numSamples); - assert(_renderBuffers[i]._texture->getNumSamples() == _numSamples); - ++_colorStamps[i]; - } - } - - if (_depthStencilBuffer) { - _depthStencilBuffer._texture->resize2D(width, height, _numSamples); - assert(_depthStencilBuffer._texture->getNumSamples() == _numSamples); - ++_depthStamp; - } - - _width = width; - _height = height; - } - } -} -#endif - uint16 Framebuffer::getWidth() const { if (isSwapchain()) { return getSwapchain()->getWidth(); diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index f357cfc3f8..8c26037ada 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -130,9 +130,6 @@ public: float getAspectRatio() const { return getWidth() / (float) getHeight() ; } - // If not a swapchain canvas, resize can resize all the render buffers and depth stencil attached in one call - //void resize( uint16 width, uint16 height, uint16 samples = 1 ); - static const uint32 MAX_NUM_RENDER_BUFFERS = 8; static uint32 getMaxNumRenderBuffers() { return MAX_NUM_RENDER_BUFFERS; } From ba3abc7b510fe0a5f671958ddba99f6bcc00f86f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Sep 2016 10:41:17 -0700 Subject: [PATCH 56/59] Fix warnings --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 1b5013beb2..7a86e4f3a2 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -491,7 +491,8 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { syncSampler(); size_t oldSize = _size; updateSize(); - assert(_size > oldSize); + Q_ASSERT(_size > oldSize); + // Re-insert into the texture-by-mips map if appropriate mipLevels = usedMipLevels(); From e6f23d6ffd3b9eb8f0a84f3833c9ae25fc1b12db Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Sep 2016 10:43:56 -0700 Subject: [PATCH 57/59] Disable sparse textures on non-Windows systems --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 7a86e4f3a2..2489f79c0b 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -25,8 +25,12 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; +#ifdef Q_OS_WIN static const QString DEBUG_FLAG("HIFI_DISABLE_SPARSE_TEXTURES"); static bool enableSparseTextures = !QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); +#else +static bool enableSparseTextures = false; +#endif // Allocate 1 MB of buffer space for paged transfers #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) From 5a91116c069047162c63b596869e1ccff8d479d0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Sep 2016 11:16:31 -0700 Subject: [PATCH 58/59] Fix debug build, fix typo in available texture memory calculation --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 2 +- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 94d645378b..56ff4166ea 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -124,7 +124,7 @@ float GLTexture::getMemoryPressure() { // Allow 50% of all available GPU memory to be consumed by textures // FIXME overly conservative? - availableTextureMemory = (totalGpuMemory > 2); + availableTextureMemory = (totalGpuMemory >> 1); } // Return the consumed texture memory divided by the available texture memory. diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 2489f79c0b..6733475c01 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -517,8 +517,8 @@ void GL45Texture::updateMips() { } void GL45Texture::derez() { - assert(_sparseInfo._sparse); - assert(_minMip < _sparseInfo._maxSparseLevel); + assert(_sparseInfo.sparse); + assert(_minMip < _sparseInfo.maxSparseLevel); assert(_minMip < _maxMip); assert(_transferrable); stripToMip(_minMip + 1); From 2dd93e0586dce438113e73baa4c9f75d3717b751 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Sep 2016 12:04:35 -0700 Subject: [PATCH 59/59] Disable debug command list --- tests/render-perf/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 5ac82bdf29..987fbe33d5 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -1129,7 +1129,7 @@ int main(int argc, char** argv) { QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow::setup(); QTestWindow window; - window.loadCommands("C:/Users/bdavis/Git/dreaming/exports/commands.txt"); + //window.loadCommands("C:/Users/bdavis/Git/dreaming/exports/commands.txt"); app.exec(); return 0; }