mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 01:12:48 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into clean/audio-config
This commit is contained in:
commit
a9318d052b
68 changed files with 2109 additions and 748 deletions
|
@ -19,6 +19,7 @@
|
|||
#include <AccountManager.h>
|
||||
#include <HTTPConnection.h>
|
||||
#include <LogHandler.h>
|
||||
#include <shared/NetworkUtils.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <UUID.h>
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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<QString>()) {
|
||||
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<LimitedNodeList>()->sendPacket(std::move(connectionDeniedPacket), senderSockAddr);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
#include <QUrlQuery>
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <BuildInfo.h>
|
||||
|
@ -66,8 +67,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();
|
||||
|
@ -159,6 +163,53 @@ 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);
|
||||
|
||||
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();
|
||||
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()) {
|
||||
qWarning() << "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<LimitedNodeList>();
|
||||
|
@ -166,7 +217,7 @@ DomainServer::~DomainServer() {
|
|||
|
||||
void DomainServer::queuedQuit(QString quitMessage, int exitCode) {
|
||||
if (!quitMessage.isEmpty()) {
|
||||
qCritical() << qPrintable(quitMessage);
|
||||
qWarning() << qPrintable(quitMessage);
|
||||
}
|
||||
|
||||
QCoreApplication::exit(exitCode);
|
||||
|
@ -307,7 +358,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();
|
||||
|
||||
|
@ -415,24 +466,30 @@ 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<AccountManager>()->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);
|
||||
|
@ -548,7 +605,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;
|
||||
}
|
||||
}
|
||||
|
@ -606,12 +662,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<Assignment::Type>& 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);
|
||||
|
||||
|
|
|
@ -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?
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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<DomainHandler::ConnectionRefusedReason>(reasonCode)) {
|
||||
void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
|
||||
DomainHandler::ConnectionRefusedReason reasonCode = static_cast<DomainHandler::ConnectionRefusedReason>(reasonCodeInt);
|
||||
|
||||
if (reasonCode == DomainHandler::ConnectionRefusedReason::TooManyUsers && !extraInfo.isEmpty()) {
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(extraInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (reasonCode) {
|
||||
case DomainHandler::ConnectionRefusedReason::ProtocolMismatch:
|
||||
case DomainHandler::ConnectionRefusedReason::TooManyUsers:
|
||||
case DomainHandler::ConnectionRefusedReason::Unknown: {
|
||||
|
|
|
@ -44,12 +44,12 @@
|
|||
#include <AbstractUriHandler.h>
|
||||
#include <shared/RateCounter.h>
|
||||
#include <ThreadSafeValueCache.h>
|
||||
#include <shared/FileLogger.h>
|
||||
|
||||
#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"
|
||||
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -58,7 +58,10 @@ void CachesSizeDialog::confirmClicked(bool checked) {
|
|||
DependencyManager::get<AnimationCache>()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES);
|
||||
DependencyManager::get<ModelCache>()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES);
|
||||
DependencyManager::get<SoundCache>()->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<TextureCache>()->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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include <QCheckBox>
|
||||
#include <QSyntaxHighlighter>
|
||||
|
||||
#include "AbstractLoggerInterface.h"
|
||||
#include <shared/AbstractLoggerInterface.h>
|
||||
|
||||
class KeywordHighlighter : public QSyntaxHighlighter {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <AddressManager.h>
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <avatar/MyAvatar.h>
|
||||
#include <FileUtils.h>
|
||||
#include <shared/FileUtils.h>
|
||||
#include <NodeList.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <SharedUtil.h>
|
||||
|
|
|
@ -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<EntityTreeRenderer*>(args->_renderer))) {
|
||||
return;
|
||||
}
|
||||
_fadeStartTime = usecTimestampNow();
|
||||
#endif
|
||||
}
|
||||
|
||||
_lastRenderTime = usecTimestampNow();
|
||||
|
|
|
@ -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();
|
||||
|
@ -589,7 +593,23 @@ void GLBackend::releaseQuery(GLuint id) const {
|
|||
_queriesTrash.push_back(id);
|
||||
}
|
||||
|
||||
void GLBackend::queueLambda(const std::function<void()> lambda) const {
|
||||
Lock lock(_trashMutex);
|
||||
_lambdaQueue.push_back(lambda);
|
||||
}
|
||||
|
||||
void GLBackend::recycle() const {
|
||||
{
|
||||
std::list<std::function<void()>> lamdbasTrash;
|
||||
{
|
||||
Lock lock(_trashMutex);
|
||||
std::swap(_lambdaQueue, lamdbasTrash);
|
||||
}
|
||||
for (auto lambda : lamdbasTrash) {
|
||||
lambda();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<GLuint> ids;
|
||||
std::list<std::pair<GLuint, Size>> buffersTrash;
|
||||
|
@ -679,6 +699,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) {
|
||||
|
|
|
@ -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 queueLambda(const std::function<void()> lambda) const;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -197,6 +198,7 @@ protected:
|
|||
mutable std::list<GLuint> _shadersTrash;
|
||||
mutable std::list<GLuint> _programsTrash;
|
||||
mutable std::list<GLuint> _queriesTrash;
|
||||
mutable std::list<std::function<void()>> _lambdaQueue;
|
||||
|
||||
void renderPassTransfer(const Batch& batch);
|
||||
void renderPassDraw(const Batch& batch);
|
||||
|
@ -365,6 +367,7 @@ protected:
|
|||
typedef void (GLBackend::*CommandCall)(const Batch&, size_t);
|
||||
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
|
||||
friend class GLState;
|
||||
friend class GLTexture;
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -17,11 +17,11 @@ using namespace gpu;
|
|||
using namespace gpu::gl;
|
||||
|
||||
std::shared_ptr<GLTextureTransferHelper> GLTexture::_textureTransferHelper;
|
||||
static std::map<uint16, size_t> _textureCountByMips;
|
||||
static uint16 _currentMaxMipCount { 0 };
|
||||
|
||||
// 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,
|
||||
|
@ -94,6 +94,7 @@ const std::vector<GLenum>& GLTexture::getFaceTargets(GLenum target) {
|
|||
return faceTargets;
|
||||
}
|
||||
|
||||
|
||||
float GLTexture::getMemoryPressure() {
|
||||
// Check for an explicit memory limit
|
||||
auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage();
|
||||
|
@ -102,15 +103,28 @@ 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) {
|
||||
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 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 >> 1);
|
||||
}
|
||||
|
||||
// Return the consumed texture memory divided by the available texture memory.
|
||||
|
@ -118,80 +132,27 @@ float GLTexture::getMemoryPressure() {
|
|||
return (float)consumedGpuMemory / (float)availableTextureMemory;
|
||||
}
|
||||
|
||||
GLTexture::DownsampleSource::DownsampleSource(const std::weak_ptr<GLBackend>& 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<GLBackend>& backend, const gpu::Texture& texture, GLuint id, GLTexture* originalTexture, 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// Create the texture and allocate storage
|
||||
GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id, bool transferrable) :
|
||||
GLTexture(backend, texture, id, nullptr, transferrable)
|
||||
GLObject(backend, texture, id),
|
||||
_source(texture.source()),
|
||||
_storageStamp(texture.getStamp()),
|
||||
_target(getGLTextureType(texture)),
|
||||
_internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())),
|
||||
_maxMip(texture.maxMip()),
|
||||
_minMip(texture.minMip()),
|
||||
_virtualSize(texture.evalTotalSize()),
|
||||
_transferrable(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<GLBackend>& 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
|
||||
auto strongBackend = _backend.lock();
|
||||
strongBackend->recycle();
|
||||
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) {
|
||||
|
@ -210,6 +171,28 @@ void GLTexture::createTexture() {
|
|||
});
|
||||
}
|
||||
|
||||
void GLTexture::withPreservedTexture(std::function<void()> 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<GLuint&>(_size) = size;
|
||||
|
@ -223,20 +206,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()) {
|
||||
|
@ -257,11 +226,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:
|
||||
|
|
|
@ -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,57 +89,38 @@ public:
|
|||
} else {
|
||||
object = Backend::getGPUObject<GLTextureType>(*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<GLuint&>(_id) = 0;
|
||||
return result;
|
||||
return object->_id;
|
||||
}
|
||||
|
||||
~GLTexture();
|
||||
|
||||
const GLuint& _texture { _id };
|
||||
const std::string _source;
|
||||
const Stamp _storageStamp;
|
||||
const GLenum _target;
|
||||
const GLenum _internalFormat;
|
||||
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>;
|
||||
DownsampleSource(const std::weak_ptr<gl::GLBackend>& backend) : _backend(backend), _size(0), _texture(0), _minMip(0), _maxMip(0) {}
|
||||
DownsampleSource(const std::weak_ptr<gl::GLBackend>& backend, GLTexture* originalTexture);
|
||||
~DownsampleSource();
|
||||
void reset() const { const_cast<GLuint&>(_texture) = 0; }
|
||||
const std::weak_ptr<gl::GLBackend>& _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,9 +134,7 @@ public:
|
|||
bool isReady() const;
|
||||
|
||||
// Execute any post-move operations that must occur only on the main thread
|
||||
void postTransfer();
|
||||
|
||||
bool isOverMaxMemory() const;
|
||||
virtual void postTransfer();
|
||||
|
||||
uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; }
|
||||
|
||||
|
@ -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<GLenum>& 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<GLenum>& getFaceTargets(GLenum textureType);
|
||||
|
||||
static GLenum getGLTextureType(const Texture& texture);
|
||||
|
||||
|
||||
const GLuint _size { 0 }; // true size as reported by the gl api
|
||||
std::atomic<GLSyncState> _syncState { GLSyncState::Idle };
|
||||
|
||||
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id, bool transferrable);
|
||||
GLTexture(const std::weak_ptr<gl::GLBackend>& 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<void()> f) const = 0;
|
||||
virtual void withPreservedTexture(std::function<void()> f) const;
|
||||
|
||||
protected:
|
||||
void setSize(GLuint size) const;
|
||||
|
@ -207,9 +180,6 @@ protected:
|
|||
virtual void finishTransfer();
|
||||
|
||||
private:
|
||||
|
||||
GLTexture(const std::weak_ptr<GLBackend>& backend, const gpu::Texture& gpuTexture, GLuint id, GLTexture* originalTexture, bool transferrable);
|
||||
|
||||
friend class GLTextureTransferHelper;
|
||||
friend class GLBackend;
|
||||
};
|
||||
|
|
|
@ -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<GLTexture>(*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() {
|
||||
|
@ -100,13 +97,28 @@ void GLTextureTransferHelper::shutdown() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void GLTextureTransferHelper::queueExecution(VoidLambda lambda) {
|
||||
Lock lock(_mutex);
|
||||
_pendingCommands.push_back(lambda);
|
||||
}
|
||||
|
||||
#define MAX_TRANSFERS_PER_PASS 2
|
||||
|
||||
bool GLTextureTransferHelper::process() {
|
||||
#ifdef THREADED_TEXTURE_TRANSFER
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (!pendingCommands.empty()) {
|
||||
for (auto command : pendingCommands) {
|
||||
command();
|
||||
}
|
||||
glFlush();
|
||||
}
|
||||
|
||||
if (!newTransferTextures.empty()) {
|
||||
|
@ -119,11 +131,16 @@ 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
|
||||
if (_transferringTextures.empty()) {
|
||||
#ifdef THREADED_TEXTURE_TRANSFER
|
||||
QThread::usleep(1);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -135,7 +152,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<GLTexture>(*texture);
|
||||
if (gltexture->continueTransfer()) {
|
||||
|
@ -144,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);
|
||||
|
@ -159,6 +180,7 @@ bool GLTextureTransferHelper::process() {
|
|||
_textureIterator = _transferringTextures.erase(_textureIterator);
|
||||
}
|
||||
|
||||
#ifdef THREADED_TEXTURE_TRANSFER
|
||||
if (!_transferringTextures.empty()) {
|
||||
// Don't saturate the GPU
|
||||
clientWait();
|
||||
|
@ -166,8 +188,7 @@ bool GLTextureTransferHelper::process() {
|
|||
// Don't saturate the CPU
|
||||
QThread::msleep(1);
|
||||
}
|
||||
#else
|
||||
QThread::msleep(1);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -28,12 +28,14 @@ using TextureListIterator = TextureList::iterator;
|
|||
|
||||
class GLTextureTransferHelper : public GenericThread {
|
||||
public:
|
||||
using VoidLambda = std::function<void()>;
|
||||
using VoidLambdaList = std::list<VoidLambda>;
|
||||
using Pointer = std::shared_ptr<GLTextureTransferHelper>;
|
||||
GLTextureTransferHelper();
|
||||
~GLTextureTransferHelper();
|
||||
void transferTexture(const gpu::TexturePointer& texturePointer);
|
||||
void queueExecution(VoidLambda lambda);
|
||||
|
||||
protected:
|
||||
void setup() override;
|
||||
void shutdown() override;
|
||||
bool process() override;
|
||||
|
@ -41,8 +43,15 @@ 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;
|
||||
// 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
|
||||
|
@ -50,11 +59,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
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
|
@ -43,7 +43,6 @@ public:
|
|||
GLuint allocate();
|
||||
public:
|
||||
GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& buffer, bool transferrable);
|
||||
GL41Texture(const std::weak_ptr<GLBackend>& 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<void()> f) const override;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -40,30 +40,6 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texture, bool transf
|
|||
|
||||
GL41Texture::GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable) : GLTexture(backend, texture, allocate(), transferrable) {}
|
||||
|
||||
GL41Texture::GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GL41Texture* original) : GLTexture(backend, texture, allocate(), original) {}
|
||||
|
||||
void GL41Texture::withPreservedTexture(std::function<void()> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<uint8_t>& dest);
|
||||
bool increment();
|
||||
TransferState(GLTexture& texture);
|
||||
};
|
||||
|
||||
class GL45Backend : public GLBackend {
|
||||
using Parent = GLBackend;
|
||||
// Context Backend static interface required
|
||||
|
@ -53,14 +30,54 @@ public:
|
|||
class GL45Texture : public GLTexture {
|
||||
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<GLBackend>& backend, const Texture& texture, bool transferrable);
|
||||
GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLTexture* original);
|
||||
~GL45Texture();
|
||||
|
||||
void postTransfer() override;
|
||||
|
||||
struct SparseInfo {
|
||||
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 {
|
||||
TransferState(GL45Texture& texture);
|
||||
uvec3 currentPageSize() const;
|
||||
void updateMip();
|
||||
void populatePage(std::vector<uint8_t>& dest);
|
||||
bool increment();
|
||||
|
||||
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);
|
||||
void startTransfer() override;
|
||||
bool continueTransfer() override;
|
||||
void finishTransfer() override;
|
||||
void incrementalTransfer(const uvec3& size, const gpu::Texture::PixelsPointer& mip, std::function<void(const ivec3& offset, const uvec3& size)> f) const;
|
||||
void transferMip(uint16_t mipLevel, uint8_t face = 0) const;
|
||||
void allocateMip(uint16_t mipLevel, uint8_t face = 0) const;
|
||||
|
@ -69,12 +86,20 @@ public:
|
|||
void syncSampler() const override;
|
||||
void generateMips() const override;
|
||||
void withPreservedTexture(std::function<void()> f) const override;
|
||||
void derez();
|
||||
|
||||
SparseInfo _sparseInfo;
|
||||
TransferState _transferState;
|
||||
uint32_t _allocatedPages { 0 };
|
||||
uint32_t _lastMipAllocatedPages { 0 };
|
||||
friend class GL45Backend;
|
||||
};
|
||||
|
||||
|
||||
protected:
|
||||
void recycle() const override;
|
||||
void derezTextures() const;
|
||||
|
||||
GLuint getFramebufferID(const FramebufferPointer& framebuffer) override;
|
||||
GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <glm/gtx/component_wise.hpp>
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
|
||||
#include "../gl/GLTexelFormat.h"
|
||||
|
||||
|
@ -24,79 +25,191 @@ using namespace gpu;
|
|||
using namespace gpu::gl;
|
||||
using namespace gpu::gl45;
|
||||
|
||||
#define SPARSE_TEXTURES 1
|
||||
#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)
|
||||
#define DEFAULT_GL_PIXEL_ALIGNMENT 4
|
||||
|
||||
using GL45Texture = GL45Backend::GL45Texture;
|
||||
|
||||
static std::map<uint16_t, std::unordered_set<GL45Texture*>> texturesByMipCounts;
|
||||
static Mutex texturesByMipCountsMutex;
|
||||
using TextureTypeFormat = std::pair<GLenum, GLenum>;
|
||||
std::map<TextureTypeFormat, std::vector<uvec3>> sparsePageDimensionsByFormat;
|
||||
Mutex sparsePageDimensionsByFormatMutex;
|
||||
|
||||
static std::vector<uvec3> 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<uvec3> result;
|
||||
if (count > 0) {
|
||||
std::vector<GLint> 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]);
|
||||
}
|
||||
qCDebug(gpugl45logging) << "Got " << count << " page sizes";
|
||||
}
|
||||
|
||||
{
|
||||
Lock lock(sparsePageDimensionsByFormatMutex);
|
||||
if (0 == sparsePageDimensionsByFormat.count(typeFormat)) {
|
||||
sparsePageDimensionsByFormat[typeFormat] = result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<uvec3> getPageDimensionsForFormat(GLenum target, GLenum format) {
|
||||
return getPageDimensionsForFormat({ target, format });
|
||||
}
|
||||
|
||||
GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texture, bool transfer) {
|
||||
return GL45Texture::sync<GL45Texture>(*this, texture, transfer);
|
||||
}
|
||||
|
||||
TransferState::TransferState(GLTexture& texture) : _texture(texture) {
|
||||
using SparseInfo = GL45Backend::GL45Texture::SparseInfo;
|
||||
|
||||
SparseInfo::SparseInfo(GL45Texture& texture)
|
||||
: texture(texture) {
|
||||
}
|
||||
|
||||
void TransferState::updateSparse() {
|
||||
glGetTextureParameterIuiv(_texture._id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel);
|
||||
_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);
|
||||
}
|
||||
|
||||
void TransferState::updateMip() {
|
||||
_mipDimensions = _texture._gpuObject.evalMipDimensions(_mipLevel);
|
||||
_mipOffset = uvec3();
|
||||
if (!_texture._gpuObject.isStoredMipFaceAvailable(_mipLevel, _face)) {
|
||||
_srcPointer = nullptr;
|
||||
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();
|
||||
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;
|
||||
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)) {
|
||||
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);
|
||||
} else {
|
||||
qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y <<
|
||||
" 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) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
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() {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
_mipOffset.x = 0;
|
||||
_mipOffset.y += _pageSize.y;
|
||||
if ((mipOffset.y + sparse.pageDimensions.y) < mipDimensions.y) {
|
||||
mipOffset.x = 0;
|
||||
mipOffset.y += sparse.pageDimensions.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_mipOffset.z + _pageSize.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;
|
||||
}
|
||||
|
@ -104,10 +217,9 @@ bool TransferState::increment() {
|
|||
return false;
|
||||
}
|
||||
|
||||
#define DEFAULT_GL_PIXEL_ALIGNMENT 4
|
||||
void TransferState::populatePage(std::vector<uint8_t>& 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));
|
||||
|
@ -118,14 +230,14 @@ void TransferState::populatePage(std::vector<uint8_t>& 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), _pageSize);
|
||||
return glm::clamp(mipDimensions - mipOffset, uvec3(1), texture._sparseInfo.pageDimensions);
|
||||
}
|
||||
|
||||
GLuint GL45Texture::allocate(const Texture& texture) {
|
||||
|
@ -139,25 +251,64 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) {
|
|||
}
|
||||
|
||||
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& 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) {
|
||||
glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE);
|
||||
if (enableSparseTextures && _transferrable) {
|
||||
_sparseInfo.maybeMakeSparse();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& 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);
|
||||
// }
|
||||
//}
|
||||
qCDebug(gpugl45logging) << "Destroying texture " << _id << " from source " << _source.c_str();
|
||||
if (_sparseInfo.sparse) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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<GLuint&>(_id) = 0;
|
||||
std::list<std::function<void()>> destructionFunctions;
|
||||
|
||||
uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1);
|
||||
auto maxSparseMip = std::min<uint16_t>(_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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void GL45Texture::withPreservedTexture(std::function<void()> f) const {
|
||||
|
@ -165,37 +316,38 @@ void GL45Texture::withPreservedTexture(std::function<void()> f) const {
|
|||
}
|
||||
|
||||
void GL45Texture::generateMips() const {
|
||||
qCDebug(gpugl45logging) << "Generating mipmaps for " << _source.c_str();
|
||||
glGenerateTextureMipmap(_id);
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -204,48 +356,65 @@ 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;
|
||||
|
||||
#if SPARSE_TEXTURES
|
||||
if (_transferState._mipLevel <= _transferState._maxSparseLevel) {
|
||||
glTexturePageCommitmentEXT(_id, _transferState._mipLevel,
|
||||
offset.x, offset.y, _transferState._face,
|
||||
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,
|
||||
pageSize.x, pageSize.y, pageSize.z,
|
||||
GL_TRUE);
|
||||
++_allocatedPages;
|
||||
}
|
||||
#endif
|
||||
|
||||
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();
|
||||
return _transferState.increment();
|
||||
auto currentMip = _transferState.mipLevel;
|
||||
auto result = _transferState.increment();
|
||||
if (_sparseInfo.sparse && _transferState.mipLevel != currentMip && currentMip <= _sparseInfo.maxSparseLevel) {
|
||||
auto mipDimensions = _gpuObject.evalMipDimensions(currentMip);
|
||||
auto mipExpectedPages = _sparseInfo.getPageCount(mipDimensions);
|
||||
auto newPages = _allocatedPages - _lastMipAllocatedPages;
|
||||
if (newPages != mipExpectedPages) {
|
||||
qCWarning(gpugl45logging) << "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 +426,131 @@ 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<uint16_t>(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 (_sparseInfo.sparse) {
|
||||
auto mipLevels = usedMipLevels();
|
||||
if (mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) {
|
||||
Lock lock(texturesByMipCountsMutex);
|
||||
texturesByMipCounts[mipLevels].insert(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GL45Texture::stripToMip(uint16_t newMinMip) {
|
||||
if (!_sparseInfo.sparse) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newMinMip < _minMip) {
|
||||
qCWarning(gpugl45logging) << "Cannot decrease the min mip";
|
||||
return;
|
||||
}
|
||||
|
||||
if (newMinMip > _sparseInfo.maxSparseLevel) {
|
||||
qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail";
|
||||
return;
|
||||
}
|
||||
|
||||
auto mipLevels = usedMipLevels();
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// If we weren't generating mips before, we need to now that we're stripping down mip levels.
|
||||
if (!_gpuObject.isAutogenerateMips()) {
|
||||
qCDebug(gpugl45logging) << "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 id = _id;
|
||||
auto mipDimensions = _gpuObject.evalMipDimensions(mip);
|
||||
_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;
|
||||
// Re-sync the sampler to force access to the new mip level
|
||||
syncSampler();
|
||||
size_t oldSize = _size;
|
||||
updateSize();
|
||||
Q_ASSERT(_size > oldSize);
|
||||
|
||||
|
||||
// Re-insert into the texture-by-mips map if appropriate
|
||||
mipLevels = usedMipLevels();
|
||||
if (_sparseInfo.sparse && mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) {
|
||||
Lock lock(texturesByMipCountsMutex);
|
||||
texturesByMipCounts[mipLevels].insert(this);
|
||||
}
|
||||
}
|
||||
|
||||
void GL45Texture::updateMips() {
|
||||
if (!_sparseInfo.sparse) {
|
||||
return;
|
||||
}
|
||||
auto newMinMip = std::min<uint16_t>(_gpuObject.minMip(), _sparseInfo.maxSparseLevel);
|
||||
if (_minMip < newMinMip) {
|
||||
stripToMip(newMinMip);
|
||||
}
|
||||
}
|
||||
|
||||
void GL45Texture::derez() {
|
||||
assert(_sparseInfo.sparse);
|
||||
assert(_minMip < _sparseInfo.maxSparseLevel);
|
||||
assert(_minMip < _maxMip);
|
||||
assert(_transferrable);
|
||||
stripToMip(_minMip + 1);
|
||||
}
|
||||
|
||||
void GL45Backend::derezTextures() const {
|
||||
if (GLTexture::getMemoryPressure() < 1.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
Lock lock(texturesByMipCountsMutex);
|
||||
if (texturesByMipCounts.empty()) {
|
||||
qCDebug(gpugl45logging) << "No available textures to derez";
|
||||
return;
|
||||
}
|
||||
|
||||
auto mipLevel = texturesByMipCounts.rbegin()->first;
|
||||
if (mipLevel <= 1) {
|
||||
qCDebug(gpugl45logging) << "Max mip levels " << mipLevel;
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(gpugl45logging) << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage();
|
||||
qCDebug(gpugl45logging) << "Used texture memory " << Context::getTextureGPUMemoryUsage();
|
||||
|
||||
GL45Texture* targetTexture = nullptr;
|
||||
{
|
||||
auto& textures = texturesByMipCounts[mipLevel];
|
||||
assert(!textures.empty());
|
||||
targetTexture = *textures.begin();
|
||||
}
|
||||
lock.unlock();
|
||||
targetTexture->derez();
|
||||
qCDebug(gpugl45logging) << "New Used texture memory " << Context::getTextureGPUMemoryUsage();
|
||||
}
|
||||
|
|
|
@ -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<uint32_t> Context::_bufferGPUCount{ 0 };
|
||||
std::atomic<Buffer::Size> Context::_bufferGPUMemoryUsage{ 0 };
|
||||
std::atomic<uint32_t> Context::_fenceCount { 0 };
|
||||
std::atomic<uint32_t> Context::_bufferGPUCount { 0 };
|
||||
std::atomic<Buffer::Size> Context::_bufferGPUMemoryUsage { 0 };
|
||||
|
||||
std::atomic<uint32_t> Context::_textureGPUCount{ 0 };
|
||||
std::atomic<Texture::Size> Context::_textureGPUMemoryUsage{ 0 };
|
||||
|
@ -170,10 +172,15 @@ std::atomic<Texture::Size> Context::_textureGPUVirtualMemoryUsage{ 0 };
|
|||
std::atomic<uint32_t> Context::_textureGPUTransferCount{ 0 };
|
||||
|
||||
void Context::incrementBufferGPUCount() {
|
||||
_bufferGPUCount++;
|
||||
static std::atomic<uint32_t> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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() {
|
||||
|
|
|
@ -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<uint32_t> _fenceCount;
|
||||
|
||||
static std::atomic<uint32_t> _bufferGPUCount;
|
||||
static std::atomic<Size> _bufferGPUMemoryUsage;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -113,30 +115,6 @@ void Framebuffer::updateSize(const TexturePointer& texture) {
|
|||
}
|
||||
}
|
||||
|
||||
void Framebuffer::resize(uint16 width, uint16 height, uint16 numSamples) {
|
||||
if (width && height && numSamples && !isEmpty() && !isSwapchain()) {
|
||||
if ((width != _width) || (height != _height) || (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();
|
||||
++_colorStamps[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (_depthStencilBuffer) {
|
||||
_depthStencilBuffer._texture->resize2D(width, height, numSamples);
|
||||
_numSamples = _depthStencilBuffer._texture->getNumSamples();
|
||||
++_depthStamp;
|
||||
}
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
// _numSamples = numSamples;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16 Framebuffer::getWidth() const {
|
||||
if (isSwapchain()) {
|
||||
return getSwapchain()->getWidth();
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
@ -118,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;
|
||||
|
@ -126,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;
|
||||
|
@ -134,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;
|
||||
|
@ -142,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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
|
|
@ -402,13 +402,18 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
|
|||
auto reasonText = message->readWithoutCopy(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<AccountManager>();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -91,9 +91,6 @@ namespace std {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
QHostAddress getGuessedLocalAddress();
|
||||
|
||||
Q_DECLARE_METATYPE(HifiSockAddr);
|
||||
|
||||
#endif // hifi_HifiSockAddr_h
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <tbb/parallel_for.h>
|
||||
|
||||
#include <LogHandler.h>
|
||||
#include <shared/NetworkUtils.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -745,8 +746,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 +787,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<udt::BasePacket> 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 +806,79 @@ void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> 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<udt::BasePacket> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
@ -166,8 +167,8 @@ public:
|
|||
std::unique_ptr<NLPacket> constructPingPacket(PingType_t pingType = PingType::Agnostic);
|
||||
std::unique_ptr<NLPacket> constructPingReplyPacket(ReceivedMessage& message);
|
||||
|
||||
std::unique_ptr<NLPacket> constructICEPingPacket(PingType_t pingType, const QUuid& iceID);
|
||||
std::unique_ptr<NLPacket> constructICEPingReplyPacket(ReceivedMessage& message, const QUuid& iceID);
|
||||
static std::unique_ptr<NLPacket> constructICEPingPacket(PingType_t pingType, const QUuid& iceID);
|
||||
static std::unique_ptr<NLPacket> constructICEPingReplyPacket(ReceivedMessage& message, const QUuid& iceID);
|
||||
|
||||
void sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID);
|
||||
|
||||
|
@ -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<NLPacket> 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<udt::BasePacket> packet);
|
||||
|
||||
|
|
|
@ -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<PacketVersion>(DomainConnectionDeniedVersion::IncludesReasonCode);
|
||||
return static_cast<PacketVersion>(DomainConnectionDeniedVersion::IncludesExtraInfo);
|
||||
|
||||
case PacketType::DomainConnectRequest:
|
||||
return static_cast<PacketVersion>(DomainConnectRequestVersion::HasProtocolVersions);
|
||||
|
|
|
@ -206,7 +206,8 @@ enum class DomainConnectRequestVersion : PacketVersion {
|
|||
|
||||
enum class DomainConnectionDeniedVersion : PacketVersion {
|
||||
ReasonMessageOnly = 17,
|
||||
IncludesReasonCode
|
||||
IncludesReasonCode,
|
||||
IncludesExtraInfo
|
||||
};
|
||||
|
||||
enum class DomainServerAddedNodeVersion : PacketVersion {
|
||||
|
|
|
@ -34,7 +34,21 @@ Antialiasing::Antialiasing() {
|
|||
const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() {
|
||||
int width = DependencyManager::get<FramebufferCache>()->getFrameBufferSize().width();
|
||||
int height = DependencyManager::get<FramebufferCache>()->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<FramebufferCache>()->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,21 +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<FramebufferCache>()->getLightingTexture()->getTexelFormat();
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
||||
_antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler));
|
||||
_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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -337,7 +337,13 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con
|
|||
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
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());
|
||||
|
@ -345,6 +351,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,11 +359,10 @@ 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);
|
||||
|
||||
}
|
||||
_primaryFramebuffer->resize(frameSize.x, frameSize.y);
|
||||
|
||||
primaryFramebuffer = _primaryFramebuffer;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
#include <QtCore/QDir>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
#include <FileUtils.h>
|
||||
#include <SharedUtil.h>
|
||||
#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";
|
|
@ -13,7 +13,7 @@
|
|||
#define hifi_FileLogger_h
|
||||
|
||||
#include "AbstractLoggerInterface.h"
|
||||
#include <GenericQueueThread.h>
|
||||
#include "../GenericQueueThread.h"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
|
|
@ -9,14 +9,29 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <qdir.h>
|
||||
#include <qfileinfo.h>
|
||||
#include <qdesktopservices.h>
|
||||
#include <qprocess.h>
|
||||
#include <qurl.h>
|
||||
|
||||
#include "FileUtils.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
|
||||
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) {
|
||||
|
|
@ -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
|
42
libraries/shared/src/shared/NetworkUtils.cpp
Normal file
42
libraries/shared/src/shared/NetworkUtils.cpp
Normal file
|
@ -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 <QtNetwork/QNetworkInterface>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
17
libraries/shared/src/shared/NetworkUtils.h
Normal file
17
libraries/shared/src/shared/NetworkUtils.h
Normal file
|
@ -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 <QtNetwork/QHostAddress>
|
||||
|
||||
QHostAddress getGuessedLocalAddress();
|
||||
|
||||
#endif // hifi_shared_NetworkUtils_h
|
|
@ -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], {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QtCore/QDir>
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QThreadPool>
|
||||
|
@ -32,12 +33,12 @@
|
|||
|
||||
|
||||
#include <shared/RateCounter.h>
|
||||
#include <shared/NetworkUtils.h>
|
||||
#include <shared/FileLogger.h>
|
||||
#include <shared/FileUtils.h>
|
||||
#include <LogHandler.h>
|
||||
#include <AssetClient.h>
|
||||
|
||||
//#include <gl/OffscreenGLCanvas.h>
|
||||
//#include <gl/GLHelpers.h>
|
||||
//#include <gl/QOpenGLContextWrapper.h>
|
||||
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
#include <gpu/gl/GLFramebuffer.h>
|
||||
#include <gpu/gl/GLTexture.h>
|
||||
|
@ -99,56 +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) {
|
||||
|
@ -188,7 +139,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);
|
||||
|
@ -197,7 +149,6 @@ public:
|
|||
_lastMouse = mouse;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
static QString toHumanSize(size_t size, size_t maxUnit = std::numeric_limits<size_t>::max()) {
|
||||
static const std::vector<QString> SUFFIXES{ { "B", "KB", "MB", "GB", "TB", "PB" } };
|
||||
|
@ -240,7 +191,7 @@ public:
|
|||
std::mutex _mutex;
|
||||
std::shared_ptr<gpu::Backend> _backend;
|
||||
std::vector<uint64_t> _frameTimes;
|
||||
size_t _frameIndex;
|
||||
size_t _frameIndex { 0 };
|
||||
std::mutex _frameLock;
|
||||
std::queue<gpu::FramePointer> _pendingFrames;
|
||||
gpu::FramePointer _activeFrame;
|
||||
|
@ -252,7 +203,6 @@ public:
|
|||
_pendingFrames.push(frame);
|
||||
}
|
||||
|
||||
|
||||
void initialize(QWindow* window, gl::Context& initContext) {
|
||||
setObjectName("RenderThread");
|
||||
_context.setWindow(window);
|
||||
|
@ -286,10 +236,6 @@ public:
|
|||
}
|
||||
|
||||
_context.makeCurrent();
|
||||
glewExperimental = true;
|
||||
glewInit();
|
||||
glGetError();
|
||||
|
||||
_frameTimes.resize(FRAME_TIME_BUFFER_SIZE, 0);
|
||||
{
|
||||
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||
|
@ -375,7 +321,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
bool process() override {
|
||||
std::queue<gpu::FramePointer> pendingFrames;
|
||||
{
|
||||
|
@ -420,6 +365,7 @@ public:
|
|||
};
|
||||
|
||||
render::ItemID BackgroundRenderData::_item = 0;
|
||||
QSharedPointer<FileLogger> logger;
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const BackgroundRenderData::Pointer& stuff) {
|
||||
|
@ -552,36 +498,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());
|
||||
|
@ -622,6 +538,16 @@ public:
|
|||
DependencyManager::destroy<NodeList>();
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -666,6 +592,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 +710,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
|
||||
{
|
||||
|
@ -803,10 +738,77 @@ private:
|
|||
#endif
|
||||
}
|
||||
|
||||
void runCommand(const QString& command) {
|
||||
qDebug() << "Running command: " << 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);
|
||||
|
@ -959,7 +961,6 @@ private:
|
|||
QString atpUrl = QUrl::fromLocalFile(atpPath).toString();
|
||||
ResourceManager::setUrlPrefixOverride("atp:/", atpUrl + "/");
|
||||
}
|
||||
_settings.setValue(LAST_SCENE_KEY, fileName);
|
||||
_octree->clear();
|
||||
_octree->getTree()->readFromURL(fileName);
|
||||
}
|
||||
|
@ -978,6 +979,7 @@ private:
|
|||
if (fileName.isNull()) {
|
||||
return;
|
||||
}
|
||||
_settings.setValue(LAST_SCENE_KEY, fileName);
|
||||
importScene(fileName);
|
||||
}
|
||||
|
||||
|
@ -1015,7 +1017,7 @@ private:
|
|||
}
|
||||
|
||||
void resetPosition() {
|
||||
_camera.yaw = 0;
|
||||
_camera.yawPitch = vec3(0);
|
||||
_camera.setPosition(vec3());
|
||||
}
|
||||
|
||||
|
@ -1077,6 +1079,13 @@ private:
|
|||
model::SunSkyStage _sunSkyStage;
|
||||
model::LightPointer _globalLight { std::make_shared<model::Light>() };
|
||||
bool _ready { false };
|
||||
|
||||
QStringList _commands;
|
||||
QString _commandPath;
|
||||
int _commandLoops { 0 };
|
||||
int _commandIndex { -1 };
|
||||
uint64_t _nextCommandTime { 0 };
|
||||
|
||||
//TextOverlay* _textOverlay;
|
||||
static bool _cullingEnabled;
|
||||
|
||||
|
@ -1093,12 +1102,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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1106,16 +1117,19 @@ const char * LOG_FILTER_RULES = R"V0G0N(
|
|||
hifi.gpu=true
|
||||
)V0G0N";
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QApplication 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();
|
||||
QTestWindow window;
|
||||
//window.loadCommands("C:/Users/bdavis/Git/dreaming/exports/commands.txt");
|
||||
app.exec();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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<TextureCache>()->getImageTexture(file);
|
||||
}
|
||||
|
||||
void unloadTexture() {
|
||||
if (_textures.empty()) {
|
||||
return;
|
||||
}
|
||||
_textures[_currentTextureIndex].reset();
|
||||
}
|
||||
|
||||
void loadAll() {
|
||||
for (size_t i = 0; i < _texturesFiles.size(); ++i) {
|
||||
if (_textures[i]) {
|
||||
continue;
|
||||
}
|
||||
auto file = _texturesFiles[i].file;
|
||||
qDebug() << "Loading texture " << file;
|
||||
_textures[i] = DependencyManager::get<TextureCache>()->getImageTexture(file);
|
||||
}
|
||||
}
|
||||
|
||||
void unloadAll() {
|
||||
for (auto& texture : _textures) {
|
||||
texture.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<TextureLoad> _textureLoads;
|
||||
std::list<gpu::TexturePointer> _textures;
|
||||
std::list<gpu::TexturePointer>::iterator _currentTexture;
|
||||
size_t _currentTextureIndex { 0 };
|
||||
std::vector<TextureLoad> _texturesFiles;
|
||||
std::vector<gpu::TexturePointer> _textures;
|
||||
|
||||
uint16_t _fps;
|
||||
gpu::PipelinePointer _simplePipeline;
|
||||
|
@ -438,7 +534,9 @@ private:
|
|||
auto now = usecTimestampNow();
|
||||
static auto last = now;
|
||||
auto delta = (now - last) / USECS_PER_MSEC;
|
||||
if (!_textureLoads.empty()) {
|
||||
Q_UNUSED(delta);
|
||||
#if 0
|
||||
if (!_textures.empty()) {
|
||||
const auto& front = _textureLoads.front();
|
||||
if (delta >= front.time) {
|
||||
QFileInfo fileInfo(front.file);
|
||||
|
@ -456,6 +554,7 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void render() {
|
||||
|
@ -474,14 +573,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 +657,6 @@ int main(int argc, char** argv) {
|
|||
}).waitForDownload();
|
||||
}
|
||||
|
||||
|
||||
QTestWindow::setup();
|
||||
QTestWindow window;
|
||||
app.exec();
|
||||
|
|
|
@ -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")
|
||||
|
|
3
tools/ice-client/CMakeLists.txt
Normal file
3
tools/ice-client/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
set(TARGET_NAME ice-client)
|
||||
setup_hifi_project(Core Widgets)
|
||||
link_hifi_libraries(shared networking)
|
387
tools/ice-client/src/ICEClientApp.cpp
Normal file
387
tools/ice-client/src/ICEClientApp.cpp
Normal file
|
@ -0,0 +1,387 @@
|
|||
//
|
||||
// ICEClientApp.cpp
|
||||
// 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 <QDataStream>
|
||||
#include <QLoggingCategory>
|
||||
#include <QCommandLineParser>
|
||||
#include <PathUtils.h>
|
||||
#include <LimitedNodeList.h>
|
||||
#include <NetworkLogging.h>
|
||||
|
||||
#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", "1");
|
||||
parser.addOption(howManyTimesOption);
|
||||
|
||||
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;
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(helpOption)) {
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
_verbose = parser.isSet(verboseOutput);
|
||||
if (!_verbose) {
|
||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtDebugMsg, false);
|
||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtInfoMsg, false);
|
||||
const_cast<QLoggingCategory*>(&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));
|
||||
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
|
||||
QString hostnamePortString = parser.value(iceServerAddressOption);
|
||||
|
||||
QHostAddress address { hostnamePortString.left(hostnamePortString.indexOf(':')) };
|
||||
quint16 port { (quint16) hostnamePortString.mid(hostnamePortString.indexOf(':') + 1).toUInt() };
|
||||
if (port == 0) {
|
||||
port = ICE_SERVER_DEFAULT_PORT;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
} else {
|
||||
_iceServerAddr = HifiSockAddr(address, port);
|
||||
}
|
||||
}
|
||||
|
||||
if (_verbose) {
|
||||
qDebug() << "ICE-server address is" << _iceServerAddr;
|
||||
}
|
||||
|
||||
setState(lookUpStunServer);
|
||||
|
||||
QTimer* doTimer = new QTimer(this);
|
||||
connect(doTimer, &QTimer::timeout, this, &ICEClientApp::doSomething);
|
||||
doTimer->start(200);
|
||||
}
|
||||
|
||||
ICEClientApp::~ICEClientApp() {
|
||||
delete _socket;
|
||||
}
|
||||
|
||||
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<udt::Packet> packet) { processPacket(std::move(packet)); });
|
||||
_socket->addUnfilteredHandler(_stunSockAddr,
|
||||
[this](std::unique_ptr<udt::BasePacket> packet) {
|
||||
processSTUNResponse(std::move(packet));
|
||||
});
|
||||
|
||||
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() {
|
||||
if (_actionMax > 0 && _actionCount >= _actionMax) {
|
||||
// time to stop.
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
|
||||
} else if (_state == lookUpStunServer) {
|
||||
// lookup STUN server address
|
||||
if (!_stunSockAddr.getAddress().isNull()) {
|
||||
if (_verbose) {
|
||||
qDebug() << "stun server is" << _stunSockAddr;
|
||||
}
|
||||
setState(sendStunRequestPacket);
|
||||
} else {
|
||||
if (_verbose) {
|
||||
qDebug() << "_stunSockAddr is" << _stunSockAddr.getAddress();
|
||||
}
|
||||
QCoreApplication::exit(stunFailureExitStatus);
|
||||
}
|
||||
|
||||
} else if (_state == sendStunRequestPacket) {
|
||||
// send STUN request packet
|
||||
closeSocket();
|
||||
openSocket();
|
||||
|
||||
if (!_cacheSTUNResult || !_stunResultSet) {
|
||||
const int NUM_BYTES_STUN_HEADER = 20;
|
||||
char stunRequestPacket[NUM_BYTES_STUN_HEADER];
|
||||
LimitedNodeList::makeSTUNRequestPacket(stunRequestPacket);
|
||||
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 {
|
||||
if (_verbose) {
|
||||
qDebug() << "using cached STUN response";
|
||||
}
|
||||
_publicSockAddr.setPort(_socket->localPort());
|
||||
setState(talkToIceServer);
|
||||
}
|
||||
|
||||
} else if (_state == talkToIceServer) {
|
||||
QUuid peerID;
|
||||
if (_domainID == QUuid()) {
|
||||
// pick a random domain-id which will fail
|
||||
peerID = QUuid::createUuid();
|
||||
setState(pause0);
|
||||
} else {
|
||||
// use the domain UUID given on the command-line
|
||||
peerID = _domainID;
|
||||
setState(waitForIceReply);
|
||||
}
|
||||
_sessionUUID = QUuid::createUuid();
|
||||
if (_verbose) {
|
||||
qDebug() << "I am" << _sessionUUID;
|
||||
}
|
||||
|
||||
sendPacketToIceServer(PacketType::ICEServerQuery, _iceServerAddr, _sessionUUID, peerID);
|
||||
_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) {
|
||||
if (_verbose) {
|
||||
qDebug() << "";
|
||||
}
|
||||
closeSocket();
|
||||
setState(sendStunRequestPacket);
|
||||
_actionCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void ICEClientApp::sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr,
|
||||
const QUuid& clientID, const QUuid& peerID) {
|
||||
std::unique_ptr<NLPacket> icePacket = NLPacket::create(packetType);
|
||||
|
||||
QDataStream iceDataStream(icePacket.get());
|
||||
iceDataStream << clientID << _publicSockAddr << _localSockAddr;
|
||||
|
||||
if (packetType == PacketType::ICEServerQuery) {
|
||||
assert(!peerID.isNull());
|
||||
|
||||
iceDataStream << 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;
|
||||
}
|
||||
|
||||
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<udt::BasePacket> packet) {
|
||||
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);
|
||||
if (_verbose) {
|
||||
qDebug() << "My public address is" << _publicSockAddr;
|
||||
}
|
||||
_stunResultSet = true;
|
||||
setState(talkToIceServer);
|
||||
} else {
|
||||
QCoreApplication::exit(stunFailureExitStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ICEClientApp::processPacket(std::unique_ptr<udt::Packet> packet) {
|
||||
std::unique_ptr<NLPacket> nlPacket = NLPacket::fromBase(std::move(packet));
|
||||
|
||||
if (nlPacket->getPayloadSize() < NLPacket::localHeaderSize(PacketType::ICEServerHeartbeat)) {
|
||||
if (_verbose) {
|
||||
qDebug() << "got a short packet.";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QSharedPointer<ReceivedMessage> message = QSharedPointer<ReceivedMessage>::create(*nlPacket);
|
||||
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;
|
||||
if (_verbose) {
|
||||
qDebug() << "got ICEServerPeerInformation from" << _domainServerPeer;
|
||||
}
|
||||
_domainServerPeerSet = true;
|
||||
|
||||
icePingDomainServer();
|
||||
_pingDomainTimer = new QTimer(this);
|
||||
connect(_pingDomainTimer, &QTimer::timeout, this, &ICEClientApp::icePingDomainServer);
|
||||
_pingDomainTimer->start(500);
|
||||
} else {
|
||||
NetworkPeer domainServerPeer;
|
||||
iceResponseStream >> domainServerPeer;
|
||||
if (_verbose) {
|
||||
qDebug() << "got repeat ICEServerPeerInformation from" << domainServerPeer;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (nlPacket->getType() == PacketType::ICEPing) {
|
||||
if (_verbose) {
|
||||
qDebug() << "got packet: " << nlPacket->getType();
|
||||
}
|
||||
auto replyPacket = LimitedNodeList::constructICEPingReplyPacket(*message, _sessionUUID);
|
||||
_socket->writePacket(*replyPacket, senderAddr);
|
||||
checkDomainPingCount();
|
||||
|
||||
} else if (nlPacket->getType() == PacketType::ICEPingReply) {
|
||||
if (_verbose) {
|
||||
qDebug() << "got packet: " << nlPacket->getType();
|
||||
}
|
||||
if (_domainServerPeerSet && _state == waitForIceReply &&
|
||||
(senderAddr == _domainServerPeer.getLocalSocket() ||
|
||||
senderAddr == _domainServerPeer.getPublicSocket())) {
|
||||
|
||||
delete _pingDomainTimer;
|
||||
_pingDomainTimer = nullptr;
|
||||
|
||||
setState(pause0);
|
||||
} else {
|
||||
if (_verbose) {
|
||||
qDebug() << "got unexpected ICEPingReply" << senderAddr;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (_verbose) {
|
||||
qDebug() << "got unexpected packet: " << nlPacket->getType();
|
||||
}
|
||||
}
|
||||
}
|
96
tools/ice-client/src/ICEClientApp.h
Normal file
96
tools/ice-client/src/ICEClientApp.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// ICEClientApp.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 <QApplication>
|
||||
#include <udt/Constants.h>
|
||||
#include <udt/Socket.h>
|
||||
#include <ReceivedMessage.h>
|
||||
#include <NetworkPeer.h>
|
||||
|
||||
|
||||
class ICEClientApp : public QCoreApplication {
|
||||
Q_OBJECT
|
||||
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
|
||||
sendStunRequestPacket, // 1
|
||||
waitForStunResponse, // 2
|
||||
talkToIceServer, // 3
|
||||
waitForIceReply, // 4
|
||||
pause0, // 5
|
||||
pause1 // 6
|
||||
};
|
||||
|
||||
void closeSocket();
|
||||
void openSocket();
|
||||
|
||||
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<udt::BasePacket> packet);
|
||||
void processPacket(std::unique_ptr<udt::Packet> packet);
|
||||
void checkDomainPingCount();
|
||||
|
||||
bool _verbose;
|
||||
bool _cacheSTUNResult; // should we only talk to stun server once?
|
||||
bool _stunResultSet { false }; // have we already talked to stun server?
|
||||
|
||||
HifiSockAddr _stunSockAddr;
|
||||
|
||||
unsigned int _actionCount { 0 };
|
||||
unsigned int _actionMax { 0 };
|
||||
|
||||
QUuid _sessionUUID;
|
||||
QUuid _domainID;
|
||||
|
||||
QTimer* _pingDomainTimer { nullptr };
|
||||
|
||||
HifiSockAddr _iceServerAddr;
|
||||
|
||||
HifiSockAddr _localSockAddr;
|
||||
HifiSockAddr _publicSockAddr;
|
||||
udt::Socket* _socket { nullptr };
|
||||
|
||||
bool _domainServerPeerSet { false };
|
||||
NetworkPeer _domainServerPeer;
|
||||
|
||||
int _state { 0 };
|
||||
|
||||
QTimer _stunResponseTimer;
|
||||
bool _stunResponseTimerCanceled { false };
|
||||
QTimer _iceResponseTimer;
|
||||
bool _iceResponseTimerCanceled { false };
|
||||
int _domainPingCount { 0 };
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //hifi_ICEClientApp_h
|
23
tools/ice-client/src/main.cpp
Normal file
23
tools/ice-client/src/main.cpp
Normal file
|
@ -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 <iostream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ICEClientApp.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
ICEClientApp app(argc, argv);
|
||||
return app.exec();
|
||||
}
|
Loading…
Reference in a new issue