Merge branch 'master' into 21055

This commit is contained in:
David Rowe 2016-10-04 08:46:30 +13:00
commit 4818ad2615
39 changed files with 722 additions and 194 deletions

View file

@ -91,7 +91,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
// check for a wallet UUID on the command line or in the config // check for a wallet UUID on the command line or in the config
// this would represent where the user running AC wants funds sent to // this would represent where the user running AC wants funds sent to
if (!walletUUID.isNull()) { if (!walletUUID.isNull()) {
qCDebug(assigmnentclient) << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); qCDebug(assignment_client) << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID);
_requestAssignment.setWalletUUID(walletUUID); _requestAssignment.setWalletUUID(walletUUID);
} }
@ -102,16 +102,16 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
} }
_assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true); _assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true);
_assignmentServerSocket.setObjectName("AssigmentServer"); _assignmentServerSocket.setObjectName("AssignmentServer");
nodeList->setAssignmentServerSocket(_assignmentServerSocket); nodeList->setAssignmentServerSocket(_assignmentServerSocket);
qCDebug(assigmnentclient) << "Assignment server socket is" << _assignmentServerSocket; qCDebug(assignment_client) << "Assignment server socket is" << _assignmentServerSocket;
// call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required
qCDebug(assigmnentclient) << "Waiting for assignment -" << _requestAssignment; qCDebug(assignment_client) << "Waiting for assignment -" << _requestAssignment;
if (_assignmentServerHostname != "localhost") { if (_assignmentServerHostname != "localhost") {
qCDebug(assigmnentclient) << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort(); qCDebug(assignment_client) << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort();
} }
connect(&_requestTimer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); connect(&_requestTimer, SIGNAL(timeout()), SLOT(sendAssignmentRequest()));
@ -129,7 +129,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
_assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort); _assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort);
_assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor"); _assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor");
qCDebug(assigmnentclient) << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket; qCDebug(assignment_client) << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;
// Hook up a timer to send this child's status to the Monitor once per second // Hook up a timer to send this child's status to the Monitor once per second
setUpStatusToMonitor(); setUpStatusToMonitor();
@ -140,7 +140,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
} }
void AssignmentClient::stopAssignmentClient() { void AssignmentClient::stopAssignmentClient() {
qCDebug(assigmnentclient) << "Forced stop of assignment-client."; qCDebug(assignment_client) << "Forced stop of assignment-client.";
_requestTimer.stop(); _requestTimer.stop();
_statsTimerACM.stop(); _statsTimerACM.stop();
@ -218,14 +218,14 @@ void AssignmentClient::sendAssignmentRequest() {
quint16 localAssignmentServerPort; quint16 localAssignmentServerPort;
if (nodeList->getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, localAssignmentServerPort)) { if (nodeList->getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, localAssignmentServerPort)) {
if (localAssignmentServerPort != _assignmentServerSocket.getPort()) { if (localAssignmentServerPort != _assignmentServerSocket.getPort()) {
qCDebug(assigmnentclient) << "Port for local assignment server read from shared memory is" qCDebug(assignment_client) << "Port for local assignment server read from shared memory is"
<< localAssignmentServerPort; << localAssignmentServerPort;
_assignmentServerSocket.setPort(localAssignmentServerPort); _assignmentServerSocket.setPort(localAssignmentServerPort);
nodeList->setAssignmentServerSocket(_assignmentServerSocket); nodeList->setAssignmentServerSocket(_assignmentServerSocket);
} }
} else { } else {
qCWarning(assigmnentclient) << "Failed to read local assignment server port from shared memory" qCWarning(assignment_client) << "Failed to read local assignment server port from shared memory"
<< "- will send assignment request to previous assignment server socket."; << "- will send assignment request to previous assignment server socket.";
} }
} }
@ -235,10 +235,10 @@ void AssignmentClient::sendAssignmentRequest() {
} }
void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message) { void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message) {
qCDebug(assigmnentclient) << "Received a PacketType::CreateAssignment - attempting to unpack."; qCDebug(assignment_client) << "Received a PacketType::CreateAssignment - attempting to unpack.";
if (_currentAssignment) { if (_currentAssignment) {
qCWarning(assigmnentclient) << "Received a PacketType::CreateAssignment while still running an active assignment. Ignoring."; qCWarning(assignment_client) << "Received a PacketType::CreateAssignment while still running an active assignment. Ignoring.";
return; return;
} }
@ -246,7 +246,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessa
_currentAssignment = AssignmentFactory::unpackAssignment(*message); _currentAssignment = AssignmentFactory::unpackAssignment(*message);
if (_currentAssignment && !_isAssigned) { if (_currentAssignment && !_isAssigned) {
qDebug(assigmnentclient) << "Received an assignment -" << *_currentAssignment; qDebug(assignment_client) << "Received an assignment -" << *_currentAssignment;
_isAssigned = true; _isAssigned = true;
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
@ -256,7 +256,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessa
nodeList->getDomainHandler().setSockAddr(message->getSenderSockAddr(), _assignmentServerHostname); nodeList->getDomainHandler().setSockAddr(message->getSenderSockAddr(), _assignmentServerHostname);
nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID()); nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID());
qCDebug(assigmnentclient) << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString(); qCDebug(assignment_client) << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
// start the deployed assignment // start the deployed assignment
QThread* workerThread = new QThread; QThread* workerThread = new QThread;
@ -284,7 +284,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessa
// Starts an event loop, and emits workerThread->started() // Starts an event loop, and emits workerThread->started()
workerThread->start(); workerThread->start();
} else { } else {
qCWarning(assigmnentclient) << "Received an assignment that could not be unpacked. Re-requesting."; qCWarning(assignment_client) << "Received an assignment that could not be unpacked. Re-requesting.";
} }
} }
@ -294,10 +294,10 @@ void AssignmentClient::handleStopNodePacket(QSharedPointer<ReceivedMessage> mess
if (senderSockAddr.getAddress() == QHostAddress::LocalHost || if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) { senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
qCDebug(assigmnentclient) << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketType::StopNode."; qCDebug(assignment_client) << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketType::StopNode.";
QCoreApplication::quit(); QCoreApplication::quit();
} else { } else {
qCWarning(assigmnentclient) << "Got a stop packet from other than localhost."; qCWarning(assignment_client) << "Got a stop packet from other than localhost.";
} }
} }
@ -317,7 +317,7 @@ void AssignmentClient::handleAuthenticationRequest() {
// ask the account manager to log us in from the env variables // ask the account manager to log us in from the env variables
accountManager->requestAccessToken(username, password); accountManager->requestAccessToken(username, password);
} else { } else {
qCWarning(assigmnentclient) << "Authentication was requested against" << qPrintable(accountManager->getAuthURL().toString()) qCWarning(assignment_client) << "Authentication was requested against" << qPrintable(accountManager->getAuthURL().toString())
<< "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV) << "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV)
<< "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate."; << "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate.";
@ -335,7 +335,7 @@ void AssignmentClient::assignmentCompleted() {
// reset the logging target to the the CHILD_TARGET_NAME // reset the logging target to the the CHILD_TARGET_NAME
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
qCDebug(assigmnentclient) << "Assignment finished or never started - waiting for new assignment."; qCDebug(assignment_client) << "Assignment finished or never started - waiting for new assignment.";
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();

View file

@ -11,4 +11,4 @@
#include "AssignmentClientLogging.h" #include "AssignmentClientLogging.h"
Q_LOGGING_CATEGORY(assigmnentclient, "hifi.assignment-client") Q_LOGGING_CATEGORY(assignment_client, "hifi.assignment-client")

View file

@ -14,6 +14,6 @@
#include <QLoggingCategory> #include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(assigmnentclient) Q_DECLARE_LOGGING_CATEGORY(assignment_client)
#endif // hifi_AssignmentClientLogging_h #endif // hifi_AssignmentClientLogging_h

View file

@ -269,19 +269,17 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData&
bool forceSilentBlock = true; bool forceSilentBlock = true;
if (!streamToAdd.getLastPopOutput().isNull()) { if (!streamToAdd.getLastPopOutput().isNull()) {
bool isInjector = dynamic_cast<const InjectedAudioStream*>(&streamToAdd);
// reptition with fade is enabled, and we do have a valid previous frame to repeat // in an injector, just go silent - the injector has likely ended
// so we mix the previously-mixed block // in other inputs (microphone, &c.), repeat with fade to avoid the harsh jump to silence
// this is preferable to not mixing it at all to avoid the harsh jump to silence
// we'll repeat the last block until it has a block to mix // we'll repeat the last block until it has a block to mix
// and we'll gradually fade that repeated block into silence. // and we'll gradually fade that repeated block into silence.
// calculate its fade factor, which depends on how many times it's already been repeated. // calculate its fade factor, which depends on how many times it's already been repeated.
repeatedFrameFadeFactor = calculateRepeatedFrameFadeFactor(streamToAdd.getConsecutiveNotMixedCount() - 1); repeatedFrameFadeFactor = calculateRepeatedFrameFadeFactor(streamToAdd.getConsecutiveNotMixedCount() - 1);
if (repeatedFrameFadeFactor > 0.0f) { if (!isInjector && repeatedFrameFadeFactor > 0.0f) {
// apply the repeatedFrameFadeFactor to the gain // apply the repeatedFrameFadeFactor to the gain
gain *= repeatedFrameFadeFactor; gain *= repeatedFrameFadeFactor;

View file

@ -6,8 +6,8 @@ if (WIN32)
include(ExternalProject) include(ExternalProject)
ExternalProject_Add( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi.zip URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi2.zip
URL_MD5 11c8a7728d6eda7223df800e10b70723 URL_MD5 272b27bd6c211c45c0c23d4701b63b5e
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND "" BUILD_COMMAND ""
INSTALL_COMMAND "" INSTALL_COMMAND ""

View file

@ -49,6 +49,7 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT)
TARGET ${TARGET_NAME} TARGET ${TARGET_NAME}
POST_BUILD POST_BUILD
COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E remove ${QTAUDIO_PATH}/qtaudio_windows.dll && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.dll ${QTAUDIO_PATH} && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.pdb ${QTAUDIO_PATH} ) COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E remove ${QTAUDIO_PATH}/qtaudio_windows.dll && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.dll ${QTAUDIO_PATH} && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.pdb ${QTAUDIO_PATH} )
COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windowsd.dll ( ${CMAKE_COMMAND} -E remove ${QTAUDIO_PATH}/qtaudio_windowsd.dll && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.dll ${QTAUDIO_PATH} && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.pdb ${QTAUDIO_PATH} )
) )
endif () endif ()

View file

@ -771,6 +771,21 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) {
// loop through the groups mentioned on the settings page and ask if this user is in each. The replies // loop through the groups mentioned on the settings page and ask if this user is in each. The replies
// will be received asynchronously and permissions will be updated as the answers come in. // will be received asynchronously and permissions will be updated as the answers come in.
QJsonObject json;
QSet<QString> groupIDSet;
foreach (QUuid groupID, _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs()) {
groupIDSet += groupID.toString().mid(1,36);
}
if (groupIDSet.isEmpty()) {
// if no groups are in the permissions settings, don't ask who is in which groups.
return;
}
QJsonArray groupIDs = QJsonArray::fromStringList(groupIDSet.toList());
json["groups"] = groupIDs;
// if we've already asked, wait for the answer before asking again // if we've already asked, wait for the answer before asking again
QString lowerUsername = username.toLower(); QString lowerUsername = username.toLower();
if (_inFlightGroupMembershipsRequests.contains(lowerUsername)) { if (_inFlightGroupMembershipsRequests.contains(lowerUsername)) {
@ -779,13 +794,6 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) {
} }
_inFlightGroupMembershipsRequests += lowerUsername; _inFlightGroupMembershipsRequests += lowerUsername;
QJsonObject json;
QSet<QString> groupIDSet;
foreach (QUuid groupID, _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs()) {
groupIDSet += groupID.toString().mid(1,36);
}
QJsonArray groupIDs = QJsonArray::fromStringList(groupIDSet.toList());
json["groups"] = groupIDs;
JSONCallbackParameters callbackParams; JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this; callbackParams.jsonCallbackReceiver = this;

View file

@ -1749,7 +1749,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
_ephemeralACScripts[scriptAssignment->getUUID()] = formData[0].second; _ephemeralACScripts[scriptAssignment->getUUID()] = formData[0].second;
// add the script assigment to the assignment queue // add the script assignment to the assignment queue
SharedAssignmentPointer sharedScriptedAssignment(scriptAssignment); SharedAssignmentPointer sharedScriptedAssignment(scriptAssignment);
_unfulfilledAssignments.enqueue(sharedScriptedAssignment); _unfulfilledAssignments.enqueue(sharedScriptedAssignment);
_allAssignments.insert(sharedScriptedAssignment->getUUID(), sharedScriptedAssignment); _allAssignments.insert(sharedScriptedAssignment->getUUID(), sharedScriptedAssignment);

View file

@ -68,6 +68,7 @@
#include <InfoView.h> #include <InfoView.h>
#include <input-plugins/InputPlugin.h> #include <input-plugins/InputPlugin.h>
#include <controllers/UserInputMapper.h> #include <controllers/UserInputMapper.h>
#include <controllers/ScriptingInterface.h>
#include <controllers/StateController.h> #include <controllers/StateController.h>
#include <UserActivityLoggerScriptingInterface.h> #include <UserActivityLoggerScriptingInterface.h>
#include <LogHandler.h> #include <LogHandler.h>
@ -4918,6 +4919,10 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data()); scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine)); scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine));
auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
scriptEngine->registerGlobalObject("Controller", scriptingInterface.data());
UserInputMapper::registerControllerTypes(scriptEngine);
} }
bool Application::canAcceptURL(const QString& urlString) const { bool Application::canAcceptURL(const QString& urlString) const {

View file

@ -355,7 +355,7 @@ Menu::Menu() {
//const QString = "1024 MB"; //const QString = "1024 MB";
//const QString = "2048 MB"; //const QString = "2048 MB";
// Developer > Render > Resolution // Developer > Render > Maximum Texture Memory
MenuWrapper* textureMenu = renderOptionsMenu->addMenu(MenuOption::RenderMaxTextureMemory); MenuWrapper* textureMenu = renderOptionsMenu->addMenu(MenuOption::RenderMaxTextureMemory);
QActionGroup* textureGroup = new QActionGroup(textureMenu); QActionGroup* textureGroup = new QActionGroup(textureMenu);
textureGroup->setExclusive(true); textureGroup->setExclusive(true);
@ -383,6 +383,43 @@ Menu::Menu() {
gpu::Texture::setAllowedGPUMemoryUsage(newMaxTextureMemory); gpu::Texture::setAllowedGPUMemoryUsage(newMaxTextureMemory);
}); });
#ifdef Q_OS_WIN
#define MIN_CORES_FOR_INCREMENTAL_TEXTURES 5
bool recommendedIncrementalTransfers = (QThread::idealThreadCount() >= MIN_CORES_FOR_INCREMENTAL_TEXTURES);
bool recommendedSparseTextures = recommendedIncrementalTransfers;
qDebug() << "[TEXTURE TRANSFER SUPPORT]"
<< "\n\tidealThreadCount:" << QThread::idealThreadCount()
<< "\n\tRECOMMENDED enableSparseTextures:" << recommendedSparseTextures
<< "\n\tRECOMMENDED enableIncrementalTextures:" << recommendedIncrementalTransfers;
gpu::Texture::setEnableIncrementalTextureTransfers(recommendedIncrementalTransfers);
gpu::Texture::setEnableSparseTextures(recommendedSparseTextures);
// Developer > Render > Enable Dynamic Texture Management
{
auto action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableDynamicTextureManagement, 0, recommendedSparseTextures);
connect(action, &QAction::triggered, [&](bool checked) {
qDebug() << "[TEXTURE TRANSFER SUPPORT] --- Enable Dynamic Texture Management menu option:" << checked;
gpu::Texture::setEnableSparseTextures(checked);
});
}
// Developer > Render > Enable Incremental Texture Transfer
{
auto action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableIncrementalTextureTransfer, 0, recommendedIncrementalTransfers);
connect(action, &QAction::triggered, [&](bool checked) {
qDebug() << "[TEXTURE TRANSFER SUPPORT] --- Enable Incremental Texture Transfer menu option:" << checked;
gpu::Texture::setEnableIncrementalTextureTransfers(checked);
});
}
#else
qDebug() << "[TEXTURE TRANSFER SUPPORT] Incremental Texture Transfer and Dynamic Texture Management not supported on this platform.";
#endif
// Developer > Render > LOD Tools // Developer > Render > LOD Tools
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0, dialogsManager.data(), SLOT(lodTools())); addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0, dialogsManager.data(), SLOT(lodTools()));

View file

@ -98,6 +98,8 @@ namespace MenuOption {
const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoLocalAudio = "Echo Local Audio";
const QString EchoServerAudio = "Echo Server Audio"; const QString EchoServerAudio = "Echo Server Audio";
const QString EnableCharacterController = "Enable avatar collisions"; const QString EnableCharacterController = "Enable avatar collisions";
const QString EnableIncrementalTextureTransfer = "Enable Incremental Texture Transfer";
const QString EnableDynamicTextureManagement = "Enable Dynamic Texture Management";
const QString EnableInverseKinematics = "Enable Inverse Kinematics"; const QString EnableInverseKinematics = "Enable Inverse Kinematics";
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
const QString ExpandMyAvatarTiming = "Expand /myAvatar"; const QString ExpandMyAvatarTiming = "Expand /myAvatar";

View file

@ -87,7 +87,7 @@ public:
AudioOutputIODevice(MixedProcessedAudioStream& receivedAudioStream, AudioClient* audio) : AudioOutputIODevice(MixedProcessedAudioStream& receivedAudioStream, AudioClient* audio) :
_receivedAudioStream(receivedAudioStream), _audio(audio), _unfulfilledReads(0) {}; _receivedAudioStream(receivedAudioStream), _audio(audio), _unfulfilledReads(0) {};
void start() { open(QIODevice::ReadOnly); } void start() { open(QIODevice::ReadOnly | QIODevice::Unbuffered); }
void stop() { close(); } void stop() { close(); }
qint64 readData(char * data, qint64 maxSize) override; qint64 readData(char * data, qint64 maxSize) override;
qint64 writeData(const char * data, qint64 maxSize) override { return 0; } qint64 writeData(const char * data, qint64 maxSize) override { return 0; }
@ -167,7 +167,8 @@ public slots:
int setOutputBufferSize(int numFrames, bool persist = true); int setOutputBufferSize(int numFrames, bool persist = true);
virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector) override; bool outputLocalInjector(bool isStereo, AudioInjector* injector) override;
bool shouldLoopbackInjectors() override { return _shouldEchoToServer; }
bool switchInputToAudioDevice(const QString& inputDeviceName); bool switchInputToAudioDevice(const QString& inputDeviceName);
bool switchOutputToAudioDevice(const QString& outputDeviceName); bool switchOutputToAudioDevice(const QString& outputDeviceName);

