mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 06:44:07 +02:00
Merge branch 'master' into out-of-body-experience
This commit is contained in:
commit
aab974e6ae
41 changed files with 1020 additions and 362 deletions
|
@ -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>();
|
||||||
|
|
||||||
|
|
|
@ -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")
|
|
@ -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
|
4
cmake/externals/wasapi/CMakeLists.txt
vendored
4
cmake/externals/wasapi/CMakeLists.txt
vendored
|
@ -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 ""
|
||||||
|
|
|
@ -44,12 +44,25 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT)
|
||||||
|
|
||||||
set(QTAUDIO_PATH $<TARGET_FILE_DIR:${TARGET_NAME}>/audio)
|
set(QTAUDIO_PATH $<TARGET_FILE_DIR:${TARGET_NAME}>/audio)
|
||||||
|
|
||||||
# if present, replace qtaudio_windows.dll with qtaudio_wasapi.dll
|
if (DEPLOY_PACKAGE)
|
||||||
add_custom_command(
|
# copy qtaudio_wasapi.dll alongside qtaudio_windows.dll, and let the installer resolve
|
||||||
TARGET ${TARGET_NAME}
|
add_custom_command(
|
||||||
POST_BUILD
|
TARGET ${TARGET_NAME}
|
||||||
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} )
|
POST_BUILD
|
||||||
)
|
COMMAND if exist ${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 copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.dll ${QTAUDIO_PATH} && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.pdb ${QTAUDIO_PATH} )
|
||||||
|
)
|
||||||
|
elseif (${CMAKE_SYSTEM_VERSION} VERSION_LESS 6.2)
|
||||||
|
# continue using qtaudio_windows.dll on Windows 7
|
||||||
|
else ()
|
||||||
|
# replace qtaudio_windows.dll with qtaudio_wasapi.dll on Windows 8/8.1/10
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${TARGET_NAME}
|
||||||
|
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_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 ()
|
endif ()
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
|
@ -23,6 +23,11 @@
|
||||||
;Default installation folder
|
;Default installation folder
|
||||||
InstallDir "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
|
InstallDir "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
|
||||||
|
|
||||||
|
;--------------------------------
|
||||||
|
;Include WinVer to get Windows version
|
||||||
|
|
||||||
|
!include "WinVer.nsh"
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
;General
|
;General
|
||||||
; leverage the UAC NSIS plugin to promote uninstaller to elevated privileges
|
; leverage the UAC NSIS plugin to promote uninstaller to elevated privileges
|
||||||
|
@ -600,8 +605,16 @@ Section "-Core installation"
|
||||||
Delete "$INSTDIR\version"
|
Delete "$INSTDIR\version"
|
||||||
Delete "$INSTDIR\xinput1_3.dll"
|
Delete "$INSTDIR\xinput1_3.dll"
|
||||||
|
|
||||||
;Delete old Qt files
|
; The installer includes two different Qt audio plugins.
|
||||||
Delete "$INSTDIR\audio\qtaudio_windows.dll"
|
; On Windows 8 and above, only qtaudio_wasapi.dll should be installed.
|
||||||
|
; On Windows 7 and below, only qtaudio_windows.dll should be installed.
|
||||||
|
${If} ${AtLeastWin8}
|
||||||
|
Delete "$INSTDIR\audio\qtaudio_windows.dll"
|
||||||
|
Delete "$INSTDIR\audio\qtaudio_windows.pdb"
|
||||||
|
${Else}
|
||||||
|
Delete "$INSTDIR\audio\qtaudio_wasapi.dll"
|
||||||
|
Delete "$INSTDIR\audio\qtaudio_wasapi.pdb"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
; Delete old desktop shortcuts before they were renamed during Sandbox rename
|
; Delete old desktop shortcuts before they were renamed during Sandbox rename
|
||||||
Delete "$DESKTOP\@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@.lnk"
|
Delete "$DESKTOP\@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@.lnk"
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
@ -814,7 +815,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
{ "gl_version", glContextData["version"] },
|
{ "gl_version", glContextData["version"] },
|
||||||
{ "gl_vender", glContextData["vendor"] },
|
{ "gl_vender", glContextData["vendor"] },
|
||||||
{ "gl_sl_version", glContextData["slVersion"] },
|
{ "gl_sl_version", glContextData["slVersion"] },
|
||||||
{ "gl_renderer", glContextData["renderer"] }
|
{ "gl_renderer", glContextData["renderer"] },
|
||||||
|
{ "ideal_thread_count", QThread::idealThreadCount() }
|
||||||
};
|
};
|
||||||
auto macVersion = QSysInfo::macVersion();
|
auto macVersion = QSysInfo::macVersion();
|
||||||
if (macVersion != QSysInfo::MV_None) {
|
if (macVersion != QSysInfo::MV_None) {
|
||||||
|
@ -824,6 +826,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
if (windowsVersion != QSysInfo::WV_None) {
|
if (windowsVersion != QSysInfo::WV_None) {
|
||||||
properties["os_win_version"] = QSysInfo::windowsVersion();
|
properties["os_win_version"] = QSysInfo::windowsVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProcessorInfo procInfo;
|
||||||
|
if (getProcessorInfo(procInfo)) {
|
||||||
|
properties["processor_core_count"] = procInfo.numProcessorCores;
|
||||||
|
properties["logical_processor_count"] = procInfo.numLogicalProcessors;
|
||||||
|
properties["processor_l1_cache_count"] = procInfo.numProcessorCachesL1;
|
||||||
|
properties["processor_l2_cache_count"] = procInfo.numProcessorCachesL2;
|
||||||
|
properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3;
|
||||||
|
}
|
||||||
|
|
||||||
UserActivityLogger::getInstance().logAction("launch", properties);
|
UserActivityLogger::getInstance().logAction("launch", properties);
|
||||||
|
|
||||||
_connectionMonitor.init();
|
_connectionMonitor.init();
|
||||||
|
@ -1122,6 +1134,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||||
|
|
||||||
properties["fps"] = _frameCounter.rate();
|
properties["fps"] = _frameCounter.rate();
|
||||||
|
properties["target_frame_rate"] = getTargetFrameRate();
|
||||||
properties["present_rate"] = displayPlugin->presentRate();
|
properties["present_rate"] = displayPlugin->presentRate();
|
||||||
properties["new_frame_present_rate"] = displayPlugin->newFramePresentRate();
|
properties["new_frame_present_rate"] = displayPlugin->newFramePresentRate();
|
||||||
properties["dropped_frame_rate"] = displayPlugin->droppedFrameRate();
|
properties["dropped_frame_rate"] = displayPlugin->droppedFrameRate();
|
||||||
|
@ -1163,6 +1176,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
properties["deleted_entity_cnt"] = entityActivityTracking.deletedEntityCount;
|
properties["deleted_entity_cnt"] = entityActivityTracking.deletedEntityCount;
|
||||||
properties["edited_entity_cnt"] = entityActivityTracking.editedEntityCount;
|
properties["edited_entity_cnt"] = entityActivityTracking.editedEntityCount;
|
||||||
|
|
||||||
|
properties["active_display_plugin"] = getActiveDisplayPlugin()->getName();
|
||||||
|
properties["using_hmd"] = isHMDMode();
|
||||||
|
|
||||||
auto hmdHeadPose = getHMDSensorPose();
|
auto hmdHeadPose = getHMDSensorPose();
|
||||||
properties["hmd_head_pose_changed"] = isHMDMode() && (hmdHeadPose != lastHMDHeadPose);
|
properties["hmd_head_pose_changed"] = isHMDMode() && (hmdHeadPose != lastHMDHeadPose);
|
||||||
lastHMDHeadPose = hmdHeadPose;
|
lastHMDHeadPose = hmdHeadPose;
|
||||||
|
@ -2169,7 +2185,7 @@ bool Application::event(QEvent* event) {
|
||||||
// handle custom URL
|
// handle custom URL
|
||||||
if (event->type() == QEvent::FileOpen) {
|
if (event->type() == QEvent::FileOpen) {
|
||||||
|
|
||||||
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
|
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
|
||||||
|
|
||||||
QUrl url = fileEvent->url();
|
QUrl url = fileEvent->url();
|
||||||
|
|
||||||
|
@ -4353,8 +4369,13 @@ namespace render {
|
||||||
auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
|
auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
|
||||||
auto sceneKeyLight = scene->getKeyLight();
|
auto sceneKeyLight = scene->getKeyLight();
|
||||||
auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture();
|
auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture();
|
||||||
sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance());
|
if (defaultSkyboxAmbientTexture) {
|
||||||
sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
|
sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance());
|
||||||
|
sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
|
||||||
|
} else {
|
||||||
|
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(
|
||||||
|
"Failed to get a valid Default Skybox Ambient Texture ? probably because it couldn't be find during initialization step");
|
||||||
|
}
|
||||||
// fall through: render defaults skybox
|
// fall through: render defaults skybox
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -4911,6 +4932,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 {
|
||||||
|
|
|
@ -25,14 +25,25 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit
|
||||||
{
|
{
|
||||||
_type = ACTION_TYPE_HOLD;
|
_type = ACTION_TYPE_HOLD;
|
||||||
_measuredLinearVelocities.resize(AvatarActionHold::velocitySmoothFrames);
|
_measuredLinearVelocities.resize(AvatarActionHold::velocitySmoothFrames);
|
||||||
|
|
||||||
|
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
|
if (myAvatar) {
|
||||||
|
myAvatar->addHoldAction(this);
|
||||||
|
}
|
||||||
|
|
||||||
#if WANT_DEBUG
|
#if WANT_DEBUG
|
||||||
qDebug() << "AvatarActionHold::AvatarActionHold";
|
qDebug() << "AvatarActionHold::AvatarActionHold" << (void*)this;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarActionHold::~AvatarActionHold() {
|
AvatarActionHold::~AvatarActionHold() {
|
||||||
|
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
|
if (myAvatar) {
|
||||||
|
myAvatar->removeHoldAction(this);
|
||||||
|
}
|
||||||
|
|
||||||
#if WANT_DEBUG
|
#if WANT_DEBUG
|
||||||
qDebug() << "AvatarActionHold::~AvatarActionHold";
|
qDebug() << "AvatarActionHold::~AvatarActionHold" << (void*)this;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,3 +471,40 @@ void AvatarActionHold::deserialize(QByteArray serializedArguments) {
|
||||||
|
|
||||||
forceBodyNonStatic();
|
forceBodyNonStatic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvatarActionHold::lateAvatarUpdate(const AnimPose& prePhysicsRoomPose, const AnimPose& postAvatarUpdateRoomPose) {
|
||||||
|
auto ownerEntity = _ownerEntity.lock();
|
||||||
|
if (!ownerEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void* physicsInfo = ownerEntity->getPhysicsInfo();
|
||||||
|
if (!physicsInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||||
|
btRigidBody* rigidBody = motionState ? motionState->getRigidBody() : nullptr;
|
||||||
|
if (!rigidBody) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||||
|
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
|
||||||
|
if (!holdingAvatar || !holdingAvatar->isMyAvatar()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
btTransform worldTrans = rigidBody->getWorldTransform();
|
||||||
|
AnimPose worldBodyPose(glm::vec3(1), bulletToGLM(worldTrans.getRotation()), bulletToGLM(worldTrans.getOrigin()));
|
||||||
|
|
||||||
|
// transform the body transform into sensor space with the prePhysics sensor-to-world matrix.
|
||||||
|
// then transform it back into world uisng the postAvatarUpdate sensor-to-world matrix.
|
||||||
|
AnimPose newWorldBodyPose = postAvatarUpdateRoomPose * prePhysicsRoomPose.inverse() * worldBodyPose;
|
||||||
|
|
||||||
|
worldTrans.setOrigin(glmToBullet(newWorldBodyPose.trans));
|
||||||
|
worldTrans.setRotation(glmToBullet(newWorldBodyPose.rot));
|
||||||
|
rigidBody->setWorldTransform(worldTrans);
|
||||||
|
|
||||||
|
bool positionSuccess;
|
||||||
|
ownerEntity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false);
|
||||||
|
bool orientationSuccess;
|
||||||
|
ownerEntity->setOrientation(bulletToGLM(worldTrans.getRotation()), orientationSuccess, false);
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
#include <EntityItem.h>
|
#include <EntityItem.h>
|
||||||
|
#include <AnimPose.h>
|
||||||
#include <ObjectActionSpring.h>
|
#include <ObjectActionSpring.h>
|
||||||
|
|
||||||
#include "avatar/MyAvatar.h"
|
#include "avatar/MyAvatar.h"
|
||||||
|
@ -41,6 +42,8 @@ public:
|
||||||
|
|
||||||
virtual void prepareForPhysicsSimulation() override;
|
virtual void prepareForPhysicsSimulation() override;
|
||||||
|
|
||||||
|
void lateAvatarUpdate(const AnimPose& prePhysicsRoomPose, const AnimPose& postAvatarUpdateRoomPose);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doKinematicUpdate(float deltaTimeStep);
|
void doKinematicUpdate(float deltaTimeStep);
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "devices/Faceshift.h"
|
#include "devices/Faceshift.h"
|
||||||
#include "AvatarManager.h"
|
#include "AvatarManager.h"
|
||||||
|
#include "AvatarActionHold.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "MyAvatar.h"
|
#include "MyAvatar.h"
|
||||||
#include "Physics.h"
|
#include "Physics.h"
|
||||||
|
@ -1376,6 +1377,8 @@ void MyAvatar::prepareForPhysicsSimulation(float deltaTime) {
|
||||||
_follow.deactivate();
|
_follow.deactivate();
|
||||||
getCharacterController()->disableFollow();
|
getCharacterController()->disableFollow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_prePhysicsRoomPose = AnimPose(_sensorToWorldMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
|
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
|
||||||
|
@ -1650,8 +1653,10 @@ void MyAvatar::postUpdate(float deltaTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
AnimPose postUpdateRoomPose(_sensorToWorldMatrix);
|
||||||
|
updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose);
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
|
void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
|
||||||
|
|
||||||
|
@ -2393,3 +2398,35 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// thread-safe
|
||||||
|
void MyAvatar::addHoldAction(AvatarActionHold* holdAction) {
|
||||||
|
std::lock_guard<std::mutex> guard(_holdActionsMutex);
|
||||||
|
_holdActions.push_back(holdAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// thread-safe
|
||||||
|
void MyAvatar::removeHoldAction(AvatarActionHold* holdAction) {
|
||||||
|
std::lock_guard<std::mutex> guard(_holdActionsMutex);
|
||||||
|
auto iter = std::find(std::begin(_holdActions), std::end(_holdActions), holdAction);
|
||||||
|
if (iter != std::end(_holdActions)) {
|
||||||
|
_holdActions.erase(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose& postUpdatePose) {
|
||||||
|
EntityTreeRenderer* entityTreeRenderer = qApp->getEntities();
|
||||||
|
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
|
||||||
|
if (entityTree) {
|
||||||
|
// to prevent actions from adding or removing themselves from the _holdActions vector
|
||||||
|
// while we are iterating, we need to enter a critical section.
|
||||||
|
std::lock_guard<std::mutex> guard(_holdActionsMutex);
|
||||||
|
|
||||||
|
// lateAvatarUpdate will modify entity position & orientation, so we need an entity write lock
|
||||||
|
entityTree->withWriteLock([&] {
|
||||||
|
for (auto& holdAction : _holdActions) {
|
||||||
|
holdAction->lateAvatarUpdate(prePhysicsPose, postUpdatePose);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "MyCharacterController.h"
|
#include "MyCharacterController.h"
|
||||||
#include <ThreadSafeValueCache.h>
|
#include <ThreadSafeValueCache.h>
|
||||||
|
|
||||||
|
class AvatarActionHold;
|
||||||
class ModelItemID;
|
class ModelItemID;
|
||||||
|
|
||||||
enum DriveKeys {
|
enum DriveKeys {
|
||||||
|
@ -289,6 +290,10 @@ public:
|
||||||
|
|
||||||
glm::vec3 getPreActionVelocity() const;
|
glm::vec3 getPreActionVelocity() const;
|
||||||
|
|
||||||
|
void addHoldAction(AvatarActionHold* holdAction); // thread-safe
|
||||||
|
void removeHoldAction(AvatarActionHold* holdAction); // thread-safe
|
||||||
|
void updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose& postUpdatePose);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void increaseSize();
|
void increaseSize();
|
||||||
void decreaseSize();
|
void decreaseSize();
|
||||||
|
@ -511,6 +516,10 @@ private:
|
||||||
bool _moveKinematically { false }; // KINEMATIC_CONTROLLER_HACK
|
bool _moveKinematically { false }; // KINEMATIC_CONTROLLER_HACK
|
||||||
float _canonicalScale { 1.0f };
|
float _canonicalScale { 1.0f };
|
||||||
|
|
||||||
|
AnimPose _prePhysicsRoomPose;
|
||||||
|
std::mutex _holdActionsMutex;
|
||||||
|
std::vector<AvatarActionHold*> _holdActions;
|
||||||
|
|
||||||
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
|
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
|
||||||
float AUDIO_ENERGY_CONSTANT { 0.000001f };
|
float AUDIO_ENERGY_CONSTANT { 0.000001f };
|
||||||
float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f };
|
float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f };
|
||||||
|
|
|
@ -817,6 +817,13 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: we assume the inputFormat and the outputFormat are the same, since on any modern
|
||||||
|
// multimedia OS they should be. If there is a device that this is not true for, we can
|
||||||
|
// add back support to do resampling.
|
||||||
|
if (_inputFormat.sampleRate() != _outputFormat.sampleRate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if this person wants local loopback add that to the locally injected audio
|
// if this person wants local loopback add that to the locally injected audio
|
||||||
// if there is reverb apply it to local audio and substract the origin samples
|
// if there is reverb apply it to local audio and substract the origin samples
|
||||||
|
|
||||||
|
@ -833,11 +840,6 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: we assume the inputFormat and the outputFormat are the same, since on any modern
|
|
||||||
// multimedia OS they should be. If there is a device that this is not true for, we can
|
|
||||||
// add back support to do resampling.
|
|
||||||
Q_ASSERT(_inputFormat.sampleRate() == _outputFormat.sampleRate());
|
|
||||||
|
|
||||||
static QByteArray loopBackByteArray;
|
static QByteArray loopBackByteArray;
|
||||||
|
|
||||||
int numInputSamples = inputByteArray.size() / AudioConstants::SAMPLE_SIZE;
|
int numInputSamples = inputByteArray.size() / AudioConstants::SAMPLE_SIZE;
|
||||||
|
@ -1362,7 +1364,7 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) {
|
||||||
// proportional to the accelerator ratio.
|
// proportional to the accelerator ratio.
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 1.0f;
|
const float AudioClient::CALLBACK_ACCELERATOR_RATIO = IsWindows8OrGreater() ? 1.0f : 0.25f;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -58,9 +58,13 @@ void WebEntityAPIHelper::emitWebEvent(const QVariant& message) {
|
||||||
} else {
|
} else {
|
||||||
// special case to handle raising and lowering the virtual keyboard
|
// special case to handle raising and lowering the virtual keyboard
|
||||||
if (message.type() == QVariant::String && message.toString() == "_RAISE_KEYBOARD" && _renderableWebEntityItem) {
|
if (message.type() == QVariant::String && message.toString() == "_RAISE_KEYBOARD" && _renderableWebEntityItem) {
|
||||||
_renderableWebEntityItem->setKeyboardRaised(true);
|
if (_renderableWebEntityItem) {
|
||||||
|
_renderableWebEntityItem->setKeyboardRaised(true);
|
||||||
|
}
|
||||||
} else if (message.type() == QVariant::String && message.toString() == "_LOWER_KEYBOARD" && _renderableWebEntityItem) {
|
} else if (message.type() == QVariant::String && message.toString() == "_LOWER_KEYBOARD" && _renderableWebEntityItem) {
|
||||||
_renderableWebEntityItem->setKeyboardRaised(false);
|
if (_renderableWebEntityItem) {
|
||||||
|
_renderableWebEntityItem->setKeyboardRaised(false);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
emit webEventReceived(message);
|
emit webEventReceived(message);
|
||||||
}
|
}
|
||||||
|
@ -343,7 +347,7 @@ void RenderableWebEntityItem::destroyWebSurface() {
|
||||||
|
|
||||||
// The lifetime of the QML surface MUST be managed by the main thread
|
// The lifetime of the QML surface MUST be managed by the main thread
|
||||||
// Additionally, we MUST use local variables copied by value, rather than
|
// Additionally, we MUST use local variables copied by value, rather than
|
||||||
// member variables, since they would implicitly refer to a this that
|
// member variables, since they would implicitly refer to a this that
|
||||||
// is no longer valid
|
// is no longer valid
|
||||||
auto webSurface = _webSurface;
|
auto webSurface = _webSurface;
|
||||||
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
|
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
|
||||||
|
@ -388,35 +392,40 @@ static bool equals(const QByteArray& byteArray, const uint8_t* ptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableWebEntityItem::synthesizeKeyPress(QString key) {
|
void RenderableWebEntityItem::synthesizeKeyPress(QString key) {
|
||||||
auto utf8Key = key.toUtf8();
|
auto eventHandler = getEventHandler();
|
||||||
|
if (eventHandler) {
|
||||||
|
auto utf8Key = key.toUtf8();
|
||||||
|
|
||||||
int scanCode = (int)utf8Key[0];
|
int scanCode = (int)utf8Key[0];
|
||||||
QString keyString = key;
|
QString keyString = key;
|
||||||
if (equals(utf8Key, UPWARDS_WHITE_ARROW_FROM_BAR) || equals(utf8Key, ASTERISIM) ||
|
if (equals(utf8Key, UPWARDS_WHITE_ARROW_FROM_BAR) || equals(utf8Key, ASTERISIM) ||
|
||||||
equals(utf8Key, (uint8_t*)PUNCTUATION_STRING) || equals(utf8Key, (uint8_t*)ALPHABET_STRING)) {
|
equals(utf8Key, (uint8_t*)PUNCTUATION_STRING) || equals(utf8Key, (uint8_t*)ALPHABET_STRING)) {
|
||||||
return; // ignore
|
return; // ignore
|
||||||
} else if (equals(utf8Key, LEFT_ARROW)) {
|
} else if (equals(utf8Key, LEFT_ARROW)) {
|
||||||
scanCode = Qt::Key_Backspace;
|
scanCode = Qt::Key_Backspace;
|
||||||
keyString = "\x08";
|
keyString = "\x08";
|
||||||
} else if (equals(utf8Key, RETURN_SYMBOL)) {
|
} else if (equals(utf8Key, RETURN_SYMBOL)) {
|
||||||
scanCode = Qt::Key_Return;
|
scanCode = Qt::Key_Return;
|
||||||
keyString = "\x0d";
|
keyString = "\x0d";
|
||||||
} else if (equals(utf8Key, LEFTWARD_WHITE_ARROW)) {
|
} else if (equals(utf8Key, LEFTWARD_WHITE_ARROW)) {
|
||||||
scanCode = Qt::Key_Left;
|
scanCode = Qt::Key_Left;
|
||||||
keyString = "";
|
keyString = "";
|
||||||
} else if (equals(utf8Key, RIGHTWARD_WHITE_ARROW)) {
|
} else if (equals(utf8Key, RIGHTWARD_WHITE_ARROW)) {
|
||||||
scanCode = Qt::Key_Right;
|
scanCode = Qt::Key_Right;
|
||||||
keyString = "";
|
keyString = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString);
|
||||||
|
QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString);
|
||||||
|
QCoreApplication::postEvent(eventHandler, pressEvent);
|
||||||
|
QCoreApplication::postEvent(eventHandler, releaseEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString);
|
|
||||||
QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString);
|
|
||||||
QCoreApplication::postEvent(getEventHandler(), pressEvent);
|
|
||||||
QCoreApplication::postEvent(getEventHandler(), releaseEvent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableWebEntityItem::emitScriptEvent(const QVariant& message) {
|
void RenderableWebEntityItem::emitScriptEvent(const QVariant& message) {
|
||||||
_webEntityAPIHelper->emitScriptEvent(message);
|
if (_webEntityAPIHelper) {
|
||||||
|
_webEntityAPIHelper->emitScriptEvent(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableWebEntityItem::setKeyboardRaised(bool raised) {
|
void RenderableWebEntityItem::setKeyboardRaised(bool raised) {
|
||||||
|
@ -424,5 +433,10 @@ void RenderableWebEntityItem::setKeyboardRaised(bool raised) {
|
||||||
// raise the keyboard only while in HMD mode and it's being requested.
|
// raise the keyboard only while in HMD mode and it's being requested.
|
||||||
bool value = AbstractViewStateInterface::instance()->isHMDMode() && raised;
|
bool value = AbstractViewStateInterface::instance()->isHMDMode() && raised;
|
||||||
|
|
||||||
_webSurface->getRootItem()->setProperty("keyboardRaised", QVariant(value));
|
if (_webSurface) {
|
||||||
|
auto rootItem = _webSurface->getRootItem();
|
||||||
|
if (rootItem) {
|
||||||
|
rootItem->setProperty("keyboardRaised", QVariant(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -511,9 +511,7 @@ void GL45Texture::stripToMip(uint16_t newMinMip) {
|
||||||
_minMip = newMinMip;
|
_minMip = newMinMip;
|
||||||
// Re-sync the sampler to force access to the new mip level
|
// Re-sync the sampler to force access to the new mip level
|
||||||
syncSampler();
|
syncSampler();
|
||||||
size_t oldSize = _size;
|
|
||||||
updateSize();
|
updateSize();
|
||||||
Q_ASSERT(_size > oldSize);
|
|
||||||
|
|
||||||
|
|
||||||
// Re-insert into the texture-by-mips map if appropriate
|
// Re-insert into the texture-by-mips map if appropriate
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -45,6 +45,20 @@ Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::uniq
|
||||||
// set the initial RTT and flow window size on congestion control object
|
// set the initial RTT and flow window size on congestion control object
|
||||||
_congestionControl->setRTT(_rtt);
|
_congestionControl->setRTT(_rtt);
|
||||||
_congestionControl->setMaxCongestionWindowSize(_flowWindowSize);
|
_congestionControl->setMaxCongestionWindowSize(_flowWindowSize);
|
||||||
|
|
||||||
|
// Setup packets
|
||||||
|
static const int ACK_PACKET_PAYLOAD_BYTES = sizeof(_lastSentACK) + sizeof(_currentACKSubSequenceNumber)
|
||||||
|
+ sizeof(_rtt) + sizeof(int32_t) + sizeof(int32_t) + sizeof(int32_t);
|
||||||
|
static const int LIGHT_ACK_PACKET_PAYLOAD_BYTES = sizeof(SequenceNumber);
|
||||||
|
static const int ACK2_PAYLOAD_BYTES = sizeof(SequenceNumber);
|
||||||
|
static const int NAK_PACKET_PAYLOAD_BYTES = 2 * sizeof(SequenceNumber);
|
||||||
|
static const int HANDSHAKE_ACK_PAYLOAD_BYTES = sizeof(SequenceNumber);
|
||||||
|
|
||||||
|
_ackPacket = ControlPacket::create(ControlPacket::ACK, ACK_PACKET_PAYLOAD_BYTES);
|
||||||
|
_lightACKPacket = ControlPacket::create(ControlPacket::LightACK, LIGHT_ACK_PACKET_PAYLOAD_BYTES);
|
||||||
|
_ack2Packet = ControlPacket::create(ControlPacket::ACK2, ACK2_PAYLOAD_BYTES);
|
||||||
|
_lossReport = ControlPacket::create(ControlPacket::NAK, NAK_PACKET_PAYLOAD_BYTES);
|
||||||
|
_handshakeACK = ControlPacket::create(ControlPacket::HandshakeACK, HANDSHAKE_ACK_PAYLOAD_BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection::~Connection() {
|
Connection::~Connection() {
|
||||||
|
@ -279,25 +293,22 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) {
|
||||||
|
|
||||||
// update the last sent ACK
|
// update the last sent ACK
|
||||||
_lastSentACK = nextACKNumber;
|
_lastSentACK = nextACKNumber;
|
||||||
|
|
||||||
// setup the ACK packet, make it static so we can re-use it
|
|
||||||
static const int ACK_PACKET_PAYLOAD_BYTES = sizeof(_lastSentACK) + sizeof(_currentACKSubSequenceNumber)
|
_ackPacket->reset(); // We need to reset it every time.
|
||||||
+ sizeof(_rtt) + sizeof(int32_t) + sizeof(int32_t) + sizeof(int32_t);
|
|
||||||
static auto ackPacket = ControlPacket::create(ControlPacket::ACK, ACK_PACKET_PAYLOAD_BYTES);
|
|
||||||
ackPacket->reset(); // We need to reset it every time.
|
|
||||||
|
|
||||||
// pack in the ACK sub-sequence number
|
// pack in the ACK sub-sequence number
|
||||||
ackPacket->writePrimitive(++_currentACKSubSequenceNumber);
|
_ackPacket->writePrimitive(++_currentACKSubSequenceNumber);
|
||||||
|
|
||||||
// pack in the ACK number
|
// pack in the ACK number
|
||||||
ackPacket->writePrimitive(nextACKNumber);
|
_ackPacket->writePrimitive(nextACKNumber);
|
||||||
|
|
||||||
// pack in the RTT and variance
|
// pack in the RTT and variance
|
||||||
ackPacket->writePrimitive(_rtt);
|
_ackPacket->writePrimitive(_rtt);
|
||||||
|
|
||||||
// pack the available buffer size, in packets
|
// pack the available buffer size, in packets
|
||||||
// in our implementation we have no hard limit on receive buffer size, send the default value
|
// in our implementation we have no hard limit on receive buffer size, send the default value
|
||||||
ackPacket->writePrimitive((int32_t) udt::CONNECTION_RECEIVE_BUFFER_SIZE_PACKETS);
|
_ackPacket->writePrimitive((int32_t) udt::CONNECTION_RECEIVE_BUFFER_SIZE_PACKETS);
|
||||||
|
|
||||||
if (wasCausedBySyncTimeout) {
|
if (wasCausedBySyncTimeout) {
|
||||||
// grab the up to date packet receive speed and estimated bandwidth
|
// grab the up to date packet receive speed and estimated bandwidth
|
||||||
|
@ -309,15 +320,15 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) {
|
||||||
_stats.recordEstimatedBandwidth(estimatedBandwidth);
|
_stats.recordEstimatedBandwidth(estimatedBandwidth);
|
||||||
|
|
||||||
// pack in the receive speed and estimatedBandwidth
|
// pack in the receive speed and estimatedBandwidth
|
||||||
ackPacket->writePrimitive(packetReceiveSpeed);
|
_ackPacket->writePrimitive(packetReceiveSpeed);
|
||||||
ackPacket->writePrimitive(estimatedBandwidth);
|
_ackPacket->writePrimitive(estimatedBandwidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
// record this as the last ACK send time
|
// record this as the last ACK send time
|
||||||
lastACKSendTime = p_high_resolution_clock::now();
|
lastACKSendTime = p_high_resolution_clock::now();
|
||||||
|
|
||||||
// have the socket send off our packet
|
// have the socket send off our packet
|
||||||
_parentSocket->writeBasePacket(*ackPacket, _destination);
|
_parentSocket->writeBasePacket(*_ackPacket, _destination);
|
||||||
|
|
||||||
Q_ASSERT_X(_sentACKs.empty() || _sentACKs.back().first + 1 == _currentACKSubSequenceNumber,
|
Q_ASSERT_X(_sentACKs.empty() || _sentACKs.back().first + 1 == _currentACKSubSequenceNumber,
|
||||||
"Connection::sendACK", "Adding an invalid ACK to _sentACKs");
|
"Connection::sendACK", "Adding an invalid ACK to _sentACKs");
|
||||||
|
@ -339,35 +350,27 @@ void Connection::sendLightACK() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the light ACK packet, make it static so we can re-use it
|
|
||||||
static const int LIGHT_ACK_PACKET_PAYLOAD_BYTES = sizeof(SequenceNumber);
|
|
||||||
static auto lightACKPacket = ControlPacket::create(ControlPacket::LightACK, LIGHT_ACK_PACKET_PAYLOAD_BYTES);
|
|
||||||
|
|
||||||
// reset the lightACKPacket before we go to write the ACK to it
|
// reset the lightACKPacket before we go to write the ACK to it
|
||||||
lightACKPacket->reset();
|
_lightACKPacket->reset();
|
||||||
|
|
||||||
// pack in the ACK
|
// pack in the ACK
|
||||||
lightACKPacket->writePrimitive(nextACKNumber);
|
_lightACKPacket->writePrimitive(nextACKNumber);
|
||||||
|
|
||||||
// have the socket send off our packet immediately
|
// have the socket send off our packet immediately
|
||||||
_parentSocket->writeBasePacket(*lightACKPacket, _destination);
|
_parentSocket->writeBasePacket(*_lightACKPacket, _destination);
|
||||||
|
|
||||||
_stats.record(ConnectionStats::Stats::SentLightACK);
|
_stats.record(ConnectionStats::Stats::SentLightACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::sendACK2(SequenceNumber currentACKSubSequenceNumber) {
|
void Connection::sendACK2(SequenceNumber currentACKSubSequenceNumber) {
|
||||||
// setup a static ACK2 packet we will re-use
|
|
||||||
static const int ACK2_PAYLOAD_BYTES = sizeof(SequenceNumber);
|
|
||||||
static auto ack2Packet = ControlPacket::create(ControlPacket::ACK2, ACK2_PAYLOAD_BYTES);
|
|
||||||
|
|
||||||
// reset the ACK2 Packet before writing the sub-sequence number to it
|
// reset the ACK2 Packet before writing the sub-sequence number to it
|
||||||
ack2Packet->reset();
|
_ack2Packet->reset();
|
||||||
|
|
||||||
// write the sub sequence number for this ACK2
|
// write the sub sequence number for this ACK2
|
||||||
ack2Packet->writePrimitive(currentACKSubSequenceNumber);
|
_ack2Packet->writePrimitive(currentACKSubSequenceNumber);
|
||||||
|
|
||||||
// send the ACK2 packet
|
// send the ACK2 packet
|
||||||
_parentSocket->writeBasePacket(*ack2Packet, _destination);
|
_parentSocket->writeBasePacket(*_ack2Packet, _destination);
|
||||||
|
|
||||||
// update the last sent ACK2 and the last ACK2 send time
|
// update the last sent ACK2 and the last ACK2 send time
|
||||||
_lastSentACK2 = currentACKSubSequenceNumber;
|
_lastSentACK2 = currentACKSubSequenceNumber;
|
||||||
|
@ -376,19 +379,16 @@ void Connection::sendACK2(SequenceNumber currentACKSubSequenceNumber) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::sendNAK(SequenceNumber sequenceNumberRecieved) {
|
void Connection::sendNAK(SequenceNumber sequenceNumberRecieved) {
|
||||||
// create the loss report packet, make it static so we can re-use it
|
_lossReport->reset(); // We need to reset it every time.
|
||||||
static const int NAK_PACKET_PAYLOAD_BYTES = 2 * sizeof(SequenceNumber);
|
|
||||||
static auto lossReport = ControlPacket::create(ControlPacket::NAK, NAK_PACKET_PAYLOAD_BYTES);
|
|
||||||
lossReport->reset(); // We need to reset it every time.
|
|
||||||
|
|
||||||
// pack in the loss report
|
// pack in the loss report
|
||||||
lossReport->writePrimitive(_lastReceivedSequenceNumber + 1);
|
_lossReport->writePrimitive(_lastReceivedSequenceNumber + 1);
|
||||||
if (_lastReceivedSequenceNumber + 1 != sequenceNumberRecieved - 1) {
|
if (_lastReceivedSequenceNumber + 1 != sequenceNumberRecieved - 1) {
|
||||||
lossReport->writePrimitive(sequenceNumberRecieved - 1);
|
_lossReport->writePrimitive(sequenceNumberRecieved - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// have the parent socket send off our packet immediately
|
// have the parent socket send off our packet immediately
|
||||||
_parentSocket->writeBasePacket(*lossReport, _destination);
|
_parentSocket->writeBasePacket(*_lossReport, _destination);
|
||||||
|
|
||||||
// record our last NAK time
|
// record our last NAK time
|
||||||
_lastNAKTime = p_high_resolution_clock::now();
|
_lastNAKTime = p_high_resolution_clock::now();
|
||||||
|
@ -519,7 +519,7 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in
|
||||||
return !wasDuplicate;
|
return !wasDuplicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::processControl(std::unique_ptr<ControlPacket> controlPacket) {
|
void Connection::processControl(ControlPacketPointer controlPacket) {
|
||||||
|
|
||||||
// Simple dispatch to control packets processing methods based on their type.
|
// Simple dispatch to control packets processing methods based on their type.
|
||||||
|
|
||||||
|
@ -577,7 +577,7 @@ void Connection::processControl(std::unique_ptr<ControlPacket> controlPacket) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::processACK(std::unique_ptr<ControlPacket> controlPacket) {
|
void Connection::processACK(ControlPacketPointer controlPacket) {
|
||||||
// read the ACK sub-sequence number
|
// read the ACK sub-sequence number
|
||||||
SequenceNumber currentACKSubSequenceNumber;
|
SequenceNumber currentACKSubSequenceNumber;
|
||||||
controlPacket->readPrimitive(¤tACKSubSequenceNumber);
|
controlPacket->readPrimitive(¤tACKSubSequenceNumber);
|
||||||
|
@ -678,7 +678,7 @@ void Connection::processACK(std::unique_ptr<ControlPacket> controlPacket) {
|
||||||
_stats.record(ConnectionStats::Stats::ProcessedACK);
|
_stats.record(ConnectionStats::Stats::ProcessedACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::processLightACK(std::unique_ptr<ControlPacket> controlPacket) {
|
void Connection::processLightACK(ControlPacketPointer controlPacket) {
|
||||||
// read the ACKed sequence number
|
// read the ACKed sequence number
|
||||||
SequenceNumber ack;
|
SequenceNumber ack;
|
||||||
controlPacket->readPrimitive(&ack);
|
controlPacket->readPrimitive(&ack);
|
||||||
|
@ -702,7 +702,7 @@ void Connection::processLightACK(std::unique_ptr<ControlPacket> controlPacket) {
|
||||||
_stats.record(ConnectionStats::Stats::ReceivedLightACK);
|
_stats.record(ConnectionStats::Stats::ReceivedLightACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::processACK2(std::unique_ptr<ControlPacket> controlPacket) {
|
void Connection::processACK2(ControlPacketPointer controlPacket) {
|
||||||
// pull the sub sequence number from the packet
|
// pull the sub sequence number from the packet
|
||||||
SequenceNumber subSequenceNumber;
|
SequenceNumber subSequenceNumber;
|
||||||
controlPacket->readPrimitive(&subSequenceNumber);
|
controlPacket->readPrimitive(&subSequenceNumber);
|
||||||
|
@ -742,7 +742,7 @@ void Connection::processACK2(std::unique_ptr<ControlPacket> controlPacket) {
|
||||||
_stats.record(ConnectionStats::Stats::ReceivedACK2);
|
_stats.record(ConnectionStats::Stats::ReceivedACK2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::processNAK(std::unique_ptr<ControlPacket> controlPacket) {
|
void Connection::processNAK(ControlPacketPointer controlPacket) {
|
||||||
// read the loss report
|
// read the loss report
|
||||||
SequenceNumber start, end;
|
SequenceNumber start, end;
|
||||||
controlPacket->readPrimitive(&start);
|
controlPacket->readPrimitive(&start);
|
||||||
|
@ -764,7 +764,7 @@ void Connection::processNAK(std::unique_ptr<ControlPacket> controlPacket) {
|
||||||
_stats.record(ConnectionStats::Stats::ReceivedNAK);
|
_stats.record(ConnectionStats::Stats::ReceivedNAK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::processHandshake(std::unique_ptr<ControlPacket> controlPacket) {
|
void Connection::processHandshake(ControlPacketPointer controlPacket) {
|
||||||
SequenceNumber initialSequenceNumber;
|
SequenceNumber initialSequenceNumber;
|
||||||
controlPacket->readPrimitive(&initialSequenceNumber);
|
controlPacket->readPrimitive(&initialSequenceNumber);
|
||||||
|
|
||||||
|
@ -782,18 +782,16 @@ void Connection::processHandshake(std::unique_ptr<ControlPacket> controlPacket)
|
||||||
_lastReceivedSequenceNumber = initialSequenceNumber - 1;
|
_lastReceivedSequenceNumber = initialSequenceNumber - 1;
|
||||||
_lastSentACK = initialSequenceNumber - 1;
|
_lastSentACK = initialSequenceNumber - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// immediately respond with a handshake ACK
|
_handshakeACK->reset();
|
||||||
static auto handshakeACK = ControlPacket::create(ControlPacket::HandshakeACK, sizeof(SequenceNumber));
|
_handshakeACK->writePrimitive(initialSequenceNumber);
|
||||||
handshakeACK->seek(0);
|
_parentSocket->writeBasePacket(*_handshakeACK, _destination);
|
||||||
handshakeACK->writePrimitive(initialSequenceNumber);
|
|
||||||
_parentSocket->writeBasePacket(*handshakeACK, _destination);
|
|
||||||
|
|
||||||
// indicate that handshake has been received
|
// indicate that handshake has been received
|
||||||
_hasReceivedHandshake = true;
|
_hasReceivedHandshake = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::processHandshakeACK(std::unique_ptr<ControlPacket> controlPacket) {
|
void Connection::processHandshakeACK(ControlPacketPointer controlPacket) {
|
||||||
// if we've decided to clean up the send queue then this handshake ACK should be ignored, it's useless
|
// if we've decided to clean up the send queue then this handshake ACK should be ignored, it's useless
|
||||||
if (_sendQueue) {
|
if (_sendQueue) {
|
||||||
SequenceNumber initialSequenceNumber;
|
SequenceNumber initialSequenceNumber;
|
||||||
|
@ -807,7 +805,7 @@ void Connection::processHandshakeACK(std::unique_ptr<ControlPacket> controlPacke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::processTimeoutNAK(std::unique_ptr<ControlPacket> controlPacket) {
|
void Connection::processTimeoutNAK(ControlPacketPointer controlPacket) {
|
||||||
// Override SendQueue's LossList with the timeout NAK list
|
// Override SendQueue's LossList with the timeout NAK list
|
||||||
getSendQueue().overrideNAKListFromPacket(*controlPacket);
|
getSendQueue().overrideNAKListFromPacket(*controlPacket);
|
||||||
|
|
||||||
|
@ -817,7 +815,7 @@ void Connection::processTimeoutNAK(std::unique_ptr<ControlPacket> controlPacket)
|
||||||
_stats.record(ConnectionStats::Stats::ReceivedTimeoutNAK);
|
_stats.record(ConnectionStats::Stats::ReceivedTimeoutNAK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::processProbeTail(std::unique_ptr<ControlPacket> controlPacket) {
|
void Connection::processProbeTail(ControlPacketPointer controlPacket) {
|
||||||
if (((uint32_t) _lastReceivedSequenceNumber & 0xF) == 0) {
|
if (((uint32_t) _lastReceivedSequenceNumber & 0xF) == 0) {
|
||||||
// this is the second packet in a probe set so we can estimate bandwidth
|
// this is the second packet in a probe set so we can estimate bandwidth
|
||||||
// the sender sent this to us in lieu of sending new data (because they didn't have any)
|
// the sender sent this to us in lieu of sending new data (because they didn't have any)
|
||||||
|
|
|
@ -55,6 +55,7 @@ public:
|
||||||
using SequenceNumberTimePair = std::pair<SequenceNumber, p_high_resolution_clock::time_point>;
|
using SequenceNumberTimePair = std::pair<SequenceNumber, p_high_resolution_clock::time_point>;
|
||||||
using ACKListPair = std::pair<SequenceNumber, SequenceNumberTimePair>;
|
using ACKListPair = std::pair<SequenceNumber, SequenceNumberTimePair>;
|
||||||
using SentACKList = std::list<ACKListPair>;
|
using SentACKList = std::list<ACKListPair>;
|
||||||
|
using ControlPacketPointer = std::unique_ptr<ControlPacket>;
|
||||||
|
|
||||||
Connection(Socket* parentSocket, HifiSockAddr destination, std::unique_ptr<CongestionControl> congestionControl);
|
Connection(Socket* parentSocket, HifiSockAddr destination, std::unique_ptr<CongestionControl> congestionControl);
|
||||||
~Connection();
|
~Connection();
|
||||||
|
@ -66,7 +67,7 @@ public:
|
||||||
|
|
||||||
// return indicates if this packet should be processed
|
// return indicates if this packet should be processed
|
||||||
bool processReceivedSequenceNumber(SequenceNumber sequenceNumber, int packetSize, int payloadSize);
|
bool processReceivedSequenceNumber(SequenceNumber sequenceNumber, int packetSize, int payloadSize);
|
||||||
void processControl(std::unique_ptr<ControlPacket> controlPacket);
|
void processControl(ControlPacketPointer controlPacket);
|
||||||
|
|
||||||
void queueReceivedMessagePacket(std::unique_ptr<Packet> packet);
|
void queueReceivedMessagePacket(std::unique_ptr<Packet> packet);
|
||||||
|
|
||||||
|
@ -96,14 +97,14 @@ private:
|
||||||
void sendNAK(SequenceNumber sequenceNumberRecieved);
|
void sendNAK(SequenceNumber sequenceNumberRecieved);
|
||||||
void sendTimeoutNAK();
|
void sendTimeoutNAK();
|
||||||
|
|
||||||
void processACK(std::unique_ptr<ControlPacket> controlPacket);
|
void processACK(ControlPacketPointer controlPacket);
|
||||||
void processLightACK(std::unique_ptr<ControlPacket> controlPacket);
|
void processLightACK(ControlPacketPointer controlPacket);
|
||||||
void processACK2(std::unique_ptr<ControlPacket> controlPacket);
|
void processACK2(ControlPacketPointer controlPacket);
|
||||||
void processNAK(std::unique_ptr<ControlPacket> controlPacket);
|
void processNAK(ControlPacketPointer controlPacket);
|
||||||
void processTimeoutNAK(std::unique_ptr<ControlPacket> controlPacket);
|
void processTimeoutNAK(ControlPacketPointer controlPacket);
|
||||||
void processHandshake(std::unique_ptr<ControlPacket> controlPacket);
|
void processHandshake(ControlPacketPointer controlPacket);
|
||||||
void processHandshakeACK(std::unique_ptr<ControlPacket> controlPacket);
|
void processHandshakeACK(ControlPacketPointer controlPacket);
|
||||||
void processProbeTail(std::unique_ptr<ControlPacket> controlPacket);
|
void processProbeTail(ControlPacketPointer controlPacket);
|
||||||
|
|
||||||
void resetReceiveState();
|
void resetReceiveState();
|
||||||
void resetRTT();
|
void resetRTT();
|
||||||
|
@ -171,7 +172,14 @@ private:
|
||||||
std::map<MessageNumber, PendingReceivedMessage> _pendingReceivedMessages;
|
std::map<MessageNumber, PendingReceivedMessage> _pendingReceivedMessages;
|
||||||
|
|
||||||
int _packetsSinceACK { 0 }; // The number of packets that have been received during the current ACK interval
|
int _packetsSinceACK { 0 }; // The number of packets that have been received during the current ACK interval
|
||||||
|
|
||||||
|
// Re-used control packets
|
||||||
|
ControlPacketPointer _ackPacket;
|
||||||
|
ControlPacketPointer _lightACKPacket;
|
||||||
|
ControlPacketPointer _ack2Packet;
|
||||||
|
ControlPacketPointer _lossReport;
|
||||||
|
ControlPacketPointer _handshakeACK;
|
||||||
|
|
||||||
ConnectionStats _stats;
|
ConnectionStats _stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -22,11 +22,8 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
#include <windows.h>
|
||||||
#include "CPUIdent.h"
|
#include "CPUIdent.h"
|
||||||
#include <Psapi.h>
|
#include <Psapi.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -877,3 +874,143 @@ bool getMemoryInfo(MemoryInfo& info) {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Largely taken from: https://msdn.microsoft.com/en-us/library/windows/desktop/ms683194(v=vs.85).aspx
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
using LPFN_GLPI = BOOL(WINAPI*)(
|
||||||
|
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
|
||||||
|
PDWORD);
|
||||||
|
|
||||||
|
DWORD CountSetBits(ULONG_PTR bitMask)
|
||||||
|
{
|
||||||
|
DWORD LSHIFT = sizeof(ULONG_PTR) * 8 - 1;
|
||||||
|
DWORD bitSetCount = 0;
|
||||||
|
ULONG_PTR bitTest = (ULONG_PTR)1 << LSHIFT;
|
||||||
|
DWORD i;
|
||||||
|
|
||||||
|
for (i = 0; i <= LSHIFT; ++i) {
|
||||||
|
bitSetCount += ((bitMask & bitTest) ? 1 : 0);
|
||||||
|
bitTest /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitSetCount;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool getProcessorInfo(ProcessorInfo& info) {
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
LPFN_GLPI glpi;
|
||||||
|
bool done = false;
|
||||||
|
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
|
||||||
|
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
|
||||||
|
DWORD returnLength = 0;
|
||||||
|
DWORD logicalProcessorCount = 0;
|
||||||
|
DWORD numaNodeCount = 0;
|
||||||
|
DWORD processorCoreCount = 0;
|
||||||
|
DWORD processorL1CacheCount = 0;
|
||||||
|
DWORD processorL2CacheCount = 0;
|
||||||
|
DWORD processorL3CacheCount = 0;
|
||||||
|
DWORD processorPackageCount = 0;
|
||||||
|
DWORD byteOffset = 0;
|
||||||
|
PCACHE_DESCRIPTOR Cache;
|
||||||
|
|
||||||
|
glpi = (LPFN_GLPI)GetProcAddress(
|
||||||
|
GetModuleHandle(TEXT("kernel32")),
|
||||||
|
"GetLogicalProcessorInformation");
|
||||||
|
if (nullptr == glpi) {
|
||||||
|
qDebug() << "GetLogicalProcessorInformation is not supported.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
DWORD rc = glpi(buffer, &returnLength);
|
||||||
|
|
||||||
|
if (FALSE == rc) {
|
||||||
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||||
|
if (buffer) {
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(
|
||||||
|
returnLength);
|
||||||
|
|
||||||
|
if (NULL == buffer) {
|
||||||
|
qDebug() << "Error: Allocation failure";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "Error " << GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = buffer;
|
||||||
|
|
||||||
|
while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) {
|
||||||
|
switch (ptr->Relationship) {
|
||||||
|
case RelationNumaNode:
|
||||||
|
// Non-NUMA systems report a single record of this type.
|
||||||
|
numaNodeCount++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RelationProcessorCore:
|
||||||
|
processorCoreCount++;
|
||||||
|
|
||||||
|
// A hyperthreaded core supplies more than one logical processor.
|
||||||
|
logicalProcessorCount += CountSetBits(ptr->ProcessorMask);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RelationCache:
|
||||||
|
// Cache data is in ptr->Cache, one CACHE_DESCRIPTOR structure for each cache.
|
||||||
|
Cache = &ptr->Cache;
|
||||||
|
if (Cache->Level == 1) {
|
||||||
|
processorL1CacheCount++;
|
||||||
|
} else if (Cache->Level == 2) {
|
||||||
|
processorL2CacheCount++;
|
||||||
|
} else if (Cache->Level == 3) {
|
||||||
|
processorL3CacheCount++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RelationProcessorPackage:
|
||||||
|
// Logical processors share a physical package.
|
||||||
|
processorPackageCount++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qDebug() << "\nError: Unsupported LOGICAL_PROCESSOR_RELATIONSHIP value.\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "GetLogicalProcessorInformation results:";
|
||||||
|
qDebug() << "Number of NUMA nodes:" << numaNodeCount;
|
||||||
|
qDebug() << "Number of physical processor packages:" << processorPackageCount;
|
||||||
|
qDebug() << "Number of processor cores:" << processorCoreCount;
|
||||||
|
qDebug() << "Number of logical processors:" << logicalProcessorCount;
|
||||||
|
qDebug() << "Number of processor L1/L2/L3 caches:"
|
||||||
|
<< processorL1CacheCount
|
||||||
|
<< "/" << processorL2CacheCount
|
||||||
|
<< "/" << processorL3CacheCount;
|
||||||
|
|
||||||
|
info.numPhysicalProcessorPackages = processorPackageCount;
|
||||||
|
info.numProcessorCores = processorCoreCount;
|
||||||
|
info.numLogicalProcessors = logicalProcessorCount;
|
||||||
|
info.numProcessorCachesL1 = processorL1CacheCount;
|
||||||
|
info.numProcessorCachesL2 = processorL2CacheCount;
|
||||||
|
info.numProcessorCachesL3 = processorL3CacheCount;
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -214,4 +214,15 @@ struct MemoryInfo {
|
||||||
|
|
||||||
bool getMemoryInfo(MemoryInfo& info);
|
bool getMemoryInfo(MemoryInfo& info);
|
||||||
|
|
||||||
|
struct ProcessorInfo {
|
||||||
|
int32_t numPhysicalProcessorPackages;
|
||||||
|
int32_t numProcessorCores;
|
||||||
|
int32_t numLogicalProcessors;
|
||||||
|
int32_t numProcessorCachesL1;
|
||||||
|
int32_t numProcessorCachesL2;
|
||||||
|
int32_t numProcessorCachesL3;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool getProcessorInfo(ProcessorInfo& info);
|
||||||
|
|
||||||
#endif // hifi_SharedUtil_h
|
#endif // hifi_SharedUtil_h
|
||||||
|
|
201
scripts/developer/tests/performance/domain-check.js
Normal file
201
scripts/developer/tests/performance/domain-check.js
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
"use strict";
|
||||||
|
/*jslint vars: true, plusplus: true*/
|
||||||
|
/*globals Script, MyAvatar, Quat, Render, ScriptDiscoveryService, Window, LODManager, Entities, print*/
|
||||||
|
//
|
||||||
|
// loadedMachine.js
|
||||||
|
// scripts/developer/tests/
|
||||||
|
//
|
||||||
|
// Created by Howard Stearns on 6/6/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
|
||||||
|
//
|
||||||
|
// Confirms that the specified domain is operating within specified constraints.
|
||||||
|
|
||||||
|
var MINIMUM_DESKTOP_FRAMERATE = 57; // frames per second
|
||||||
|
var MINIMUM_HMD_FRAMERATE = 86;
|
||||||
|
var EXPECTED_DESKTOP_FRAMERATE = 60;
|
||||||
|
var EXPECTED_HMD_FRAMERATE = 90;
|
||||||
|
var MAXIMUM_LOAD_TIME = 60; // seconds
|
||||||
|
var MINIMUM_AVATARS = 25; // FIXME: not implemented yet. Requires agent scripts. Idea is to have them organize themselves to the right number.
|
||||||
|
|
||||||
|
var version = 1;
|
||||||
|
function debug() {
|
||||||
|
print.apply(null, [].concat.apply(['hrs fixme', version], [].map.call(arguments, JSON.stringify)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptyishPlace = 'empty';
|
||||||
|
var cachePlaces = ['localhost', 'Welcome'];
|
||||||
|
var isInCachePlace = cachePlaces.indexOf(location.hostname) >= 0;
|
||||||
|
var defaultPlace = isInCachePlace ? 'Playa' : location.hostname;
|
||||||
|
var prompt = "domain-check.js version " + version + "\n\nWhat place should we enter?";
|
||||||
|
debug(cachePlaces, isInCachePlace, defaultPlace, prompt);
|
||||||
|
var entryPlace = Window.prompt(prompt, defaultPlace);
|
||||||
|
|
||||||
|
var fail = false, results = "";
|
||||||
|
function addResult(label, actual, minimum, maximum) {
|
||||||
|
if ((minimum !== undefined) && (actual < minimum)) {
|
||||||
|
fail = true;
|
||||||
|
}
|
||||||
|
if ((maximum !== undefined) && (actual > maximum)) {
|
||||||
|
fail = true;
|
||||||
|
}
|
||||||
|
results += "\n" + label + ": " + actual + " (" + ((100 * actual) / (maximum || minimum)).toFixed(0) + "%)";
|
||||||
|
}
|
||||||
|
function giveReport() {
|
||||||
|
Window.alert(entryPlace + (fail ? " FAILED" : " OK") + "\n" + results);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests are performed domain-wide, at full LOD
|
||||||
|
var initialLodIsAutomatic = LODManager.getAutomaticLODAdjust();
|
||||||
|
var LOD = 32768 * 400;
|
||||||
|
LODManager.setAutomaticLODAdjust(false);
|
||||||
|
LODManager.setOctreeSizeScale(LOD);
|
||||||
|
Script.scriptEnding.connect(function () { LODManager.setAutomaticLODAdjust(initialLodIsAutomatic); });
|
||||||
|
|
||||||
|
function startTwirl(targetRotation, degreesPerUpdate, interval, strafeDistance, optionalCallback) {
|
||||||
|
var initialRotation = Quat.safeEulerAngles(MyAvatar.orientation).y;
|
||||||
|
var accumulatedRotation = 0;
|
||||||
|
function tick() {
|
||||||
|
MyAvatar.orientation = Quat.fromPitchYawRollDegrees(0, accumulatedRotation + initialRotation, 0);
|
||||||
|
if (strafeDistance) {
|
||||||
|
MyAvatar.position = Vec3.sum(MyAvatar.position, Vec3.multiply(strafeDistance, Quat.getRight(MyAvatar.orientation)));
|
||||||
|
}
|
||||||
|
accumulatedRotation += degreesPerUpdate;
|
||||||
|
if (accumulatedRotation >= targetRotation) {
|
||||||
|
return optionalCallback && optionalCallback();
|
||||||
|
}
|
||||||
|
Script.setTimeout(tick, interval);
|
||||||
|
}
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
function doLoad(place, continuationWithLoadTime) { // Go to place and call continuationWithLoadTime(loadTimeInSeconds)
|
||||||
|
var start = Date.now(), timeout, onDownloadUpdate, finishedTwirl = false, loadTime;
|
||||||
|
function clearHandlers() {
|
||||||
|
debug('clearHandlers');
|
||||||
|
Stats.downloadsPendingChanged.disconnect(onDownloadUpdate);
|
||||||
|
Stats.downloadsChanged.disconnect(onDownloadUpdate);
|
||||||
|
}
|
||||||
|
function waitForLoad(flag) {
|
||||||
|
debug('entry', place, 'initial downloads/pending', Stats.downloads, Stats.downloadsPending);
|
||||||
|
location.hostChanged.disconnect(waitForLoad);
|
||||||
|
timeout = Script.setTimeout(function () {
|
||||||
|
debug('downloads timeout', Date());
|
||||||
|
clearHandlers();
|
||||||
|
Window.alert("Timeout during " + place + " load. FAILED");
|
||||||
|
Script.stop();
|
||||||
|
}, MAXIMUM_LOAD_TIME * 1000);
|
||||||
|
startTwirl(360, 6, 90, null, function () {
|
||||||
|
finishedTwirl = true;
|
||||||
|
if (loadTime) {
|
||||||
|
continuationWithLoadTime(loadTime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Stats.downloadsPendingChanged.connect(onDownloadUpdate);
|
||||||
|
Stats.downloadsChanged.connect(onDownloadUpdate);
|
||||||
|
}
|
||||||
|
function isLoading() {
|
||||||
|
// FIXME: This tells us when download are completed, but it doesn't tell us when the objects are parsed and loaded.
|
||||||
|
// We really want something like _physicsEnabled, but that isn't signalled.
|
||||||
|
return Stats.downloads || Stats.downloadsPending;
|
||||||
|
}
|
||||||
|
onDownloadUpdate = function onDownloadUpdate() {
|
||||||
|
debug('update downloads/pending', Stats.downloads, Stats.downloadsPending);
|
||||||
|
if (isLoading()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Script.clearTimeout(timeout);
|
||||||
|
clearHandlers();
|
||||||
|
loadTime = (Date.now() - start) / 1000;
|
||||||
|
if (finishedTwirl) {
|
||||||
|
continuationWithLoadTime(loadTime);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function doit() {
|
||||||
|
debug('go', place);
|
||||||
|
location.hostChanged.connect(waitForLoad);
|
||||||
|
location.handleLookupString(place);
|
||||||
|
}
|
||||||
|
if (location.placename.toLowerCase() === place.toLowerCase()) {
|
||||||
|
location.handleLookupString(emptyishPlace);
|
||||||
|
Script.setTimeout(doit, 1000);
|
||||||
|
} else {
|
||||||
|
doit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = Render.getConfig("Stats");
|
||||||
|
function doRender(continuation) {
|
||||||
|
var start = Date.now(), frames = 0;
|
||||||
|
function onNewStats() { // Accumulates frames on signal during load test
|
||||||
|
frames++;
|
||||||
|
}
|
||||||
|
config.newStats.connect(onNewStats);
|
||||||
|
startTwirl(720, 1, 15, 0.08, function () {
|
||||||
|
var end = Date.now();
|
||||||
|
config.newStats.disconnect(onNewStats);
|
||||||
|
addResult('frame rate', 1000 * frames / (end - start),
|
||||||
|
HMD.active ? MINIMUM_HMD_FRAMERATE : MINIMUM_DESKTOP_FRAMERATE,
|
||||||
|
HMD.active ? EXPECTED_HMD_FRAMERATE : EXPECTED_DESKTOP_FRAMERATE);
|
||||||
|
continuation();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybePrepareCache(continuation) {
|
||||||
|
var prepareCache = Window.confirm("Prepare cache?\n\n\
|
||||||
|
Should we start with all and only those items cached that are encountered when visiting:\n" + cachePlaces.join(', ') + "\n\
|
||||||
|
If 'yes', cache will be cleared and we will visit these two, with a turn in each, and wait for everything to be loaded.\n\
|
||||||
|
You would want to say 'no' (and make other preparations) if you were testing these places.");
|
||||||
|
|
||||||
|
if (prepareCache) {
|
||||||
|
location.handleLookupString(emptyishPlace);
|
||||||
|
Window.alert("Please do menu Edit->Reload Content (Clears all caches) and THEN press 'ok'.");
|
||||||
|
function loadNext() {
|
||||||
|
var place = cachePlaces.shift();
|
||||||
|
doLoad(place, function (prepTime) {
|
||||||
|
debug(place, 'ready', prepTime);
|
||||||
|
if (cachePlaces.length) {
|
||||||
|
loadNext();
|
||||||
|
} else {
|
||||||
|
continuation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
loadNext();
|
||||||
|
} else {
|
||||||
|
continuation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeRunTribbles(continuation) {
|
||||||
|
if (Window.confirm("Run tribbles?\n\n\
|
||||||
|
At most, only one participant should say yes.")) {
|
||||||
|
Script.load('http://howard-stearns.github.io/models/scripts/tests/performance/tribbles.js'); // FIXME: replace with AWS
|
||||||
|
Script.setTimeout(continuation, 3000);
|
||||||
|
} else {
|
||||||
|
continuation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entryPlace) {
|
||||||
|
Window.alert("domain-check.js cancelled");
|
||||||
|
Script.stop();
|
||||||
|
} else {
|
||||||
|
maybePrepareCache(function (prepTime) {
|
||||||
|
debug('cache ready', prepTime);
|
||||||
|
doLoad(entryPlace, function (loadTime) {
|
||||||
|
addResult("load time", loadTime, undefined, MAXIMUM_LOAD_TIME);
|
||||||
|
maybeRunTribbles(function () {
|
||||||
|
doRender(function () {
|
||||||
|
giveReport();
|
||||||
|
Script.stop();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function () { print("domain-check completed"); });
|
|
@ -17,11 +17,9 @@
|
||||||
// keystroke:
|
// keystroke:
|
||||||
//
|
//
|
||||||
// CTRL/s for snapshot.
|
// CTRL/s for snapshot.
|
||||||
// CTRL/m for mic mute and unmute.
|
|
||||||
|
|
||||||
// System generated notifications:
|
// System generated notifications:
|
||||||
// If Screen is resized.
|
// Connection refused.
|
||||||
// If mic is muted for any reason.
|
|
||||||
//
|
//
|
||||||
// To add a new System notification type:
|
// To add a new System notification type:
|
||||||
//
|
//
|
||||||
|
@ -92,16 +90,12 @@ var lodTextID = false;
|
||||||
|
|
||||||
var NotificationType = {
|
var NotificationType = {
|
||||||
UNKNOWN: 0,
|
UNKNOWN: 0,
|
||||||
MUTE_TOGGLE: 1,
|
SNAPSHOT: 1,
|
||||||
SNAPSHOT: 2,
|
LOD_WARNING: 2,
|
||||||
WINDOW_RESIZE: 3,
|
CONNECTION_REFUSED: 3,
|
||||||
LOD_WARNING: 4,
|
EDIT_ERROR: 4,
|
||||||
CONNECTION_REFUSED: 5,
|
|
||||||
EDIT_ERROR: 6,
|
|
||||||
properties: [
|
properties: [
|
||||||
{ text: "Mute Toggle" },
|
|
||||||
{ text: "Snapshot" },
|
{ text: "Snapshot" },
|
||||||
{ text: "Window Resize" },
|
|
||||||
{ text: "Level of Detail" },
|
{ text: "Level of Detail" },
|
||||||
{ text: "Connection Refused" },
|
{ text: "Connection Refused" },
|
||||||
{ text: "Edit error" }
|
{ text: "Edit error" }
|
||||||
|
@ -446,19 +440,6 @@ function wordWrap(str) {
|
||||||
return stringDivider(str, 43.0, "\n");
|
return stringDivider(str, 43.0, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// This fires a notification on window resize
|
|
||||||
function checkSize() {
|
|
||||||
if ((Window.innerWidth !== ourWidth) || (Window.innerHeight !== ourHeight)) {
|
|
||||||
var windowResize = "Window has been resized";
|
|
||||||
ourWidth = Window.innerWidth;
|
|
||||||
ourHeight = Window.innerHeight;
|
|
||||||
windowDimensions = Controller.getViewportDimensions();
|
|
||||||
overlayLocationX = (windowDimensions.x - (width + 60.0));
|
|
||||||
buttonLocationX = overlayLocationX + (width - 35.0);
|
|
||||||
createNotification(windowResize, NotificationType.WINDOW_RESIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
var nextOverlay,
|
var nextOverlay,
|
||||||
noticeOut,
|
noticeOut,
|
||||||
|
@ -480,7 +461,6 @@ function update() {
|
||||||
|
|
||||||
frame += 1;
|
frame += 1;
|
||||||
if ((frame % 60.0) === 0) { // only update once a second
|
if ((frame % 60.0) === 0) { // only update once a second
|
||||||
checkSize(); // checks for size change to trigger windowResize notification
|
|
||||||
locationY = 20.0;
|
locationY = 20.0;
|
||||||
for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade
|
for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade
|
||||||
nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY });
|
nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY });
|
||||||
|
@ -533,16 +513,6 @@ function isStartingUp() {
|
||||||
return startingUp;
|
return startingUp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Triggers mic mute notification
|
|
||||||
function onMuteStateChanged() {
|
|
||||||
var muteState,
|
|
||||||
muteString;
|
|
||||||
|
|
||||||
muteState = AudioDevice.getMuted() ? "muted" : "unmuted";
|
|
||||||
muteString = "Microphone is now " + muteState;
|
|
||||||
createNotification(muteString, NotificationType.MUTE_TOGGLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDomainConnectionRefused(reason) {
|
function onDomainConnectionRefused(reason) {
|
||||||
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
|
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
|
||||||
}
|
}
|
||||||
|
@ -653,7 +623,6 @@ LODManager.LODDecreased.connect(function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AudioDevice.muteToggled.connect(onMuteStateChanged);
|
|
||||||
Controller.keyPressEvent.connect(keyPressEvent);
|
Controller.keyPressEvent.connect(keyPressEvent);
|
||||||
Controller.mousePressEvent.connect(mousePressEvent);
|
Controller.mousePressEvent.connect(mousePressEvent);
|
||||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||||
|
|
|
@ -56,98 +56,99 @@ a:hover {
|
||||||
color: #2D88A4;
|
color: #2D88A4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header{
|
.header {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
left: 2.5%
|
left: 2.5%
|
||||||
}
|
}
|
||||||
.colmask{
|
.colmask {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
left: 2.5%
|
left: 2.5%
|
||||||
}
|
}
|
||||||
.colmid{ right: 25% }
|
.colmid { right: 25% }
|
||||||
.colin{ right: 25% }
|
.colin { right: 25% }
|
||||||
.colleft{ right: 25% }
|
.colleft { right: 25% }
|
||||||
.col1{
|
.col1 {
|
||||||
width: 23%;
|
width: 23%;
|
||||||
left: 101%
|
left: 101%
|
||||||
}
|
}
|
||||||
.col2{
|
.col2 {
|
||||||
width: 23%;
|
width: 23%;
|
||||||
left: 53%
|
left: 53%
|
||||||
}
|
}
|
||||||
.col3{
|
.col3 {
|
||||||
width: 23%;
|
width: 23%;
|
||||||
left: 80%
|
left: 80%
|
||||||
}
|
}
|
||||||
.col4{
|
.col4 {
|
||||||
width: 23%;
|
width: 23%;
|
||||||
left: 82%
|
left: 82%
|
||||||
}
|
}
|
||||||
.footer{
|
.footer {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
left: 2.5%
|
left: 2.5%
|
||||||
}
|
}
|
||||||
.header{
|
.header {
|
||||||
clear: both;
|
clear: both;
|
||||||
float: left;
|
float: left;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-bottom: #000 1px solid;
|
border-bottom: #000 1px solid;
|
||||||
background-color: #b4d2f7
|
background-color: #b4d2f7
|
||||||
}
|
}
|
||||||
.colmask{
|
.colmask {
|
||||||
clear: both;
|
clear: both;
|
||||||
float: left;
|
float: left;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
.colmid{
|
.colmid {
|
||||||
float: left;
|
float: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
.colin{
|
.colin {
|
||||||
float: left;
|
float: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
.colleft{
|
.colleft {
|
||||||
float: left;
|
float: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
.col1{
|
.col1 {
|
||||||
|
padding: 0px 0px 1em 0px;
|
||||||
|
overflow: hidden;
|
||||||
|
float: left;
|
||||||
|
position: relative;
|
||||||
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
}
|
||||||
|
.col2 {
|
||||||
padding: 0px 0px 1em 0px;
|
padding: 0px 0px 1em 0px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
float: left;
|
float: left;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
.col2{
|
.col3 {
|
||||||
padding: 0px 0px 1em 0px;
|
padding: 0px 0px 1em 0px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
float: left;
|
float: left;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
.col3{
|
.col4 {
|
||||||
padding: 0px 0px 1em 0px;
|
padding: 0px 0px 1em 0px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
float: left;
|
float: left;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
.col4{
|
.footer {
|
||||||
padding: 0px 0px 1em 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
float: left;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
}
|
|
||||||
.footer{
|
|
||||||
clear: both;
|
clear: both;
|
||||||
float: left;
|
float: left;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -155,7 +156,7 @@ a:hover {
|
||||||
border-top: #000 1px solid;
|
border-top: #000 1px solid;
|
||||||
|
|
||||||
}
|
}
|
||||||
.bottom{
|
.bottom {
|
||||||
clear: both;
|
clear: both;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
float: left;
|
float: left;
|
||||||
|
|
|
@ -501,16 +501,15 @@ function backupResourceDirectories(folder) {
|
||||||
fs.mkdirSync(folder);
|
fs.mkdirSync(folder);
|
||||||
console.log("Created directory " + folder);
|
console.log("Created directory " + folder);
|
||||||
|
|
||||||
|
|
||||||
var dsBackup = path.join(folder, '/domain-server');
|
var dsBackup = path.join(folder, '/domain-server');
|
||||||
fs.renameSync(getDomainServerClientResourcesDirectory(), dsBackup);
|
|
||||||
console.log("Moved directory " + getDomainServerClientResourcesDirectory());
|
|
||||||
console.log("to " + dsBackup);
|
|
||||||
|
|
||||||
var acBackup = path.join(folder, '/assignment-client');
|
var acBackup = path.join(folder, '/assignment-client');
|
||||||
fs.renameSync(getAssignmentClientResourcesDirectory(), acBackup);
|
|
||||||
console.log("Moved directory " + getDomainServerClientResourcesDirectory());
|
fs.copySync(getDomainServerClientResourcesDirectory(), dsBackup);
|
||||||
console.log("to " + acBackup);
|
fs.copySync(getAssignmentClientResourcesDirectory(), acBackup);
|
||||||
|
|
||||||
|
fs.removeSync(getDomainServerClientResourcesDirectory());
|
||||||
|
fs.removeSync(getAssignmentClientResourcesDirectory());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
@ -519,23 +518,22 @@ function backupResourceDirectories(folder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function openBackupInstructions(folder) {
|
function openBackupInstructions(folder) {
|
||||||
// Explain user how to restore server
|
// Explain user how to restore server
|
||||||
var window = new BrowserWindow({
|
var window = new BrowserWindow({
|
||||||
icon: appIcon,
|
icon: appIcon,
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 520,
|
height: 520,
|
||||||
});
|
});
|
||||||
window.loadURL('file://' + __dirname + '/content-update.html');
|
window.loadURL('file://' + __dirname + '/content-update.html');
|
||||||
if (!debug) {
|
if (!debug) {
|
||||||
window.setMenu(null);
|
window.setMenu(null);
|
||||||
}
|
}
|
||||||
window.show();
|
window.show();
|
||||||
|
|
||||||
electron.ipcMain.on('ready', function() {
|
|
||||||
console.log("got ready");
|
|
||||||
window.webContents.send('update', folder);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
electron.ipcMain.on('ready', function() {
|
||||||
|
console.log("got ready");
|
||||||
|
window.webContents.send('update', folder);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function backupResourceDirectoriesAndRestart() {
|
function backupResourceDirectoriesAndRestart() {
|
||||||
homeServer.stop();
|
homeServer.stop();
|
||||||
|
@ -576,15 +574,30 @@ function checkNewContent() {
|
||||||
dialog.showMessageBox({
|
dialog.showMessageBox({
|
||||||
type: 'question',
|
type: 'question',
|
||||||
buttons: ['Yes', 'No'],
|
buttons: ['Yes', 'No'],
|
||||||
|
defaultId: 1,
|
||||||
|
cancelId: 1,
|
||||||
title: 'New home content',
|
title: 'New home content',
|
||||||
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?',
|
||||||
|
noLink: true,
|
||||||
}, function(idx) {
|
}, function(idx) {
|
||||||
if (idx === 0) {
|
if (idx === 0) {
|
||||||
backupResourceDirectoriesAndRestart();
|
dialog.showMessageBox({
|
||||||
} else {
|
type: 'warning',
|
||||||
// They don't want to update, mark content set as current
|
buttons: ['Yes', 'No'],
|
||||||
userConfig.set('homeContentLastModified', new Date());
|
defaultId: 1,
|
||||||
}
|
cancelId: 1,
|
||||||
|
title: 'Are you sure?',
|
||||||
|
message: 'Updating with the new content will remove all your current content and settings and place them in a backup folder.\nAre you sure?',
|
||||||
|
noLink: true,
|
||||||
|
}, function(idx) {
|
||||||
|
if (idx === 0) {
|
||||||
|
backupResourceDirectoriesAndRestart();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// They don't want to update, mark content set as current
|
||||||
|
userConfig.set('homeContentLastModified', new Date());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue