Merge branch 'master' of https://github.com/highfidelity/hifi into HMDLightingBug

This commit is contained in:
Sam Gateau 2015-09-15 08:35:42 -07:00
commit 9f8d6a78cc
28 changed files with 716 additions and 179 deletions

View file

@ -15,16 +15,22 @@
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("connexionclient")
hifi_library_search_hints("3dconnexionclient")
if (APPLE)
find_library(3DCONNEXIONCLIENT_LIBRARIES NAMES 3DConnexionClient HINTS 3DCONNEXIONCLIENT_SEARCH_DIRS)
if(EXISTS ${3DConnexionClient})
set(3DCONNEXIONCLIENT_FOUND true)
set(3DCONNEXIONCLIENT_INCLUDE_DIRS ${3DConnexionClient})
set(3DCONNEXIONCLIENT_LIBRARY ${3DConnexionClient})
message(STATUS "Found 3DConnexion at " ${3DConnexionClient})
mark_as_advanced(3DCONNEXIONCLIENT_INCLUDE_DIR 3DCONNEXIONCLIENT_LIBRARY)
find_library(3DCONNEXIONCLIENT 3DconnexionClient)
if(EXISTS ${3DCONNEXIONCLIENT})
find_path(3DCONNEXIONCLIENT_INCLUDE_DIR2 ConnexionClient.h PATH_SUFFIXES include HINTS ${3DCONNEXIONCLIENT_SEARCH_DIRS})
include_directories(${3DCONNEXIONCLIENT_INCLUDE_DIR2})
get_filename_component( 3DCONNEXIONCLIENT_FRAMEWORK_DIR ${3DCONNEXIONCLIENT} PATH )
set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS "-weak_framework 3DconnexionClient")
set_target_properties(${TARGET_NAME} PROPERTIES FRAMEWORK_SEARCH_PATHS 3DCONNEXIONCLIENT_FRAMEWORK_DIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(3DCONNEXIONCLIENT DEFAULT_MSG 3DCONNEXIONCLIENT_INCLUDE_DIR2)
mark_as_advanced(3DCONNEXIONCLIENT_INCLUDE_DIR2)
message(STATUS "Found 3DConnexion")
else ()
message(STATUS "Could NOT find 3DConnexionClient")
endif()
@ -32,7 +38,7 @@ elseif (WIN32)
find_path(3DCONNEXIONCLIENT_INCLUDE_DIRS I3dMouseParams.h PATH_SUFFIXES include HINTS ${3DCONNEXIONCLIENT_SEARCH_DIRS})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(3DConnexionClient DEFAULT_MSG 3DCONNEXIONCLIENT_INCLUDE_DIRS)
find_package_handle_standard_args(3DCONNEXIONCLIENT DEFAULT_MSG 3DCONNEXIONCLIENT_INCLUDE_DIRS)
mark_as_advanced(3DCONNEXIONCLIENT_INCLUDE_DIRS 3DCONNEXIONCLIENT_SEARCH_DIRS)
endif()
endif()

View file

@ -0,0 +1,237 @@
//==============================================================================
#ifndef _H_connexionclient
#define _H_connexionclient
#include <stdint.h>
//==============================================================================
#ifdef __cplusplus
extern "C" {
#endif
//==============================================================================
#pragma pack(push,2)
//==============================================================================
// Client registration modes
// Use kConnexionClientWildcard ('****') as the application signature in the
// RegisterConnexionClient API to take over the device system-wide in all
// applications:
#define kConnexionClientWildcard 0x2A2A2A2A
// There are two plugin operating modes: one takes over the device
// and disables all built-in driver assignments, the other complements
// the driver by only executing commands that are meant for plugins:
enum {
kConnexionClientModeTakeOver = 1, // take over device completely, driver no longer executes assignments
kConnexionClientModePlugin = 2 // receive plugin assignments only, let driver take care of its own
};
//==============================================================================
// Client commands
// The following assignments must be executed by the client:
enum {
kConnexionCmdNone = 0,
kConnexionCmdHandleRawData = 1,
kConnexionCmdHandleButtons = 2,
kConnexionCmdHandleAxis = 3,
kConnexionCmdAppSpecific = 10
};
//==============================================================================
// Messages
// The following messages are forwarded to user space clients:
#define kConnexionMsgDeviceState '3dSR' // forwarded device state data
#define kConnexionMsgPrefsChanged '3dPC' // notify clients that the current app prefs have changed
#define kConnexionMsgCalibrateDevice '3dSC' // device state data to be used for calibration
// Control messages for the driver sent via the ConnexionControl API:
#define kConnexionCtlSetLEDState '3dsl' // set the LED state, param = (uint8_t)ledState
#define kConnexionCtlGetDeviceID '3did' // get vendorID and productID in the high and low words of the result
#define kConnexionCtlCalibrate '3dca' // calibrate the device with the current axes values (same as executing the calibrate assignment)
#define kConnexionCtlUncalibrate '3dde' // uncalibrate the device (i.e. reset calibration to 0,0,0,0,0,0)
#define kConnexionCtlOpenPrefPane '3dop' // open the 3dconnexion preference pane in System Preferences
#define kConnexionCtlSetSwitches '3dss' // set the current state of the client-controlled feature switches (bitmap, see masks below)
// Client capability mask constants (this mask defines which buttons and controls should be sent to clients, the others are handled by the driver)
#define kConnexionMaskButton1 0x0001
#define kConnexionMaskButton2 0x0002
#define kConnexionMaskButton3 0x0004
#define kConnexionMaskButton4 0x0008
#define kConnexionMaskButton5 0x0010
#define kConnexionMaskButton6 0x0020
#define kConnexionMaskButton7 0x0040
#define kConnexionMaskButton8 0x0080
#define kConnexionMaskAxis1 0x0100
#define kConnexionMaskAxis2 0x0200
#define kConnexionMaskAxis3 0x0400
#define kConnexionMaskAxis4 0x0800
#define kConnexionMaskAxis5 0x1000
#define kConnexionMaskAxis6 0x2000
#define kConnexionMaskButtons 0x00FF // note: this only specifies the first 8 buttons, kept for backwards compatibility
#define kConnexionMaskAxisTrans 0x0700
#define kConnexionMaskAxisRot 0x3800
#define kConnexionMaskAxis 0x3F00
#define kConnexionMaskAll 0x3FFF
// Added in version 10:0 to support all 32 buttons on the SpacePilot Pro, use with the new SetConnexionClientButtonMask API
#define kConnexionMaskButton9 0x00000100
#define kConnexionMaskButton10 0x00000200
#define kConnexionMaskButton11 0x00000400
#define kConnexionMaskButton12 0x00000800
#define kConnexionMaskButton13 0x00001000
#define kConnexionMaskButton14 0x00002000
#define kConnexionMaskButton15 0x00004000
#define kConnexionMaskButton16 0x00008000
#define kConnexionMaskButton17 0x00010000
#define kConnexionMaskButton18 0x00020000
#define kConnexionMaskButton19 0x00040000
#define kConnexionMaskButton20 0x00080000
#define kConnexionMaskButton21 0x00100000
#define kConnexionMaskButton22 0x00200000
#define kConnexionMaskButton23 0x00400000
#define kConnexionMaskButton24 0x00800000
#define kConnexionMaskButton25 0x01000000
#define kConnexionMaskButton26 0x02000000
#define kConnexionMaskButton27 0x04000000
#define kConnexionMaskButton28 0x08000000
#define kConnexionMaskButton29 0x10000000
#define kConnexionMaskButton30 0x20000000
#define kConnexionMaskButton31 0x40000000
#define kConnexionMaskButton32 0x80000000
#define kConnexionMaskAllButtons 0xFFFFFFFF
// Masks for client-controlled feature switches
#define kConnexionSwitchDominant 0x0002
#define kConnexionSwitchEnableAxis1 0x0004
#define kConnexionSwitchEnableAxis2 0x0008
#define kConnexionSwitchEnableAxis3 0x0010
#define kConnexionSwitchEnableAxis4 0x0020
#define kConnexionSwitchEnableAxis5 0x0040
#define kConnexionSwitchEnableAxis6 0x0080
#define kConnexionSwitchEnableTrans 0x001C
#define kConnexionSwitchEnableRot 0x00E0
#define kConnexionSwitchEnableAll 0x00FC
#define kConnexionSwitchZoomOnY 0x0001 // no longer applies, no effect on new driver
#define kConnexionSwitchReverseAxis1 0x0100 // no longer applies, no effect on new driver
#define kConnexionSwitchReverseAxis2 0x0200 // no longer applies, no effect on new driver
#define kConnexionSwitchReverseAxis3 0x0400 // no longer applies, no effect on new driver
#define kConnexionSwitchReverseAxis4 0x0800 // no longer applies, no effect on new driver
#define kConnexionSwitchReverseAxis5 0x1000 // no longer applies, no effect on new driver
#define kConnexionSwitchReverseAxis6 0x2000 // no longer applies, no effect on new driver
#define kConnexionSwitchReverseTrans 0x0700 // no longer applies, no effect on new driver
#define kConnexionSwitchReverseRot 0x3800 // no longer applies, no effect on new driver
#define kConnexionSwitchReverseAll 0x3F00 // no longer applies, no effect on new driver
#define kConnexionSwitchesDisabled 0x80000000 // use driver defaults instead of client-controlled switches
//==============================================================================
// Device state record
// Structure type and current version:
#define kConnexionDeviceStateType 0x4D53 // 'MS' (Connexion State)
#define kConnexionDeviceStateVers 0x6D33 // 'm3' (version 3 includes 32-bit button data in previously unused field, binary compatible with version 2)
// This structure is used to forward device data and commands from the kext to the client:
typedef struct {
// header
uint16_t version; // kConnexionDeviceStateVers
uint16_t client; // identifier of the target client when sending a state message to all user clients
// command
uint16_t command; // command for the user-space client
int16_t param; // optional parameter for the specified command
int32_t value; // optional value for the specified command
uint64_t time; // timestamp for this message (clock_get_uptime)
// raw report
uint8_t report[8]; // raw USB report from the device
// processed data
uint16_t buttons8; // buttons (first 8 buttons only, for backwards binary compatibility- use "buttons" field instead)
int16_t axis[6]; // x, y, z, rx, ry, rz
uint16_t address; // USB device address, used to tell one device from the other
uint32_t buttons; // buttons
} ConnexionDeviceState, *ConnexionDeviceStatePtr;
// Size of the above structure:
#define kConnexionDeviceStateSize (sizeof(ConnexionDeviceState))
//==============================================================================
// Device IDs for 3Dconnexion devices with separate and different preferences.
// NOTE: These IDs are no longer internally used by the driver, and the
// ConnexionGetCurrentDevicePrefs API always returns kDevID_AnyDevice in the
// deviceID field. The definitions are kept here for backwards compatibility only.
#define kDevID_AnyDevice 0x7FFF // wildcard used to specify any available device
//==============================================================================
// Device prefs record
// Structure type and current version:
#define kConnexionDevicePrefsType 0x4D50 // 'MP' (Connexion Prefs)
#define kConnexionDevicePrefsVers 0x7031 // 'p1' (version 1)
// This structure is used to retrieve the current device prefs from the helper:
typedef struct {
// header
uint16_t type; // kConnexionDevicePrefsType
uint16_t version; // kConnexionDevicePrefsVers
uint16_t deviceID; // device ID (SpaceNavigator, SpaceNavigatorNB, SpaceExplorer...)
uint16_t reserved1; // set to 0
// target application
uint32_t appSignature; // target application signature
uint32_t reserved2; // set to 0
uint8_t appName[64]; // target application name (Pascal string with length byte at the beginning)
// device preferences
uint8_t mainSpeed; // overall speed
uint8_t zoomOnY; // use Y axis for zoom, Z axis for un/down pan
uint8_t dominant; // only respond to the largest one of all 6 axes values at any given time
uint8_t reserved3; // set to 0
int8_t mapV[6]; // axes mapping when Zoom direction is on vertical axis (zoomOnY = 0)
int8_t mapH[6]; // axes mapping when Zoom direction is on horizontal axis (zoomOnY != 0)
uint8_t enabled[6]; // enable or disable individual axes
uint8_t reversed[6]; // reverse individual axes
uint8_t speed[6]; // speed for individual axes (min 0, max 200, reserved 201-255)
uint8_t sensitivity[6]; // sensitivity for individual axes (min 0, max 200, reserved 201-255)
int32_t scale[6]; // 10000 * scale and "natural" reverse state for individual axes
// added in version 10.0 (build 136)
uint32_t gamma; // 1000 * gamma value used to compute nonlinear axis response, use 1000 (1.0) for linear response
uint32_t intersect; // intersect value used for gamma computations
} ConnexionDevicePrefs, *ConnexionDevicePrefsPtr;
// Size of the above structure:
#define kConnexionDevicePrefsSize (sizeof(ConnexionDevicePrefs))
//==============================================================================
#pragma pack(pop)
//==============================================================================
#ifdef __cplusplus
}
#endif
//==============================================================================
#endif // _H_connexionclient
//==============================================================================

View file

@ -0,0 +1,88 @@
//==============================================================================
#ifndef _H_connexionclientapi
#define _H_connexionclientapi
#include <stdbool.h>
#include "ConnexionClient.h"
//==============================================================================
#ifdef __cplusplus
extern "C" {
#endif
//==============================================================================
// Callback procedure types
typedef void (*ConnexionAddedHandlerProc) (unsigned int productID);
typedef void (*ConnexionRemovedHandlerProc) (unsigned int productID);
typedef void (*ConnexionMessageHandlerProc) (unsigned int productID, unsigned int messageType, void *messageArgument);
// NOTE for ConnexionMessageHandlerProc:
// when messageType == kConnexionMsgDeviceState, messageArgument points to ConnexionDeviceState with size kConnexionDeviceStateSize
// when messageType == kConnexionMsgPrefsChanged, messageArgument points to the target application signature with size sizeof(uint32_t)
//==============================================================================
// Public APIs to be called once when the application starts up or shuts down
int16_t SetConnexionHandlers (ConnexionMessageHandlerProc messageHandler, ConnexionAddedHandlerProc addedHandler, ConnexionRemovedHandlerProc removedHandler, bool useSeparateThread);
void CleanupConnexionHandlers (void);
// Obsolete API replaced by SetConnexionHandlers, will be removed in the future
int16_t InstallConnexionHandlers (ConnexionMessageHandlerProc messageHandler, ConnexionAddedHandlerProc addedHandler, ConnexionRemovedHandlerProc removedHandler);
//==============================================================================
// Public APIs to be called whenever the app wants to start/stop receiving data
// the mask parameter (client capabilities mask) specifies which controls must be forwarded to the client
// buttonMask (previously part of the client capabilities mask) specifies which buttons must be forwarded to the client
uint16_t RegisterConnexionClient (uint32_t signature, uint8_t *name, uint16_t mode, uint32_t mask);
void SetConnexionClientMask (uint16_t clientID, uint32_t mask);
void SetConnexionClientButtonMask (uint16_t clientID, uint32_t buttonMask);
void UnregisterConnexionClient (uint16_t clientID);
//==============================================================================
// Public API to send control commands to the driver and retrieve a result value
// Note: the new ConnexionClientControl variant is strictly required for
// kConnexionCtlSetSwitches and kConnexionCtlClearSwitches but also works for
// all other Control calls. The old variant remains for backwards compatibility.
int16_t ConnexionControl (uint32_t message, int32_t param, int32_t *result);
int16_t ConnexionClientControl (uint16_t clientID, uint32_t message, int32_t param, int32_t *result);
//==============================================================================
// Public API to fetch the current device preferences for either the first connected device or a specific device type (kDevID_Xxx)
int16_t ConnexionGetCurrentDevicePrefs (uint32_t deviceID, ConnexionDevicePrefs *prefs);
//==============================================================================
// Public API to set all button labels in the iOS/Android "virtual device" apps
int16_t ConnexionSetButtonLabels (uint8_t *labels, uint16_t size);
// Labels data is a series of 32 variable-length null-terminated UTF8-encoded strings.
// The sequence of strings follows the SpacePilot Pro button numbering.
// Empty strings revert the button label to its default value.
// As an example, this data would set the label for button Top to "Top" and
// revert all other button labels to their default values:
//
// 0x00, // empty string for Menu
// 0x00, // empty string for Fit
// 0x54, 0x6F, 0x70, 0x00, // utf-8 encoded "Top" string for Top
// 0x00, // empty string for Left
// 0x00, 0x00, 0x00, 0x00, // empty strings for Right, Front, etc...
// 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00
//==============================================================================
#ifdef __cplusplus
}
#endif
//==============================================================================
#endif // _H_connexionclientapi
//==============================================================================

View file

@ -1,3 +1,4 @@
The Mac version does not require any files. The 3D Connexion driver should be installed from http://www.3dconnexion.eu/service/drivers.html
You can copy the header files to the include dir, so that they get compiled in by xcode
For Windows the provided header file is required: include/I3dMouseParams.h
For Windows the provided header file is required: include/I3dMouseParams.h

View file

@ -158,7 +158,7 @@ private:
#else
#include <glm/glm.hpp>
#include "3DconnexionClient/ConnexionClientAPI.h"
#include "ConnexionClientAPI.h"
class ConnexionClient : public QObject {
Q_OBJECT
@ -239,4 +239,4 @@ protected:
AxisStateMap _axisStateMap;
};
#endif // defined(hifi_3DConnexionClient_h)
#endif // defined(hifi_3DConnexionClient_h)

View file

@ -134,6 +134,9 @@ void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const Q
case AssetUpload::FileOpenError:
additionalError = "The file could not be opened. Please check your permissions and try again.";
break;
case AssetUpload::NetworkError:
additionalError = "The file could not be opened. Please check your network connectivity.";
break;
default:
// not handled, do not show a message box
return;

View file

@ -149,28 +149,6 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
glm::vec3 tip = absolutePoses[tipIndex].trans;
float error = glm::length(targetPose.trans - tip);
if (error < ACCEPTABLE_RELATIVE_ERROR) {
if (largestError < error) {
largestError = error;
}
// this targetPose has been met
// finally set the relative rotation of the tip to agree with absolute target rotation
int parentIndex = _skeleton->getParentIndex(tipIndex);
if (parentIndex != -1) {
// compute tip's new parent-relative rotation
// Q = Qp * q --> q' = Qp^ * Q
glm::quat newRelativeRotation = glm::inverse(absolutePoses[parentIndex].rot) * targetPose.rot;
RotationConstraint* constraint = getConstraint(tipIndex);
if (constraint) {
constraint->apply(newRelativeRotation);
// TODO: ATM the final rotation target may fails but we need to provide
// feedback to the IK system so that it can adjust the bones up the skeleton
// to help this rotation target get met.
}
_relativePoses[tipIndex].rot = newRelativeRotation;
}
break;
}
// descend toward root, rotating each joint to get tip closer to target
int index = _skeleton->getParentIndex(tipIndex);
@ -191,6 +169,8 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
// NOTE: even when axisLength is not zero (e.g. lever-arm and pivot-arm are not quite aligned) it is
// still possible for the angle to be zero so we also check that to avoid unnecessary calculations.
if (angle > EPSILON) {
// reduce angle by half: slows convergence but adds stability to IK solution
angle = 0.5f * angle;
glm::quat deltaRotation = glm::angleAxis(angle, axis);
int parentIndex = _skeleton->getParentIndex(index);
@ -268,7 +248,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
loadPoses(underPoses);
} else {
// relax toward underpose
const float RELAXATION_TIMESCALE = 0.25f;
const float RELAXATION_TIMESCALE = 0.125f;
const float alpha = glm::clamp(dt / RELAXATION_TIMESCALE, 0.0f, 1.0f);
int numJoints = (int)_relativePoses.size();
for (int i = 0; i < numJoints; ++i) {
@ -342,7 +322,7 @@ void AnimInverseKinematics::initConstraints() {
// compute corresponding absolute poses
int numJoints = (int)_defaultRelativePoses.size();
AnimPoseVec absolutePoses;
absolutePoses.reserve(numJoints);
absolutePoses.resize(numJoints);
for (int i = 0; i < numJoints; ++i) {
int parentIndex = _skeleton->getParentIndex(i);
if (parentIndex < 0) {
@ -467,11 +447,11 @@ void AnimInverseKinematics::initConstraints() {
} else if (baseName.startsWith("Shoulder", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
const float MAX_SHOULDER_TWIST = PI / 8.0f;
const float MAX_SHOULDER_TWIST = PI / 20.0f;
stConstraint->setTwistLimits(-MAX_SHOULDER_TWIST, MAX_SHOULDER_TWIST);
std::vector<float> minDots;
const float MAX_SHOULDER_SWING = PI / 14.0f;
const float MAX_SHOULDER_SWING = PI / 20.0f;
minDots.push_back(cosf(MAX_SHOULDER_SWING));
stConstraint->setSwingLimits(minDots);
@ -509,7 +489,7 @@ void AnimInverseKinematics::initConstraints() {
// we determine the max/min angles by rotating the swing limit lines from parent- to child-frame
// then measure the angles to swing the yAxis into alignment
glm::vec3 hingeAxis = - mirror * zAxis;
const float MIN_ELBOW_ANGLE = 0.0f;
const float MIN_ELBOW_ANGLE = 0.05f;
const float MAX_ELBOW_ANGLE = 11.0f * PI / 12.0f;
glm::quat invReferenceRotation = glm::inverse(referenceRotation);
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_ELBOW_ANGLE, hingeAxis) * yAxis;

View file

@ -418,9 +418,17 @@ soxr_error_t possibleResampling(soxr_t resampler,
numSourceSamples,
sourceAudioFormat, destinationAudioFormat);
size_t numDestinationSamplesActual = 0;
resampleError = soxr_process(resampler,
channelConversionSamples, numChannelCoversionSamples, NULL,
destinationSamples, numDestinationSamples, NULL);
destinationSamples, numDestinationSamples, &numDestinationSamplesActual);
// return silence instead of playing garbage samples
if (numDestinationSamplesActual < numDestinationSamples) {
unsigned int nBytes = (numDestinationSamples - numDestinationSamplesActual) * destinationAudioFormat.channelCount() * sizeof(int16_t);
memset(&destinationSamples[numDestinationSamplesActual * destinationAudioFormat.channelCount()], 0, nBytes);
qCDebug(audioclient) << "SOXR: padded with" << nBytes << "bytes of silence";
}
delete[] channelConversionSamples;
} else {
@ -433,9 +441,17 @@ soxr_error_t possibleResampling(soxr_t resampler,
numAdjustedDestinationSamples /= 2;
}
size_t numAdjustedDestinationSamplesActual = 0;
resampleError = soxr_process(resampler,
sourceSamples, numAdjustedSourceSamples, NULL,
destinationSamples, numAdjustedDestinationSamples, NULL);
destinationSamples, numAdjustedDestinationSamples, &numAdjustedDestinationSamplesActual);
// return silence instead of playing garbage samples
if (numAdjustedDestinationSamplesActual < numAdjustedDestinationSamples) {
unsigned int nBytes = (numAdjustedDestinationSamples - numAdjustedDestinationSamplesActual) * destinationAudioFormat.channelCount() * sizeof(int16_t);
memset(&destinationSamples[numAdjustedDestinationSamplesActual * destinationAudioFormat.channelCount()], 0, nBytes);
qCDebug(audioclient) << "SOXR: padded with" << nBytes << "bytes of silence";
}
}
return resampleError;

View file

@ -31,23 +31,16 @@ AssetClient::AssetClient() {
static_cast<AssetClient*>(dependency)->deleteLater();
});
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::AssetGetInfoReply, this, "handleAssetGetInfoReply");
packetReceiver.registerMessageListener(PacketType::AssetGetReply, this, "handleAssetGetReply");
packetReceiver.registerListener(PacketType::AssetUploadReply, this, "handleAssetUploadReply");
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &AssetClient::handleNodeKilled);
}
AssetRequest* AssetClient::createRequest(const QString& hash, const QString& extension) {
if (QThread::currentThread() != thread()) {
AssetRequest* req;
QMetaObject::invokeMethod(this, "createRequest",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(AssetRequest*, req),
Q_ARG(QString, hash),
Q_ARG(QString, extension));
return req;
}
if (hash.length() != SHA256_HASH_HEX_LENGTH) {
qDebug() << "Invalid hash size";
return nullptr;
@ -62,19 +55,15 @@ AssetRequest* AssetClient::createRequest(const QString& hash, const QString& ext
return nullptr;
}
return new AssetRequest(this, hash, extension);
auto request = new AssetRequest(hash, extension);
// Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case)
request->moveToThread(thread());
return request;
}
AssetUpload* AssetClient::createUpload(const QString& filename) {
if (QThread::currentThread() != thread()) {
AssetUpload* upload;
QMetaObject::invokeMethod(this, "createUpload",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(AssetUpload*, upload),
Q_ARG(QString, filename));
return upload;
}
auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
@ -83,7 +72,11 @@ AssetUpload* AssetClient::createUpload(const QString& filename) {
return nullptr;
}
return new AssetUpload(this, filename);
auto upload = new AssetUpload(this, filename);
upload->moveToThread(thread());
return upload;
}
bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end,
@ -118,7 +111,7 @@ bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOf
nodeList->sendPacket(std::move(packet), *assetServer);
_pendingRequests[messageID] = callback;
_pendingRequests[assetServer][messageID] = callback;
return true;
}
@ -143,7 +136,7 @@ bool AssetClient::getAssetInfo(const QString& hash, const QString& extension, Ge
nodeList->sendPacket(std::move(packet), *assetServer);
_pendingInfoRequests[messageID] = callback;
_pendingInfoRequests[assetServer][messageID] = callback;
return true;
}
@ -165,9 +158,23 @@ void AssetClient::handleAssetGetInfoReply(QSharedPointer<NLPacket> packet, Share
packet->readPrimitive(&info.size);
}
if (_pendingInfoRequests.contains(messageID)) {
auto callback = _pendingInfoRequests.take(messageID);
callback(error, info);
// Check if we have any pending requests for this node
auto messageMapIt = _pendingInfoRequests.find(senderNode);
if (messageMapIt != _pendingInfoRequests.end()) {
// Found the node, get the MessageID -> Callback map
auto& messageCallbackMap = messageMapIt->second;
// Check if we have this pending request
auto requestIt = messageCallbackMap.find(messageID);
if (requestIt != messageCallbackMap.end()) {
auto callback = requestIt->second;
callback(true, error, info);
messageCallbackMap.erase(requestIt);
}
// Although the messageCallbackMap may now be empty, we won't delete the node until we have disconnected from
// it to avoid constantly creating/deleting the map on subsequent requests.
}
}
@ -194,9 +201,23 @@ void AssetClient::handleAssetGetReply(QSharedPointer<NLPacketList> packetList, S
qDebug() << "Failure getting asset: " << error;
}
if (_pendingRequests.contains(messageID)) {
auto callback = _pendingRequests.take(messageID);
callback(error, data);
// Check if we have any pending requests for this node
auto messageMapIt = _pendingRequests.find(senderNode);
if (messageMapIt != _pendingRequests.end()) {
// Found the node, get the MessageID -> Callback map
auto& messageCallbackMap = messageMapIt->second;
// Check if we have this pending request
auto requestIt = messageCallbackMap.find(messageID);
if (requestIt != messageCallbackMap.end()) {
auto callback = requestIt->second;
callback(true, error, data);
messageCallbackMap.erase(requestIt);
}
// Although the messageCallbackMap may now be empty, we won't delete the node until we have disconnected from
// it to avoid constantly creating/deleting the map on subsequent requests.
}
}
@ -219,7 +240,7 @@ bool AssetClient::uploadAsset(const QByteArray& data, const QString& extension,
nodeList->sendPacketList(std::move(packetList), *assetServer);
_pendingUploads[messageID] = callback;
_pendingUploads[assetServer][messageID] = callback;
return true;
}
@ -244,8 +265,59 @@ void AssetClient::handleAssetUploadReply(QSharedPointer<NLPacket> packet, Shared
qDebug() << "Successfully uploaded asset to asset-server - SHA256 hash is " << hashString;
}
if (_pendingUploads.contains(messageID)) {
auto callback = _pendingUploads.take(messageID);
callback(error, hashString);
// Check if we have any pending requests for this node
auto messageMapIt = _pendingUploads.find(senderNode);
if (messageMapIt != _pendingUploads.end()) {
// Found the node, get the MessageID -> Callback map
auto& messageCallbackMap = messageMapIt->second;
// Check if we have this pending request
auto requestIt = messageCallbackMap.find(messageID);
if (requestIt != messageCallbackMap.end()) {
auto callback = requestIt->second;
callback(true, error, hashString);
messageCallbackMap.erase(requestIt);
}
// Although the messageCallbackMap may now be empty, we won't delete the node until we have disconnected from
// it to avoid constantly creating/deleting the map on subsequent requests.
}
}
void AssetClient::handleNodeKilled(SharedNodePointer node) {
if (node->getType() != NodeType::AssetServer) {
return;
}
{
auto messageMapIt = _pendingRequests.find(node);
if (messageMapIt != _pendingRequests.end()) {
for (const auto& value : messageMapIt->second) {
value.second(false, AssetServerError::NoError, QByteArray());
}
messageMapIt->second.clear();
}
}
{
auto messageMapIt = _pendingInfoRequests.find(node);
if (messageMapIt != _pendingInfoRequests.end()) {
AssetInfo info { "", 0 };
for (const auto& value : messageMapIt->second) {
value.second(false, AssetServerError::NoError, info);
}
messageMapIt->second.clear();
}
}
{
auto messageMapIt = _pendingUploads.find(node);
if (messageMapIt != _pendingUploads.end()) {
for (const auto& value : messageMapIt->second) {
value.second(false, AssetServerError::NoError, "");
}
messageMapIt->second.clear();
}
}
}

View file

@ -20,6 +20,7 @@
#include "AssetUtils.h"
#include "LimitedNodeList.h"
#include "NLPacket.h"
#include "Node.h"
class AssetRequest;
class AssetUpload;
@ -29,9 +30,11 @@ struct AssetInfo {
int64_t size;
};
using ReceivedAssetCallback = std::function<void(AssetServerError serverError, const QByteArray& data)>;
using GetInfoCallback = std::function<void(AssetServerError serverError, AssetInfo info)>;
using UploadResultCallback = std::function<void(AssetServerError serverError, const QString& hash)>;
using ReceivedAssetCallback = std::function<void(bool responseReceived, AssetServerError serverError, const QByteArray& data)>;
using GetInfoCallback = std::function<void(bool responseReceived, AssetServerError serverError, AssetInfo info)>;
using UploadResultCallback = std::function<void(bool responseReceived, AssetServerError serverError, const QString& hash)>;
class AssetClient : public QObject, public Dependency {
Q_OBJECT
@ -46,15 +49,17 @@ private slots:
void handleAssetGetReply(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
void handleAssetUploadReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleNodeKilled(SharedNodePointer node);
private:
bool getAssetInfo(const QString& hash, const QString& extension, GetInfoCallback callback);
bool getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end, ReceivedAssetCallback callback);
bool uploadAsset(const QByteArray& data, const QString& extension, UploadResultCallback callback);
static MessageID _currentID;
QHash<MessageID, ReceivedAssetCallback> _pendingRequests;
QHash<MessageID, GetInfoCallback> _pendingInfoRequests;
QHash<MessageID, UploadResultCallback> _pendingUploads;
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, ReceivedAssetCallback>> _pendingRequests;
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, GetInfoCallback>> _pendingInfoRequests;
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, UploadResultCallback>> _pendingUploads;
friend class AssetRequest;
friend class AssetUpload;

View file

@ -19,8 +19,8 @@
#include "NetworkLogging.h"
#include "NodeList.h"
AssetRequest::AssetRequest(QObject* parent, const QString& hash, const QString& extension) :
QObject(parent),
AssetRequest::AssetRequest(const QString& hash, const QString& extension) :
QObject(),
_hash(hash),
_extension(extension)
{
@ -33,29 +33,40 @@ void AssetRequest::start() {
return;
}
if (_state != NOT_STARTED) {
if (_state != NotStarted) {
qCWarning(networking) << "AssetRequest already started.";
return;
}
_state = WAITING_FOR_INFO;
_state = WaitingForInfo;
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->getAssetInfo(_hash, _extension, [this](AssetServerError serverError, AssetInfo info) {
assetClient->getAssetInfo(_hash, _extension, [this](bool responseReceived, AssetServerError serverError, AssetInfo info) {
_info = info;
if (serverError != AssetServerError::NoError) {
if (!responseReceived) {
_error = NetworkError;
} else if (serverError != AssetServerError::NoError) {
switch(serverError) {
case AssetServerError::AssetNotFound:
_error = NotFound;
break;
default:
_error = UnknownError;
break;
}
}
if (_error != NoError) {
qCDebug(networking) << "Got error retrieving asset info for" << _hash;
_state = FINISHED;
_state = Finished;
emit finished(this);
_error = (serverError == AssetServerError::AssetNotFound) ? NotFound : UnknownError;
return;
}
_state = WAITING_FOR_DATA;
_state = WaitingForData;
_data.resize(info.size);
qCDebug(networking) << "Got size of " << _hash << " : " << info.size << " bytes";
@ -63,23 +74,11 @@ void AssetRequest::start() {
int start = 0, end = _info.size;
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->getAsset(_hash, _extension, start, end, [this, start, end](AssetServerError serverError,
assetClient->getAsset(_hash, _extension, start, end, [this, start, end](bool responseReceived, AssetServerError serverError,
const QByteArray& data) {
Q_ASSERT(data.size() == (end - start));
if (serverError == AssetServerError::NoError) {
// we need to check the hash of the received data to make sure it matches what we expect
if (hashData(data).toHex() == _hash) {
memcpy(_data.data() + start, data.constData(), data.size());
_totalReceived += data.size();
emit progress(_totalReceived, _info.size);
} else {
// hash doesn't match - we have an error
_error = HashVerificationFailed;
}
} else {
if (!responseReceived) {
_error = NetworkError;
} else if (serverError != AssetServerError::NoError) {
switch (serverError) {
case AssetServerError::AssetNotFound:
_error = NotFound;
@ -91,13 +90,26 @@ void AssetRequest::start() {
_error = UnknownError;
break;
}
} else {
Q_ASSERT(data.size() == (end - start));
// we need to check the hash of the received data to make sure it matches what we expect
if (hashData(data).toHex() == _hash) {
memcpy(_data.data() + start, data.constData(), data.size());
_totalReceived += data.size();
emit progress(_totalReceived, _info.size);
} else {
// hash doesn't match - we have an error
_error = HashVerificationFailed;
}
}
if (_error != NoError) {
qCDebug(networking) << "Got error retrieving asset" << _hash << "- error code" << _error;
}
_state = FINISHED;
_state = Finished;
emit finished(this);
});
});

View file

@ -24,10 +24,10 @@ class AssetRequest : public QObject {
Q_OBJECT
public:
enum State {
NOT_STARTED = 0,
WAITING_FOR_INFO,
WAITING_FOR_DATA,
FINISHED
NotStarted = 0,
WaitingForInfo,
WaitingForData,
Finished
};
enum Error {
@ -35,10 +35,11 @@ public:
NotFound,
InvalidByteRange,
HashVerificationFailed,
NetworkError,
UnknownError
};
AssetRequest(QObject* parent, const QString& hash, const QString& extension);
AssetRequest(const QString& hash, const QString& extension);
Q_INVOKABLE void start();
@ -51,7 +52,7 @@ signals:
void progress(qint64 totalReceived, qint64 total);
private:
State _state = NOT_STARTED;
State _state = NotStarted;
Error _error = NoError;
AssetInfo _info;
uint64_t _totalReceived { 0 };

View file

@ -12,9 +12,14 @@
#include "AssetResourceRequest.h"
#include "AssetClient.h"
#include "AssetRequest.h"
#include "AssetUtils.h"
AssetResourceRequest::~AssetResourceRequest() {
if (_assetRequest) {
_assetRequest->deleteLater();
}
}
void AssetResourceRequest::doSend() {
// Make request to atp
auto assetClient = DependencyManager::get<AssetClient>();
@ -31,9 +36,9 @@ void AssetResourceRequest::doSend() {
return;
}
auto request = assetClient->createRequest(hash, extension);
_assetRequest = assetClient->createRequest(hash, extension);
if (!request) {
if (!_assetRequest) {
_result = ServerUnavailable;
_state = Finished;
@ -42,10 +47,11 @@ void AssetResourceRequest::doSend() {
return;
}
connect(request, &AssetRequest::progress, this, &AssetResourceRequest::progress);
QObject::connect(request, &AssetRequest::finished, [this](AssetRequest* req) mutable {
connect(_assetRequest, &AssetRequest::progress, this, &AssetResourceRequest::progress);
QObject::connect(_assetRequest, &AssetRequest::finished, [this](AssetRequest* req) mutable {
Q_ASSERT(_state == InProgress);
Q_ASSERT(req->getState() == AssetRequest::FINISHED);
Q_ASSERT(req == _assetRequest);
Q_ASSERT(req->getState() == AssetRequest::Finished);
switch (req->getError()) {
case AssetRequest::Error::NoError:
@ -55,6 +61,9 @@ void AssetResourceRequest::doSend() {
case AssetRequest::Error::NotFound:
_result = NotFound;
break;
case AssetRequest::Error::NetworkError:
_result = ServerUnavailable;
break;
default:
_result = Error;
break;
@ -62,9 +71,12 @@ void AssetResourceRequest::doSend() {
_state = Finished;
emit finished();
_assetRequest->deleteLater();
_assetRequest = nullptr;
});
request->start();
_assetRequest->start();
}
void AssetResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {

View file

@ -14,18 +14,23 @@
#include <QUrl>
#include "AssetRequest.h"
#include "ResourceRequest.h"
class AssetResourceRequest : public ResourceRequest {
Q_OBJECT
public:
AssetResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { }
~AssetResourceRequest();
protected:
virtual void doSend() override;
private slots:
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private:
AssetRequest* _assetRequest;
};
#endif

View file

@ -43,20 +43,24 @@ void AssetUpload::start() {
qDebug() << "Attempting to upload" << _filename << "to asset-server.";
assetClient->uploadAsset(data, _extension, [this](AssetServerError error, const QString& hash){
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
case AssetServerError::AssetTooLarge:
_error = TooLarge;
break;
case AssetServerError::PermissionDenied:
_error = PermissionDenied;
break;
default:
_error = FileOpenError;
break;
assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){
if (!responseReceived) {
_error = NetworkError;
} else {
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
case AssetServerError::AssetTooLarge:
_error = TooLarge;
break;
case AssetServerError::PermissionDenied:
_error = PermissionDenied;
break;
default:
_error = FileOpenError;
break;
}
}
emit finished(this, hash);
});

View file

@ -28,6 +28,7 @@ public:
enum Error {
NoError = 0,
NetworkError,
Timeout,
TooLarge,
PermissionDenied,

View file

@ -24,7 +24,7 @@ const size_t SHA256_HASH_HEX_LENGTH = 64;
const uint64_t MAX_UPLOAD_SIZE = 1000 * 1000 * 1000; // 1GB
enum AssetServerError : uint8_t {
NoError,
NoError = 0,
AssetNotFound,
InvalidByteRange,
AssetTooLarge,

View file

@ -88,6 +88,16 @@ private:
typedef QSharedPointer<Node> SharedNodePointer;
Q_DECLARE_METATYPE(SharedNodePointer)
namespace std {
template<>
struct hash<SharedNodePointer> {
size_t operator()(const SharedNodePointer& p) const {
// Return the hash of the pointer
return hash<Node*>()(p.data());
}
};
}
QDebug operator<<(QDebug debug, const Node& node);
#endif // hifi_Node_h

View file

@ -34,7 +34,7 @@ void CongestionControl::setPacketSendPeriod(double newSendPeriod) {
}
DefaultCC::DefaultCC() :
_lastRCTime(high_resolution_clock::now()),
_lastRCTime(p_high_resolution_clock::now()),
_slowStartLastAck(_sendCurrSeqNum),
_lastDecreaseMaxSeq(SequenceNumber {SequenceNumber::MAX })
{
@ -54,7 +54,7 @@ void DefaultCC::onACK(SequenceNumber ackNum) {
const double minimumIncrease = 0.01;
// we will only adjust once per sync interval so check that it has been at least that long now
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
if (duration_cast<microseconds>(now - _lastRCTime).count() < synInterval()) {
return;
}

View file

@ -12,11 +12,12 @@
#ifndef hifi_CongestionControl_h
#define hifi_CongestionControl_h
#include <chrono>
#include <memory>
#include <vector>
#include <memory>
#include <PortableHighResolutionClock.h>
#include "LossList.h"
#include "SequenceNumber.h"
@ -107,7 +108,7 @@ public:
private:
void stopSlowStart(); // stops the slow start on loss or timeout
std::chrono::high_resolution_clock::time_point _lastRCTime; // last rate increase time
p_high_resolution_clock::time_point _lastRCTime; // last rate increase time
bool _slowStart { true }; // if in slow start phase
SequenceNumber _slowStartLastAck; // last ACKed seq num
bool _loss { false }; // if loss happened since last rate increase

View file

@ -28,7 +28,7 @@ using namespace udt;
using namespace std::chrono;
Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::unique_ptr<CongestionControl> congestionControl) :
_connectionStart(high_resolution_clock::now()),
_connectionStart(p_high_resolution_clock::now()),
_parentSocket(parentSocket),
_destination(destination),
_congestionControl(move(congestionControl))
@ -154,7 +154,7 @@ void Connection::sync() {
static const int NUM_TIMEOUTS_BEFORE_EXPIRY = 16;
static const int MIN_SECONDS_BEFORE_EXPIRY = 5;
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
auto sincePacketReceive = now - _lastReceiveTime;
@ -185,7 +185,7 @@ void Connection::sync() {
if (_lossList.getLength() > 0) {
// check if we need to re-transmit a loss list
// we do this if it has been longer than the current nakInterval since we last sent
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
if (duration_cast<microseconds>(now - _lastNAKTime).count() >= _nakInterval) {
// Send a timeout NAK packet
@ -197,7 +197,7 @@ void Connection::sync() {
// this most likely means we were started erroneously
// check the start time for this connection and auto expire it after 5 seconds of not receiving or sending any data
static const int CONNECTION_NOT_USED_EXPIRY_SECONDS = 5;
auto secondsSinceStart = duration_cast<seconds>(high_resolution_clock::now() - _connectionStart).count();
auto secondsSinceStart = duration_cast<seconds>(p_high_resolution_clock::now() - _connectionStart).count();
if (secondsSinceStart >= CONNECTION_NOT_USED_EXPIRY_SECONDS) {
// it's been CONNECTION_NOT_USED_EXPIRY_SECONDS and nothing has actually happened with this connection
@ -222,8 +222,8 @@ void Connection::recordRetransmission() {
}
void Connection::sendACK(bool wasCausedBySyncTimeout) {
static high_resolution_clock::time_point lastACKSendTime;
auto currentTime = high_resolution_clock::now();
static p_high_resolution_clock::time_point lastACKSendTime;
auto currentTime = p_high_resolution_clock::now();
SequenceNumber nextACKNumber = nextACK();
Q_ASSERT_X(nextACKNumber >= _lastSentACK, "Connection::sendACK", "Sending lower ACK, something is wrong");
@ -280,7 +280,7 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) {
ackPacket->writePrimitive(estimatedBandwidth);
// record this as the last ACK send time
lastACKSendTime = high_resolution_clock::now();
lastACKSendTime = p_high_resolution_clock::now();
}
// have the socket send off our packet
@ -290,7 +290,7 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) {
"Connection::sendACK", "Adding an invalid ACK to _sentACKs");
// write this ACK to the map of sent ACKs
_sentACKs.push_back({ _currentACKSubSequenceNumber, { nextACKNumber, high_resolution_clock::now() }});
_sentACKs.push_back({ _currentACKSubSequenceNumber, { nextACKNumber, p_high_resolution_clock::now() }});
// reset the number of data packets received since last ACK
_packetsSinceACK = 0;
@ -358,7 +358,7 @@ void Connection::sendNAK(SequenceNumber sequenceNumberRecieved) {
_parentSocket->writeBasePacket(*lossReport, _destination);
// record our last NAK time
_lastNAKTime = high_resolution_clock::now();
_lastNAKTime = p_high_resolution_clock::now();
_stats.record(ConnectionStats::Stats::SentNAK);
}
@ -379,7 +379,7 @@ void Connection::sendTimeoutNAK() {
_parentSocket->writeBasePacket(*lossListPacket, _destination);
// record this as the last NAK time
_lastNAKTime = high_resolution_clock::now();
_lastNAKTime = p_high_resolution_clock::now();
_stats.record(ConnectionStats::Stats::SentTimeoutNAK);
}
@ -403,7 +403,7 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in
_isReceivingData = true;
// mark our last receive time as now (to push the potential expiry farther)
_lastReceiveTime = high_resolution_clock::now();
_lastReceiveTime = p_high_resolution_clock::now();
// check if this is a packet pair we should estimate bandwidth from, or just a regular packet
if (((uint32_t) sequenceNumber & 0xF) == 0) {
@ -533,8 +533,8 @@ void Connection::processACK(std::unique_ptr<ControlPacket> controlPacket) {
// Check if we need send an ACK2 for this ACK
// This will be the case if it has been longer than the sync interval OR
// it looks like they haven't received our ACK2 for this ACK
auto currentTime = high_resolution_clock::now();
static high_resolution_clock::time_point lastACK2SendTime;
auto currentTime = p_high_resolution_clock::now();
static p_high_resolution_clock::time_point lastACK2SendTime;
microseconds sinceLastACK2 = duration_cast<microseconds>(currentTime - lastACK2SendTime);
@ -542,7 +542,7 @@ void Connection::processACK(std::unique_ptr<ControlPacket> controlPacket) {
// Send ACK2 packet
sendACK2(currentACKSubSequenceNumber);
lastACK2SendTime = high_resolution_clock::now();
lastACK2SendTime = p_high_resolution_clock::now();
}
// read the ACKed sequence number
@ -664,7 +664,7 @@ void Connection::processACK2(std::unique_ptr<ControlPacket> controlPacket) {
// update the RTT using the ACK window
// calculate the RTT (time now - time ACK sent)
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
int rtt = duration_cast<microseconds>(now - it->second.second).count();
updateRTT(rtt);
@ -779,13 +779,13 @@ void Connection::resetReceiveState() {
// clear the loss list and _lastNAKTime
_lossList.clear();
_lastNAKTime = high_resolution_clock::time_point();
_lastNAKTime = p_high_resolution_clock::time_point();
// the _nakInterval need not be reset, that will happen on loss
// clear sync variables
_isReceivingData = false;
_connectionStart = high_resolution_clock::now();
_connectionStart = p_high_resolution_clock::now();
_acksDuringSYN = 1;
_lightACKsDuringSYN = 1;

View file

@ -12,12 +12,13 @@
#ifndef hifi_Connection_h
#define hifi_Connection_h
#include <chrono>
#include <list>
#include <memory>
#include <QtCore/QObject>
#include <PortableHighResolutionClock.h>
#include "ConnectionStats.h"
#include "Constants.h"
#include "LossList.h"
@ -51,7 +52,7 @@ private:
class Connection : public QObject {
Q_OBJECT
public:
using SequenceNumberTimePair = std::pair<SequenceNumber, std::chrono::high_resolution_clock::time_point>;
using SequenceNumberTimePair = std::pair<SequenceNumber, p_high_resolution_clock::time_point>;
using ACKListPair = std::pair<SequenceNumber, SequenceNumberTimePair>;
using SentACKList = std::list<ACKListPair>;
@ -113,13 +114,13 @@ private:
int _nakInterval { -1 }; // NAK timeout interval, in microseconds, set on loss
int _minNAKInterval { 100000 }; // NAK timeout interval lower bound, default of 100ms
std::chrono::high_resolution_clock::time_point _lastNAKTime;
p_high_resolution_clock::time_point _lastNAKTime;
bool _hasReceivedHandshake { false }; // flag for receipt of handshake from server
bool _hasReceivedHandshakeACK { false }; // flag for receipt of handshake ACK from client
std::chrono::high_resolution_clock::time_point _connectionStart; // holds the time_point for creation of this connection
std::chrono::high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender
p_high_resolution_clock::time_point _connectionStart; // holds the time_point for creation of this connection
p_high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender
bool _isReceivingData { false }; // flag used for expiry of receipt portion of connection
LossList _lossList; // List of all missing packets

View file

@ -94,7 +94,7 @@ int32_t PacketTimeWindow::getEstimatedBandwidth() const {
void PacketTimeWindow::onPacketArrival() {
// take the current time
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
// record the interval between this packet and the last one
_packetIntervals[_currentPacketInterval++] = duration_cast<microseconds>(now - _lastPacketTime).count();
@ -110,12 +110,12 @@ void PacketTimeWindow::onPacketArrival() {
void PacketTimeWindow::onProbePair1Arrival() {
// take the current time as the first probe time
_firstProbeTime = high_resolution_clock::now();
_firstProbeTime = p_high_resolution_clock::now();
}
void PacketTimeWindow::onProbePair2Arrival() {
// store the interval between the two probes
auto now = high_resolution_clock::now();
auto now = p_high_resolution_clock::now();
_probeIntervals[_currentProbeInterval++] = duration_cast<microseconds>(now - _firstProbeTime).count();

View file

@ -14,9 +14,10 @@
#ifndef hifi_PacketTimeWindow_h
#define hifi_PacketTimeWindow_h
#include <chrono>
#include <vector>
#include <PortableHighResolutionClock.h>
namespace udt {
class PacketTimeWindow {
@ -41,8 +42,8 @@ private:
std::vector<int> _packetIntervals; // vector of microsecond intervals between packet arrivals
std::vector<int> _probeIntervals; // vector of microsecond intervals between probe pair arrivals
std::chrono::high_resolution_clock::time_point _lastPacketTime; // the time_point when last packet arrived
std::chrono::high_resolution_clock::time_point _firstProbeTime; // the time_point when first probe in pair arrived
p_high_resolution_clock::time_point _lastPacketTime; // the time_point when last packet arrived
p_high_resolution_clock::time_point _firstProbeTime; // the time_point when first probe in pair arrived
};
}

View file

@ -272,7 +272,7 @@ void SendQueue::run() {
while (_isRunning) {
// Record how long the loop takes to execute
auto loopStartTimestamp = high_resolution_clock::now();
auto loopStartTimestamp = p_high_resolution_clock::now();
std::unique_lock<std::mutex> handshakeLock { _handshakeMutex };
@ -281,12 +281,12 @@ void SendQueue::run() {
// if it has been at least 100ms since we last sent a handshake, send another now
// hold the time of last send in a static
static auto lastSendHandshake = high_resolution_clock::time_point();
static auto lastSendHandshake = p_high_resolution_clock::time_point();
static const auto HANDSHAKE_RESEND_INTERVAL_MS = std::chrono::milliseconds(100);
// calculation the duration since the last handshake send
auto sinceLastHandshake = std::chrono::duration_cast<std::chrono::milliseconds>(high_resolution_clock::now()
auto sinceLastHandshake = std::chrono::duration_cast<std::chrono::milliseconds>(p_high_resolution_clock::now()
- lastSendHandshake);
if (sinceLastHandshake >= HANDSHAKE_RESEND_INTERVAL_MS) {
@ -295,12 +295,12 @@ void SendQueue::run() {
static auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, 0);
_socket->writeBasePacket(*handshakePacket, _destination);
lastSendHandshake = high_resolution_clock::now();
lastSendHandshake = p_high_resolution_clock::now();
}
// we wait for the ACK or the re-send interval to expire
_handshakeACKCondition.wait_until(handshakeLock,
high_resolution_clock::now()
p_high_resolution_clock::now()
+ HANDSHAKE_RESEND_INTERVAL_MS);
// Once we're here we've either received the handshake ACK or it's going to be time to re-send a handshake.
@ -420,7 +420,7 @@ void SendQueue::run() {
}
}
auto loopEndTimestamp = high_resolution_clock::now();
auto loopEndTimestamp = p_high_resolution_clock::now();
// sleep as long as we need until next packet send, if we can
auto timeToSleep = (loopStartTimestamp + std::chrono::microseconds(_packetSendPeriod)) - loopEndTimestamp;

View file

@ -13,7 +13,6 @@
#define hifi_SendQueue_h
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <list>
@ -24,6 +23,8 @@
#include <QtCore/QObject>
#include <QtCore/QReadWriteLock>
#include <PortableHighResolutionClock.h>
#include "../HifiSockAddr.h"
#include "Constants.h"
@ -44,8 +45,7 @@ class SendQueue : public QObject {
Q_OBJECT
public:
using high_resolution_clock = std::chrono::high_resolution_clock;
using time_point = high_resolution_clock::time_point;
using time_point = p_high_resolution_clock::time_point;
static std::unique_ptr<SendQueue> create(Socket* socket, HifiSockAddr destination);

View file

@ -0,0 +1,30 @@
//
// PortableHighResolutionClock.cpp
// libraries/shared/src
//
// Created by Stephen Birarda on 2015-09-14.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#if defined(_MSC_VER) && _MSC_VER < 1900
#include "PortableHighResolutionClock.h"
namespace {
const long long g_Frequency = []() -> long long {
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
return frequency.QuadPart;
}();
}
win_high_resolution_clock::time_point win_high_resolution_clock::now() {
LARGE_INTEGER count;
QueryPerformanceCounter(&count);
return time_point(duration(count.QuadPart * static_cast<rep>(period::den) / g_Frequency));
}
#endif

View file

@ -0,0 +1,51 @@
//
// PortableHighResolutionClock.h
// libraries/shared/src
//
// Created by Stephen Birarda on 2015-09-14.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// I discovered the low-resolution nature of the VC2012 implementation of std::chrono::high_resolution_clock
// while debugging a UDT issue with packet receive speeds. That lead to
// http://stackoverflow.com/questions/16299029/resolution-of-stdchronohigh-resolution-clock-doesnt-correspond-to-measureme
// which is where the implementation of this class is from.
#pragma once
#ifndef hifi_PortableHighResolutionClock_h
#define hifi_PortableHighResolutionClock_h
#include <chrono>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// The following struct is not compliant with the HF coding standard, but uses underscores to match the classes
// in std::chrono
struct win_high_resolution_clock {
typedef long long rep;
typedef std::nano period;
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<win_high_resolution_clock> time_point;
static const bool is_steady = true;
static time_point now();
};
using p_high_resolution_clock = win_high_resolution_clock;
#else
using p_high_resolution_clock = std::chrono::high_resolution_clock;
#endif
#endif // hifi_PortableHighResolutionClock_h