View file

@ -33,6 +33,7 @@ public:
public slots: public slots:
virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector) = 0; virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector) = 0;
virtual bool shouldLoopbackInjectors() { return false; }
virtual void setIsStereoInput(bool stereo) = 0; virtual void setIsStereoInput(bool stereo) = 0;
}; };

View file

@ -211,6 +211,7 @@ int64_t AudioInjector::injectNextFrame() {
} }
// if we haven't setup the packet to send then do so now // if we haven't setup the packet to send then do so now
static int loopbackOptionOffset = -1;
static int positionOptionOffset = -1; static int positionOptionOffset = -1;
static int volumeOptionOffset = -1; static int volumeOptionOffset = -1;
static int audioDataOffset = -1; static int audioDataOffset = -1;
@ -260,10 +261,9 @@ int64_t AudioInjector::injectNextFrame() {
// pack the stereo/mono type of the stream // pack the stereo/mono type of the stream
audioPacketStream << _options.stereo; audioPacketStream << _options.stereo;
// pack the flag for loopback. Now, we don't loopback // pack the flag for loopback, if requested
// and _always_ play locally, so loopbackFlag should be loopbackOptionOffset = _currentPacket->pos();
// false always. uchar loopbackFlag = (_localAudioInterface && _localAudioInterface->shouldLoopbackInjectors());
uchar loopbackFlag = (uchar)false;
audioPacketStream << loopbackFlag; audioPacketStream << loopbackFlag;
// pack the position for injected audio // pack the position for injected audio
@ -293,6 +293,7 @@ int64_t AudioInjector::injectNextFrame() {
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
} }
} }
if (!_frameTimer->isValid()) { if (!_frameTimer->isValid()) {
// in the case where we have been restarted, the frame timer will be invalid and we need to start it back over here // in the case where we have been restarted, the frame timer will be invalid and we need to start it back over here
_frameTimer->restart(); _frameTimer->restart();
@ -317,6 +318,9 @@ int64_t AudioInjector::injectNextFrame() {
// pack the sequence number // pack the sequence number
_currentPacket->writePrimitive(_outgoingSequenceNumber); _currentPacket->writePrimitive(_outgoingSequenceNumber);
_currentPacket->seek(loopbackOptionOffset);
_currentPacket->writePrimitive((uchar)(_localAudioInterface && _localAudioInterface->shouldLoopbackInjectors()));
_currentPacket->seek(positionOptionOffset); _currentPacket->seek(positionOptionOffset);
_currentPacket->writePrimitive(_options.position); _currentPacket->writePrimitive(_options.position);
_currentPacket->writePrimitive(_options.orientation); _currentPacket->writePrimitive(_options.orientation);

View file

@ -8,6 +8,7 @@
#pragma once #pragma once
#include "DisplayPlugin.h" #include "DisplayPlugin.h"
#include <gl/Config.h>
#include <condition_variable> #include <condition_variable>
#include <memory> #include <memory>
@ -18,7 +19,6 @@
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <SimpleMovingAverage.h> #include <SimpleMovingAverage.h>
#include <gl/GLEscrow.h>
#include <shared/RateCounter.h> #include <shared/RateCounter.h>
namespace gpu { namespace gpu {
@ -35,7 +35,6 @@ protected:
using Mutex = std::mutex; using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>; using Lock = std::unique_lock<Mutex>;
using Condition = std::condition_variable; using Condition = std::condition_variable;
using TextureEscrow = GLEscrow<gpu::TexturePointer>;
public: public:
// These must be final to ensure proper ordering of operations // These must be final to ensure proper ordering of operations
// between the main thread and the presentation thread // between the main thread and the presentation thread

View file

@ -67,8 +67,8 @@ void GLWidget::createContext() {
_context = new gl::Context(); _context = new gl::Context();
_context->setWindow(windowHandle()); _context->setWindow(windowHandle());
_context->create(); _context->create();
_context->clear();
_context->makeCurrent(); _context->makeCurrent();
_context->clear();
} }
bool GLWidget::makeCurrent() { bool GLWidget::makeCurrent() {

View file

@ -6,7 +6,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "OffscreenQmlSurface.h" #include "OffscreenQmlSurface.h"
#include "OglplusHelpers.h" #include "Config.h"
#include <queue>
#include <set>
#include <map>
#include <QtWidgets/QWidget> #include <QtWidgets/QWidget>
#include <QtQml/QtQml> #include <QtQml/QtQml>
@ -116,6 +120,108 @@ static const QEvent::Type RENDER = QEvent::Type(QEvent::User + 2);
static const QEvent::Type RESIZE = QEvent::Type(QEvent::User + 3); static const QEvent::Type RESIZE = QEvent::Type(QEvent::User + 3);
static const QEvent::Type STOP = QEvent::Type(QEvent::User + 4); static const QEvent::Type STOP = QEvent::Type(QEvent::User + 4);
class RawTextureRecycler {
public:
using TexturePtr = GLuint;
RawTextureRecycler(bool useMipmaps) : _useMipmaps(useMipmaps) {}
void setSize(const uvec2& size);
void clear();
TexturePtr getNextTexture();
void recycleTexture(GLuint texture);
private:
struct TexInfo {
TexturePtr _tex { 0 };
uvec2 _size;
bool _active { false };
TexInfo() {}
TexInfo(TexturePtr tex, const uvec2& size) : _tex(tex), _size(size) {}
};
using Map = std::map<GLuint, TexInfo>;
using Queue = std::queue<TexturePtr>;
Map _allTextures;
Queue _readyTextures;
uvec2 _size { 1920, 1080 };
bool _useMipmaps;
};
void RawTextureRecycler::setSize(const uvec2& size) {
if (size == _size) {
return;
}
_size = size;
while (!_readyTextures.empty()) {
_readyTextures.pop();
}
std::set<Map::key_type> toDelete;
std::for_each(_allTextures.begin(), _allTextures.end(), [&](Map::const_reference item) {
if (!item.second._active && item.second._size != _size) {
toDelete.insert(item.first);
}
});
std::for_each(toDelete.begin(), toDelete.end(), [&](Map::key_type key) {
_allTextures.erase(key);
});
}
void RawTextureRecycler::clear() {
while (!_readyTextures.empty()) {
_readyTextures.pop();
}
_allTextures.clear();
}
RawTextureRecycler::TexturePtr RawTextureRecycler::getNextTexture() {
if (_readyTextures.empty()) {
TexturePtr newTexture;
glGenTextures(1, &newTexture);
glBindTexture(GL_TEXTURE_2D, newTexture);
if (_useMipmaps) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.2f);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _size.x, _size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
_allTextures[newTexture] = TexInfo { newTexture, _size };
_readyTextures.push(newTexture);
}
TexturePtr result = _readyTextures.front();
_readyTextures.pop();
auto& item = _allTextures[result];
item._active = true;
return result;
}
void RawTextureRecycler::recycleTexture(GLuint texture) {
Q_ASSERT(_allTextures.count(texture));
auto& item = _allTextures[texture];
Q_ASSERT(item._active);
item._active = false;
if (item._size != _size) {
// Buh-bye
_allTextures.erase(texture);
return;
}
_readyTextures.push(item._tex);
}
class OffscreenQmlRenderThread : public QThread { class OffscreenQmlRenderThread : public QThread {
public: public:
OffscreenQmlRenderThread(OffscreenQmlSurface* surface, QOpenGLContext* shareContext); OffscreenQmlRenderThread(OffscreenQmlSurface* surface, QOpenGLContext* shareContext);
@ -165,9 +271,9 @@ private:
OffscreenQmlSurface* _surface{ nullptr }; OffscreenQmlSurface* _surface{ nullptr };
QQuickWindow* _quickWindow{ nullptr }; QQuickWindow* _quickWindow{ nullptr };
QMyQuickRenderControl* _renderControl{ nullptr }; QMyQuickRenderControl* _renderControl{ nullptr };
FramebufferPtr _fbo; GLuint _fbo { 0 };
RenderbufferPtr _depthStencil; GLuint _depthStencil { 0 };
TextureRecycler _textures { true }; RawTextureRecycler _textures { true };
GLTextureEscrow _escrow; GLTextureEscrow _escrow;
uint64_t _lastRenderTime{ 0 }; uint64_t _lastRenderTime{ 0 };
@ -253,24 +359,23 @@ bool OffscreenQmlRenderThread::event(QEvent *e) {
} }
void OffscreenQmlRenderThread::setupFbo() { void OffscreenQmlRenderThread::setupFbo() {
using namespace oglplus;
_textures.setSize(_size); _textures.setSize(_size);
if (_depthStencil) {
try { glDeleteRenderbuffers(1, &_depthStencil);
_depthStencil.reset(new Renderbuffer()); _depthStencil = 0;
Context::Bound(Renderbuffer::Target::Renderbuffer, *_depthStencil)
.Storage(
PixelDataInternalFormat::DepthComponent,
_size.x, _size.y);
_fbo.reset(new Framebuffer());
_fbo->Bind(Framebuffer::Target::Draw);
_fbo->AttachRenderbuffer(Framebuffer::Target::Draw,
FramebufferAttachment::Depth, *_depthStencil);
DefaultFramebuffer().Bind(Framebuffer::Target::Draw);
} catch (oglplus::Error& error) {
qWarning() << "OpenGL error in QML render setup: " << error.what();
} }
glGenRenderbuffers(1, &_depthStencil);
glBindRenderbuffer(GL_RENDERBUFFER, _depthStencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _size.x, _size.y);
if (_fbo) {
glDeleteFramebuffers(1, &_fbo);
_fbo = 0;
}
glGenFramebuffers(1, &_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencil);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
} }
QJsonObject OffscreenQmlRenderThread::getGLContextData() { QJsonObject OffscreenQmlRenderThread::getGLContextData() {
@ -309,8 +414,15 @@ void OffscreenQmlRenderThread::init() {
void OffscreenQmlRenderThread::cleanup() { void OffscreenQmlRenderThread::cleanup() {
_renderControl->invalidate(); _renderControl->invalidate();
_fbo.reset(); if (_depthStencil) {
_depthStencil.reset(); glDeleteRenderbuffers(1, &_depthStencil);
_depthStencil = 0;
}
if (_fbo) {
glDeleteFramebuffers(1, &_fbo);
_fbo = 0;
}
_textures.clear(); _textures.clear();
_canvas.doneCurrent(); _canvas.doneCurrent();
@ -371,42 +483,22 @@ void OffscreenQmlRenderThread::render() {
releaseMainThread.trigger(); releaseMainThread.trigger();
} }
using namespace oglplus; _quickWindow->setRenderTarget(_fbo, QSize(_size.x, _size.y));
_quickWindow->setRenderTarget(GetName(*_fbo), QSize(_size.x, _size.y));
try { try {
PROFILE_RANGE("qml_render") GLuint texture = _textures.getNextTexture();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
TexturePtr texture = _textures.getNextTexture(); glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0);
try {
_fbo->Bind(Framebuffer::Target::Draw);
_fbo->AttachTexture(Framebuffer::Target::Draw, FramebufferAttachment::Color, *texture, 0);
_fbo->Complete(Framebuffer::Target::Draw);
} catch (oglplus::Error& error) {
qWarning() << "OpenGL error in QML render: " << error.what();
// In case we are failing from a failed setupFbo, reset fbo before next render
setupFbo();
throw;
}
{
PROFILE_RANGE("qml_render->rendercontrol") PROFILE_RANGE("qml_render->rendercontrol")
_renderControl->render(); _renderControl->render();
// FIXME The web browsers seem to be leaving GL in an error state.
// Need a debug context with sync logging to figure out why.
// for now just clear the errors
glGetError();
}
Context::Bound(oglplus::Texture::Target::_2D, *texture).GenerateMipmap(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, texture);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
// FIXME probably unecessary
DefaultFramebuffer().Bind(Framebuffer::Target::Draw);
_quickWindow->resetOpenGLState(); _quickWindow->resetOpenGLState();
_escrow.submit(GetName(*texture)); _escrow.submit(texture);
_lastRenderTime = usecTimestampNow(); _lastRenderTime = usecTimestampNow();
} catch (std::runtime_error& error) { } catch (std::runtime_error& error) {
qWarning() << "Failed to render QML: " << error.what(); qWarning() << "Failed to render QML: " << error.what();

View file

@ -18,7 +18,6 @@
std::unordered_map<TexturePointer, nvtxRangeId_t> _map; std::unordered_map<TexturePointer, nvtxRangeId_t> _map;
#endif #endif
//#define TEXTURE_TRANSFER_PBOS
#ifdef TEXTURE_TRANSFER_PBOS #ifdef TEXTURE_TRANSFER_PBOS
#define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024) #define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024)
@ -62,11 +61,16 @@ void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texture
void GLTextureTransferHelper::setup() { void GLTextureTransferHelper::setup() {
#ifdef THREADED_TEXTURE_TRANSFER #ifdef THREADED_TEXTURE_TRANSFER
_context.makeCurrent(); _context.makeCurrent();
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
// FIXME don't use opengl 4.5 DSA functionality without verifying it's present
glCreateRenderbuffers(1, &_drawRenderbuffer); glCreateRenderbuffers(1, &_drawRenderbuffer);
glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128); glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128);
glCreateFramebuffers(1, &_drawFramebuffer); glCreateFramebuffers(1, &_drawFramebuffer);
glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer); glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer);
glCreateFramebuffers(1, &_readFramebuffer); glCreateFramebuffers(1, &_readFramebuffer);
#endif
#ifdef TEXTURE_TRANSFER_PBOS #ifdef TEXTURE_TRANSFER_PBOS
std::array<GLuint, TEXTURE_TRANSFER_PBO_COUNT> pbos; std::array<GLuint, TEXTURE_TRANSFER_PBO_COUNT> pbos;
glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]); glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]);
@ -84,7 +88,9 @@ void GLTextureTransferHelper::setup() {
void GLTextureTransferHelper::shutdown() { void GLTextureTransferHelper::shutdown() {
#ifdef THREADED_TEXTURE_TRANSFER #ifdef THREADED_TEXTURE_TRANSFER
_context.makeCurrent(); _context.makeCurrent();
#endif
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
glDeleteFramebuffers(1, &_drawFramebuffer); glDeleteFramebuffers(1, &_drawFramebuffer);
_drawFramebuffer = 0; _drawFramebuffer = 0;
@ -165,6 +171,11 @@ bool GLTextureTransferHelper::process() {
} }
gltexture->finishTransfer(); gltexture->finishTransfer();
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
// FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use
#endif
#ifdef THREADED_TEXTURE_TRANSFER #ifdef THREADED_TEXTURE_TRANSFER
clientWait(); clientWait();
#endif #endif

View file

@ -21,6 +21,14 @@
#define THREADED_TEXTURE_TRANSFER #define THREADED_TEXTURE_TRANSFER
#endif #endif
#ifdef THREADED_TEXTURE_TRANSFER
// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread
// also, the current draw code is implicitly using OpenGL 4.5 functionality
//#define TEXTURE_TRANSFER_FORCE_DRAW
// FIXME PBO's increase the complexity and don't seem to work reliably
//#define TEXTURE_TRANSFER_PBOS
#endif
namespace gpu { namespace gl { namespace gpu { namespace gl {
using TextureList = std::list<TexturePointer>; using TextureList = std::list<TexturePointer>;
@ -43,11 +51,15 @@ public:
private: private:
#ifdef THREADED_TEXTURE_TRANSFER #ifdef THREADED_TEXTURE_TRANSFER
::gl::OffscreenContext _context; ::gl::OffscreenContext _context;
#endif
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
// Framebuffers / renderbuffers for forcing access to the texture on the transfer thread // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread
GLuint _drawRenderbuffer { 0 }; GLuint _drawRenderbuffer { 0 };
GLuint _drawFramebuffer { 0 }; GLuint _drawFramebuffer { 0 };
GLuint _readFramebuffer { 0 }; GLuint _readFramebuffer { 0 };
#endif #endif
// A mutex for protecting items access on the render and transfer threads // A mutex for protecting items access on the render and transfer threads
Mutex _mutex; Mutex _mutex;
// Commands that have been submitted for execution on the texture transfer thread // Commands that have been submitted for execution on the texture transfer thread

View file

@ -16,8 +16,8 @@
#include <unordered_map> #include <unordered_map>
#include <glm/gtx/component_wise.hpp> #include <glm/gtx/component_wise.hpp>
#include <QtCore/QDebug>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtCore/QProcessEnvironment>
#include "../gl/GLTexelFormat.h" #include "../gl/GLTexelFormat.h"
@ -25,13 +25,6 @@ using namespace gpu;
using namespace gpu::gl; using namespace gpu::gl;
using namespace gpu::gl45; using namespace gpu::gl45;
#ifdef Q_OS_WIN
static const QString DEBUG_FLAG("HIFI_DISABLE_SPARSE_TEXTURES");
static bool enableSparseTextures = !QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
#else
static bool enableSparseTextures = false;
#endif
// Allocate 1 MB of buffer space for paged transfers // Allocate 1 MB of buffer space for paged transfers
#define DEFAULT_PAGE_BUFFER_SIZE (1024*1024) #define DEFAULT_PAGE_BUFFER_SIZE (1024*1024)
#define DEFAULT_GL_PIXEL_ALIGNMENT 4 #define DEFAULT_GL_PIXEL_ALIGNMENT 4
@ -253,7 +246,7 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) {
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable) GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable)
: GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) { : GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) {
if (enableSparseTextures && _transferrable) { if (_transferrable && Texture::getEnableSparseTextures()) {
_sparseInfo.maybeMakeSparse(); _sparseInfo.maybeMakeSparse();
} }
} }
@ -352,6 +345,31 @@ void GL45Texture::startTransfer() {
} }
bool GL45Texture::continueTransfer() { bool GL45Texture::continueTransfer() {
if (!Texture::getEnableIncrementalTextureTransfers()) {
size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1;
for (uint8_t face = 0; face < maxFace; ++face) {
for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) {
if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) {
auto mip = _gpuObject.accessStoredMipFace(mipLevel, face);
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat());
auto size = _gpuObject.evalMipDimensions(mipLevel);
if (GL_TEXTURE_2D == _target) {
glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
} else if (GL_TEXTURE_CUBE_MAP == _target) {
// 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());
auto target = CUBE_FACE_LAYOUT[face];
glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
} else {
Q_ASSERT(false);
}
(void)CHECK_GL_ERROR();
}
}
}
return false;
}
static std::vector<uint8_t> buffer; static std::vector<uint8_t> buffer;
if (buffer.empty()) { if (buffer.empty()) {
buffer.resize(DEFAULT_PAGE_BUFFER_SIZE); buffer.resize(DEFAULT_PAGE_BUFFER_SIZE);

View file

@ -9,6 +9,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QtCore/QDebug>
#include "Texture.h" #include "Texture.h"
#include <glm/gtc/constants.hpp> #include <glm/gtc/constants.hpp>
@ -28,6 +31,34 @@ std::atomic<uint32_t> Texture::_textureCPUCount{ 0 };
std::atomic<Texture::Size> Texture::_textureCPUMemoryUsage{ 0 }; std::atomic<Texture::Size> Texture::_textureCPUMemoryUsage{ 0 };
std::atomic<Texture::Size> Texture::_allowedCPUMemoryUsage { 0 }; std::atomic<Texture::Size> Texture::_allowedCPUMemoryUsage { 0 };
std::atomic<bool> Texture::_enableSparseTextures { false };
std::atomic<bool> Texture::_enableIncrementalTextureTransfers { false };
void Texture::setEnableSparseTextures(bool enabled) {
#ifdef Q_OS_WIN
qDebug() << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Sparse Textures and Dynamic Texture Management:" << enabled;
_enableSparseTextures = enabled;
if (!_enableIncrementalTextureTransfers && _enableSparseTextures) {
qDebug() << "[TEXTURE TRANSFER SUPPORT] WARNING - Sparse texture management requires incremental texture transfer enabled.";
}
#else
qDebug() << "[TEXTURE TRANSFER SUPPORT] Sparse Textures and Dynamic Texture Management not supported on this platform.";
#endif
}
void Texture::setEnableIncrementalTextureTransfers(bool enabled) {
#ifdef Q_OS_WIN
qDebug() << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Incremental Texture Transfer:" << enabled;
_enableIncrementalTextureTransfers = enabled;
if (!_enableIncrementalTextureTransfers && _enableSparseTextures) {
qDebug() << "[TEXTURE TRANSFER SUPPORT] WARNING - Sparse texture management requires incremental texture transfer enabled.";
}
#else
qDebug() << "[TEXTURE TRANSFER SUPPORT] Incremental Texture Transfer not supported on this platform.";
#endif
}
void Texture::updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { void Texture::updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize) {
if (prevObjectSize == newObjectSize) { if (prevObjectSize == newObjectSize) {
return; return;

View file

@ -144,6 +144,9 @@ class Texture : public Resource {
static std::atomic<Size> _textureCPUMemoryUsage; static std::atomic<Size> _textureCPUMemoryUsage;
static std::atomic<Size> _allowedCPUMemoryUsage; static std::atomic<Size> _allowedCPUMemoryUsage;
static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
static std::atomic<bool> _enableSparseTextures;
static std::atomic<bool> _enableIncrementalTextureTransfers;
public: public:
static uint32_t getTextureCPUCount(); static uint32_t getTextureCPUCount();
static Size getTextureCPUMemoryUsage(); static Size getTextureCPUMemoryUsage();
@ -154,6 +157,12 @@ public:
static Size getAllowedGPUMemoryUsage(); static Size getAllowedGPUMemoryUsage();
static void setAllowedGPUMemoryUsage(Size size); static void setAllowedGPUMemoryUsage(Size size);
static bool getEnableSparseTextures() { return _enableSparseTextures.load(); }
static bool getEnableIncrementalTextureTransfers() { return _enableIncrementalTextureTransfers.load(); }
static void setEnableSparseTextures(bool enabled);
static void setEnableIncrementalTextureTransfers(bool enabled);
class Usage { class Usage {
public: public:
enum FlagBit { enum FlagBit {

View file

@ -377,8 +377,10 @@ void GeometryResource::deleter() {
} }
void GeometryResource::setTextures() { void GeometryResource::setTextures() {
for (const FBXMaterial& material : _fbxGeometry->materials) { if (_fbxGeometry) {
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseUrl)); for (const FBXMaterial& material : _fbxGeometry->materials) {
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseUrl));
}
} }
} }
@ -457,7 +459,9 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, c
_textures[channel] = Texture { fbxTexture.name, texture }; _textures[channel] = Texture { fbxTexture.name, texture };
auto map = std::make_shared<model::TextureMap>(); auto map = std::make_shared<model::TextureMap>();
map->setTextureSource(texture->_textureSource); if (texture) {
map->setTextureSource(texture->_textureSource);
}
map->setTextureTransform(fbxTexture.transform); map->setTextureTransform(fbxTexture.transform);
return map; return map;

View file

@ -44,8 +44,11 @@ Material::Material(const Material& material) :
} }
Material& Material::operator= (const Material& material) { Material& Material::operator= (const Material& material) {
QMutexLocker locker(&_textureMapsMutex);
_key = (material._key); _key = (material._key);
_textureMaps = (material._textureMaps); _textureMaps = (material._textureMaps);
_hasCalculatedTextureInfo = false;
// copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer // copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer
Schema schema; Schema schema;
@ -112,6 +115,8 @@ void Material::setScattering(float scattering) {
} }
void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) { void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) {
QMutexLocker locker(&_textureMapsMutex);
if (textureMap) { if (textureMap) {
_key.setMapChannel(channel, (true)); _key.setMapChannel(channel, (true));
_textureMaps[channel] = textureMap; _textureMaps[channel] = textureMap;
@ -119,6 +124,7 @@ void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textur
_key.setMapChannel(channel, (false)); _key.setMapChannel(channel, (false));
_textureMaps.erase(channel); _textureMaps.erase(channel);
} }
_hasCalculatedTextureInfo = false;
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong(); _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
@ -173,6 +179,8 @@ void Material::resetOpacityMap() const {
const TextureMapPointer Material::getTextureMap(MapChannel channel) const { const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
QMutexLocker locker(&_textureMapsMutex);
auto result = _textureMaps.find(channel); auto result = _textureMaps.find(channel);
if (result != _textureMaps.end()) { if (result != _textureMaps.end()) {
return (result->second); return (result->second);
@ -180,3 +188,37 @@ const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
return TextureMapPointer(); return TextureMapPointer();
} }
} }
bool Material::calculateMaterialInfo() const {
if (!_hasCalculatedTextureInfo) {
QMutexLocker locker(&_textureMapsMutex);
bool allTextures = true; // assume we got this...
_textureSize = 0;
_textureCount = 0;
for (auto const &textureMapItem : _textureMaps) {
auto textureMap = textureMapItem.second;
if (textureMap) {
auto textureSoure = textureMap->getTextureSource();
if (textureSoure) {
auto texture = textureSoure->getGPUTexture();
if (texture) {
auto size = texture->getSize();
_textureSize += size;
_textureCount++;
} else {
allTextures = false;
}
} else {
allTextures = false;
}
} else {
allTextures = false;
}
}
_hasCalculatedTextureInfo = allTextures;
}
return _hasCalculatedTextureInfo;
}

View file

@ -11,6 +11,8 @@
#ifndef hifi_model_Material_h #ifndef hifi_model_Material_h
#define hifi_model_Material_h #define hifi_model_Material_h
#include <QMutex>
#include <bitset> #include <bitset>
#include <map> #include <map>
@ -324,7 +326,7 @@ public:
// The texture map to channel association // The texture map to channel association
void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap); void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap);
const TextureMaps& getTextureMaps() const { return _textureMaps; } const TextureMaps& getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe...
const TextureMapPointer getTextureMap(MapChannel channel) const; const TextureMapPointer getTextureMap(MapChannel channel) const;
// Albedo maps cannot have opacity detected until they are loaded // Albedo maps cannot have opacity detected until they are loaded
@ -344,12 +346,25 @@ public:
}; };
const UniformBufferView& getTexMapArrayBuffer() const { return _texMapArrayBuffer; } const UniformBufferView& getTexMapArrayBuffer() const { return _texMapArrayBuffer; }
int getTextureCount() const { calculateMaterialInfo(); return _textureCount; }
size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; }
bool hasTextureInfo() const { return _hasCalculatedTextureInfo; }
private: private:
mutable MaterialKey _key; mutable MaterialKey _key;
mutable UniformBufferView _schemaBuffer; mutable UniformBufferView _schemaBuffer;
mutable UniformBufferView _texMapArrayBuffer; mutable UniformBufferView _texMapArrayBuffer;
TextureMaps _textureMaps; TextureMaps _textureMaps;
mutable QMutex _textureMapsMutex { QMutex::Recursive };
mutable size_t _textureSize { 0 };
mutable int _textureCount { 0 };
mutable bool _hasCalculatedTextureInfo { false };
bool calculateMaterialInfo() const;
}; };
typedef std::shared_ptr< Material > MaterialPointer; typedef std::shared_ptr< Material > MaterialPointer;

View file

@ -536,15 +536,7 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
QUuid domainUUID; QUuid domainUUID;
packetStream >> domainUUID; packetStream >> domainUUID;
// if this was the first domain-server list from this domain, we've now connected if (_domainHandler.isConnected() && _domainHandler.getUUID() != domainUUID) {
if (!_domainHandler.isConnected()) {
_domainHandler.setUUID(domainUUID);
_domainHandler.setIsConnected(true);
// in case we didn't use a place name to get to this domain,
// give the address manager a chance to lookup a default one now
DependencyManager::get<AddressManager>()->lookupShareableNameForDomainID(domainUUID);
} else if (_domainHandler.getUUID() != domainUUID) {
// Recieved packet from different domain. // Recieved packet from different domain.
qWarning() << "IGNORING DomainList packet from" << domainUUID << "while connected to" << _domainHandler.getUUID(); qWarning() << "IGNORING DomainList packet from" << domainUUID << "while connected to" << _domainHandler.getUUID();
return; return;
@ -555,6 +547,16 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
packetStream >> newUUID; packetStream >> newUUID;
setSessionUUID(newUUID); setSessionUUID(newUUID);
// if this was the first domain-server list from this domain, we've now connected
if (!_domainHandler.isConnected()) {
_domainHandler.setUUID(domainUUID);
_domainHandler.setIsConnected(true);
// in case we didn't use a place name to get to this domain,
// give the address manager a chance to lookup a default one now
DependencyManager::get<AddressManager>()->lookupShareableNameForDomainID(domainUUID);
}
// pull the permissions/right/privileges for this node out of the stream // pull the permissions/right/privileges for this node out of the stream
NodePermissions newPermissions; NodePermissions newPermissions;
packetStream >> newPermissions; packetStream >> newPermissions;

View file

@ -71,39 +71,8 @@ void MeshPartPayload::updateTransform(const Transform& transform, const Transfor
void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) { void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) {
_drawMaterial = drawMaterial; _drawMaterial = drawMaterial;
calculateMaterialSize();
} }
bool MeshPartPayload::calculateMaterialSize() {
bool allTextures = true; // assume we got this...
_materialTextureSize = 0;
_materialTextureCount = 0;
auto textureMaps = _drawMaterial->getTextureMaps();
for (auto const &textureMapItem : textureMaps) {
auto textureMap = textureMapItem.second;
if (textureMap) {
auto textureSoure = textureMap->getTextureSource();
if (textureSoure) {
auto texture = textureSoure->getGPUTexture();
if (texture) {
//auto storedSize = texture->getStoredSize();
auto size = texture->getSize();
_materialTextureSize += size;
_materialTextureCount++;
} else {
allTextures = false;
}
} else {
allTextures = false;
}
} else {
allTextures = false;
}
}
return allTextures;
}
ItemKey MeshPartPayload::getKey() const { ItemKey MeshPartPayload::getKey() const {
ItemKey::Builder builder; ItemKey::Builder builder;
builder.withTypeShape(); builder.withTypeShape();
@ -378,7 +347,6 @@ void ModelMeshPartPayload::initCache() {
auto networkMaterial = _model->getGeometry()->getShapeMaterial(_shapeID); auto networkMaterial = _model->getGeometry()->getShapeMaterial(_shapeID);
if (networkMaterial) { if (networkMaterial) {
_drawMaterial = networkMaterial; _drawMaterial = networkMaterial;
calculateMaterialSize();
} }
} }
@ -429,7 +397,12 @@ ItemKey ModelMeshPartPayload::getKey() const {
} }
ShapeKey ModelMeshPartPayload::getShapeKey() const { ShapeKey ModelMeshPartPayload::getShapeKey() const {
assert(_model->isLoaded());
// guard against partially loaded meshes
if (!_model || !_model->isLoaded() || !_model->getGeometry()) {
return ShapeKey::Builder::invalid();
}
const FBXGeometry& geometry = _model->getFBXGeometry(); const FBXGeometry& geometry = _model->getFBXGeometry();
const auto& networkMeshes = _model->getGeometry()->getMeshes(); const auto& networkMeshes = _model->getGeometry()->getMeshes();

View file

@ -66,13 +66,9 @@ public:
bool _hasColorAttrib = false; bool _hasColorAttrib = false;
size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; }
size_t getMaterialTextureSize() { return _materialTextureSize; } size_t getMaterialTextureSize() { return _drawMaterial ? _drawMaterial->getTextureSize() : 0; }
int getMaterialTextureCount() { return _materialTextureCount; } int getMaterialTextureCount() { return _drawMaterial ? _drawMaterial->getTextureCount() : 0; }
bool calculateMaterialSize(); bool hasTextureInfo() const { return _drawMaterial ? _drawMaterial->hasTextureInfo() : false; }
protected:
size_t _materialTextureSize { 0 };
int _materialTextureCount { 0 };
}; };
namespace render { namespace render {

View file

@ -168,10 +168,9 @@ void Model::calculateTextureInfo() {
bool allTexturesLoaded = true; bool allTexturesLoaded = true;
foreach(auto renderItem, _modelMeshRenderItemsSet) { foreach(auto renderItem, _modelMeshRenderItemsSet) {
auto meshPart = renderItem.get(); auto meshPart = renderItem.get();
bool allTexturesForThisMesh = meshPart->calculateMaterialSize();
allTexturesLoaded = allTexturesLoaded & allTexturesForThisMesh;
textureSize += meshPart->getMaterialTextureSize(); textureSize += meshPart->getMaterialTextureSize();
textureCount += meshPart->getMaterialTextureCount(); textureCount += meshPart->getMaterialTextureCount();
allTexturesLoaded = allTexturesLoaded & meshPart->hasTextureInfo();
} }
_renderInfoTextureSize = textureSize; _renderInfoTextureSize = textureSize;
_renderInfoTextureCount = textureCount; _renderInfoTextureCount = textureCount;

View file

@ -517,10 +517,6 @@ void ScriptEngine::init() {
// constants // constants
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
registerGlobalObject("Controller", scriptingInterface.data());
UserInputMapper::registerControllerTypes(this);
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>(); auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
registerGlobalObject("Recording", recordingInterface.data()); registerGlobalObject("Recording", recordingInterface.data());
@ -1021,9 +1017,12 @@ void ScriptEngine::updateMemoryCost(const qint64& deltaSize) {
} }
void ScriptEngine::timerFired() { void ScriptEngine::timerFired() {
if (DependencyManager::get<ScriptEngines>()->isStopped()) { {
qCDebug(scriptengine) << "Script.timerFired() while shutting down is ignored... parent script:" << getFilename(); auto engine = DependencyManager::get<ScriptEngines>();
return; // bail early if (!engine || engine->isStopped()) {
qCDebug(scriptengine) << "Script.timerFired() while shutting down is ignored... parent script:" << getFilename();
return; // bail early
}
} }
QTimer* callingTimer = reinterpret_cast<QTimer*>(sender()); QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());

View file

@ -166,7 +166,7 @@ public:
void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; } void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
bool isUserLoaded() const { return _isUserLoaded; } bool isUserLoaded() const { return _isUserLoaded; }
// NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety // NOTE - this is used by the TypedArray implementation. we need to review this for thread safety
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
void setEmitScriptUpdatesFunction(std::function<bool()> func) { _emitScriptUpdates = func; } void setEmitScriptUpdatesFunction(std::function<bool()> func) { _emitScriptUpdates = func; }

View file

@ -0,0 +1,172 @@
@font-face {
font-family: 'Raleway';
src: url('vendor/Raleway/Raleway-Regular.ttf');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Raleway';
src: url('vendor/Raleway/Raleway-ExtraLight.ttf');
font-weight: 200;
font-style: normal;
}
@font-face {
font-family: 'Raleway';
src: url('vendor/Raleway/Raleway-SemiBold.ttf');
font-weight: bold;
font-style: normal;
}
* {
font-family: "Raleway", "Open Sans", Arial, Helvetica, sans-serif;
line-height: 130%;
}
body {
margin: 0;
padding: 0;
color: #808785;
margin: 0 auto;
text-align: left;
font-size: 13.5pt;
-webkit-touch-callout: none; -webkit-user-select: none;
cursor: default;
overflow: hidden;
font-variant-numeric: lining-nums;
-moz-font-feature-settings: "lnum";
-webkit-font-feature-settings: "lnum";
font-feature-settings: "lnum";
}
.selectable {
-webkit-touch-callout: text;
-webkit-user-select: text;
cursor: text;
}
a:link,
a:visited,
a:hover,
a:active {
color: #B4B4B4;
}
a:hover {
color: #2D88A4;
}
.header {
width: 95%;
left: 2.5%
}
.colmask {
width: 95%;
left: 2.5%
}
.colmid { right: 25% }
.colin { right: 25% }
.colleft { right: 25% }
.col1 {
width: 23%;
left: 101%
}
.col2 {
width: 23%;
left: 53%
}
.col3 {
width: 23%;
left: 80%
}
.col4 {
width: 23%;
left: 82%
}
.footer {
width: 95%;
left: 2.5%
}
.header {
clear: both;
float: left;
position: relative;
border-bottom: #000 1px solid;
background-color: #b4d2f7
}
.colmask {
clear: both;
float: left;
overflow: hidden;
position: relative;
}
.colmid {
float: left;
width: 100%;
position: relative;
}
.colin {
float: left;
width: 100%;
position: relative;
}
.colleft {
float: left;
width: 100%;
position: relative;
}
.col1 {
padding: 0px 0px 1em 0px;
overflow: hidden;
float: left;
position: relative;
}
.col2 {
padding: 0px 0px 1em 0px;
overflow: hidden;
float: left;
position: relative;
}
.col3 {
padding: 0px 0px 1em 0px;
overflow: hidden;
float: left;
position: relative;
}
.col4 {
padding: 0px 0px 1em 0px;
overflow: hidden;
float: left;
position: relative;
}
.footer {
clear: both;
float: left;
position: relative;
padding-top: 8px;
border-top: #000 1px solid;
}
.bottom {
clear: both;
width: 100%;
float: left;
position: relative;
}
body {
border-width: 0px;
padding: 0px;
margin: 0px;
font-size: 90%;
width: 100%;
min-width: 600px;
}

View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<title>Server Backup</title>
<script src="content-update.js"></script>
<link rel="stylesheet" type="text/css" href="content-update.css"></link>
</head>
<body onload="ready()">
<div class="colmask">
<h3>We backed up your old Sandbox content, just in case.</h3>
<p><b>To restore it, follow these steps:</b>
<div class="colmid">
<div class="colin">
<div class="colleft">
<!-- I know the content of the columns are swapped -->
<!-- If you manage to fix the CSS, you get to swap it back! -->
<div class="col1">
<img src="images/step2.jpg" alt="Step 2">
<p><b>2.</b> Go to your backup directory:
<span id="directory"></span>
</div>
<div class="col2">
<img src="images/step1.jpg" alt="Step 1">
<p><b>1.</b> Stop your Sandbox server.
</div>
<div class="col3">
<img src="images/step3.jpg" alt="Step 3">
<p><b>3.</b> Copy the backed up content and paste it into the parent directory.
</div>
<div class="col4">
<img src="images/step4.jpg" alt="Step 4">
<p><b>4.</b> Restart your Sandbox server.
</div>
</div>
</div>
</div>
<div class="footer">
<p>For more information about managing your high Fidelity Sandbox Server, check out our docs: <a href="https://wiki.highfidelity.com/wiki/Sandbox" target="_blank">wiki.highfidelity.com/wiki/Sandbox</a>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,12 @@
function ready() {
console.log("Ready");
const electron = require('electron');
window.$ = require('./vendor/jquery/jquery-2.1.4.min.js');
electron.ipcRenderer.on('update', function(event, message) {
$('#directory').html(message);
});
electron.ipcRenderer.send('ready');
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View file

@ -408,6 +408,13 @@ var labels = {
logWindow.open(); logWindow.open();
} }
}, },
restoreBackup: {
label: 'Restore Backup Instructions',
click: function() {
var folder = getRootHifiDataDirectory() + "/Server Backup";
openBackupInstructions(folder);
}
},
share: { share: {
label: 'Share', label: 'Share',
click: function() { click: function() {
@ -443,6 +450,7 @@ function buildMenuArray(serverState) {
menuArray.push(labels.stopServer); menuArray.push(labels.stopServer);
menuArray.push(labels.settings); menuArray.push(labels.settings);
menuArray.push(labels.viewLogs); menuArray.push(labels.viewLogs);
menuArray.push(labels.restoreBackup);
menuArray.push(separator); menuArray.push(separator);
menuArray.push(labels.share); menuArray.push(labels.share);
menuArray.push(separator); menuArray.push(separator);
@ -488,27 +496,60 @@ function updateTrayMenu(serverState) {
const httpStatusPort = 60332; const httpStatusPort = 60332;
function deleteResourceDirectories() { function backupResourceDirectories(folder) {
const dsResourceDirectory = getDomainServerClientResourcesDirectory();
try { try {
fs.removeSync(dsResourceDirectory); fs.mkdirSync(folder);
console.log("Deleted directory " + dsResourceDirectory); console.log("Created directory " + folder);
} catch (e) {
console.log(e); var dsBackup = path.join(folder, '/domain-server');
} var acBackup = path.join(folder, '/assignment-client');
const acResourceDirectory = getAssignmentClientResourcesDirectory();
try { fs.copySync(getDomainServerClientResourcesDirectory(), dsBackup);
fs.removeSync(acResourceDirectory); fs.copySync(getAssignmentClientResourcesDirectory(), acBackup);
console.log("Deleted directory " + acResourceDirectory);
fs.removeSync(getDomainServerClientResourcesDirectory());
fs.removeSync(getAssignmentClientResourcesDirectory());
return true;
} catch (e) { } catch (e) {
console.log(e); console.log(e);
return false;
} }
} }
function deleteResourceDirectoriesAndRestart() { function openBackupInstructions(folder) {
// Explain user how to restore server
var window = new BrowserWindow({
icon: appIcon,
width: 800,
height: 520,
});
window.loadURL('file://' + __dirname + '/content-update.html');
if (!debug) {
window.setMenu(null);
}
window.show();
electron.ipcMain.on('ready', function() {
console.log("got ready");
window.webContents.send('update', folder);
});
}
function backupResourceDirectoriesAndRestart() {
homeServer.stop(); homeServer.stop();
deleteResourceDirectories();
maybeInstallDefaultContentSet(onContentLoaded); var folder = getRootHifiDataDirectory() + "/Server Backup - " + Date.now();
if (backupResourceDirectories(folder)) {
maybeInstallDefaultContentSet(onContentLoaded);
openBackupInstructions(folder);
} else {
dialog.showMessageBox({
type: 'warning',
buttons: ['Ok'],
title: 'Update Error',
message: 'There was an error updating the content, aborting.'
}, function() {});
}
} }
function checkNewContent() { function checkNewContent() {
@ -537,16 +578,7 @@ function checkNewContent() {
message: 'A newer version of the home content set is available.\nDo you wish to update?' message: 'A newer version of the home content set is available.\nDo you wish to update?'
}, function(idx) { }, function(idx) {
if (idx === 0) { if (idx === 0) {
dialog.showMessageBox({ backupResourceDirectoriesAndRestart();
type: 'question',
buttons: ['Yes', 'No'],
title: 'Are you sure?',
message: 'This action will delete your current sandbox content.\nDo you wish to continue?'
}, function(idx) {
if (idx === 0 && homeServer) {
deleteResourceDirectoriesAndRestart();
}
});
} else { } else {
// They don't want to update, mark content set as current // They don't want to update, mark content set as current
userConfig.set('homeContentLastModified', new Date()); userConfig.set('homeContentLastModified', new Date());