Merge branch 'master' into 20855

Conflicts:
	interface/resources/qml/ToolWindow.qml
This commit is contained in:
David Rowe 2016-03-24 10:39:09 +13:00
commit 8d778141ef
27 changed files with 276 additions and 141 deletions

View file

@ -77,6 +77,18 @@ void AssetServer::completeSetup() {
auto assetServerObject = settingsObject[ASSET_SERVER_SETTINGS_KEY].toObject();
static const QString MAX_BANDWIDTH_OPTION = "max_bandwidth";
auto maxBandwidthValue = assetServerObject[MAX_BANDWIDTH_OPTION];
auto maxBandwidthFloat = maxBandwidthValue.toDouble(-1);
if (maxBandwidthFloat > 0.0) {
const int BYTES_PER_MEGABITS = (1024 * 1024) / 8;
int maxBandwidth = maxBandwidthFloat * BYTES_PER_MEGABITS;
nodeList->setConnectionMaxBandwidth(maxBandwidth);
qInfo() << "Set maximum bandwith per connection to" << maxBandwidthFloat << "Mb/s."
" (" << maxBandwidth << "bytes/sec)";
}
// get the path to the asset folder from the domain server settings
static const QString ASSETS_PATH_OPTION = "assets_path";
auto assetsJSONValue = assetServerObject[ASSETS_PATH_OPTION];

View file

@ -4,7 +4,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://iweb.dl.sourceforge.net/project/oglplus/oglplus-0.63.x/oglplus-0.63.0.zip
URL http://hifi-public.s3.amazonaws.com/dependencies/oglplus-0.63.0.zip
URL_MD5 de984ab245b185b45c87415c0e052135
CONFIGURE_COMMAND ""
BUILD_COMMAND ""

View file

@ -186,6 +186,15 @@
"help": "The path to the directory assets are stored in.<br/>If this path is relative, it will be relative to the application data directory.<br/>If you change this path you will need to manually copy any existing assets from the previous directory.",
"default": "",
"advanced": true
},
{
"name": "max_bandwidth",
"type": "double",
"label": "Max Bandwidth Per User",
"help": "The maximum upstream bandwidth each user can use (in Mb/s).",
"placeholder": "10.0",
"default": "",
"advanced": true
}
]
},

View file

@ -28,7 +28,6 @@ Window {
visible: false
width: 384; height: 640;
title: "Tools"
property string newTabSource
property alias tabView: tabView
minSize: Qt.vector2d(400, 500)
@ -47,28 +46,23 @@ Window {
property alias y: toolWindow.y
}
property var webTabCreator: Component {
WebView {
id: webView
property string originalUrl;
// Both toolWindow.newTabSource and url can change, so we need
// to store the original url here, without creating any bindings
Component.onCompleted: {
originalUrl = toolWindow.newTabSource;
url = originalUrl;
}
}
}
TabView {
id: tabView;
width: pane.contentWidth
height: pane.scrollHeight // Pane height so that don't use Window's scrollbars otherwise tabs may be scrolled out of view.
onCountChanged: {
if (0 == count) {
toolWindow.visible = false
Repeater {
model: 4
Tab {
active: true
enabled: false;
// we need to store the original url here for future identification
property string originalUrl: "";
onEnabledChanged: toolWindow.updateVisiblity();
WebView {
id: webView;
anchors.fill: parent
}
}
}
@ -140,8 +134,7 @@ Window {
function findIndexForUrl(source) {
for (var i = 0; i < tabView.count; ++i) {
var tab = tabView.getTab(i);
if (tab && tab.item && tab.item.originalUrl &&
tab.item.originalUrl === source) {
if (tab.originalUrl === source) {
return i;
}
}
@ -173,21 +166,32 @@ Window {
}
}
function findFreeTab() {
for (var i = 0; i < tabView.count; ++i) {
var tab = tabView.getTab(i);
if (tab && (!tab.originalUrl || tab.originalUrl === "")) {
return i;
}
}
console.warn("Could not find free tab");
return -1;
}
function removeTabForUrl(source) {
var index = findIndexForUrl(source);
if (index < 0) {
return;
}
var tab = tabView.getTab(index);
tab.enabledChanged.disconnect(updateVisiblity);
tabView.removeTab(index);
console.log("Updating visibility based on child tab removed");
updateVisiblity();
tab.title = "";
tab.originalUrl = "";
tab.enabled = false;
}
function addWebTab(properties) {
if (!properties.source) {
console.warn("Attempted to open Web Tool Pane without URl")
console.warn("Attempted to open Web Tool Pane without URL")
return;
}
@ -197,11 +201,17 @@ Window {
return tabView.getTab(existingTabIndex);
}
var title = properties.title || "Unknown";
newTabSource = properties.source;
var newTab = tabView.addTab(title, webTabCreator);
var freeTabIndex = findFreeTab();
if (freeTabIndex === -1) {
console.warn("Unable to add new tab");
return;
}
var newTab = tabView.getTab(freeTabIndex);
newTab.title = properties.title || "Unknown";
newTab.originalUrl = properties.source;
newTab.item.url = properties.source;
newTab.active = true;
newTab.enabled = false;
if (properties.width) {
tabView.width = Math.min(Math.max(tabView.width, properties.width),

View file

@ -239,11 +239,7 @@ class DeadlockWatchdogThread : public QThread {
public:
static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1;
static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1;
#ifdef DEBUG
static const unsigned long MAX_HEARTBEAT_AGE_USECS = 600 * USECS_PER_SECOND;
#else
static const unsigned long MAX_HEARTBEAT_AGE_USECS = 10 * USECS_PER_SECOND;
#endif
// Set the heartbeat on launch
DeadlockWatchdogThread() {
@ -511,8 +507,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
auto nodeList = DependencyManager::get<NodeList>();
// Set up a watchdog thread to intentionally crash the application on deadlocks
auto deadlockWatchdog = new DeadlockWatchdogThread();
deadlockWatchdog->start();
_deadlockWatchdogThread = new DeadlockWatchdogThread();
_deadlockWatchdogThread->start();
qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion());
@ -586,7 +582,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
ResourceManager::init();
// Make sure we don't time out during slow operations at startup
deadlockWatchdog->updateHeartbeat();
updateHeartbeat();
// Setup MessagesClient
auto messagesClient = DependencyManager::get<MessagesClient>();
@ -734,7 +730,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
initializeGL();
_offscreenContext->makeCurrent();
// Make sure we don't time out during slow operations at startup
deadlockWatchdog->updateHeartbeat();
updateHeartbeat();
// Tell our entity edit sender about our known jurisdictions
_entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
@ -746,7 +742,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_overlays.init(); // do this before scripts load
// Make sure we don't time out during slow operations at startup
deadlockWatchdog->updateHeartbeat();
updateHeartbeat();
connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
@ -900,11 +896,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// do this as late as possible so that all required subsystems are initialized
scriptEngines->loadScripts();
// Make sure we don't time out during slow operations at startup
deadlockWatchdog->updateHeartbeat();
updateHeartbeat();
loadSettings();
// Make sure we don't time out during slow operations at startup
deadlockWatchdog->updateHeartbeat();
updateHeartbeat();
int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now
connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
@ -1019,7 +1015,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
});
// Make sure we don't time out during slow operations at startup
deadlockWatchdog->updateHeartbeat();
updateHeartbeat();
connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);
@ -1060,6 +1056,10 @@ void Application::showCursor(const QCursor& cursor) {
_cursorNeedsChanging = true;
}
void Application::updateHeartbeat() {
static_cast<DeadlockWatchdogThread*>(_deadlockWatchdogThread)->updateHeartbeat();
}
void Application::aboutToQuit() {
emit beforeAboutToQuit();

View file

@ -271,6 +271,8 @@ public slots:
void reloadResourceCaches();
void updateHeartbeat();
void crashApplication();
void deadlockApplication();
@ -505,6 +507,8 @@ private:
mutable QMutex _changeCursorLock { QMutex::Recursive };
QCursor _desiredCursor{ Qt::BlankCursor };
bool _cursorNeedsChanging { false };
QThread* _deadlockWatchdogThread;
};
#endif // hifi_Application_h

View file

@ -968,9 +968,10 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray& data) {
bool hasIdentityChanged = false;
if (skeletonModelURL != _skeletonModelURL) {
if (_firstSkeletonCheck || (skeletonModelURL != _skeletonModelURL)) {
setSkeletonModelURL(skeletonModelURL);
hasIdentityChanged = true;
_firstSkeletonCheck = false;
}
if (displayName != _displayName) {

View file

@ -368,7 +368,8 @@ protected:
HeadData* _headData;
QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit
QUrl _skeletonModelURL;
bool _firstSkeletonCheck { true };
QUrl _skeletonFBXURL;
QVector<AttachmentData> _attachmentData;
QString _displayName;

View file

@ -78,10 +78,14 @@ bool FBXGeometry::convexHullContains(const glm::vec3& point) const {
auto checkEachPrimitive = [=](FBXMesh& mesh, QVector<int> indices, int primitiveSize) -> bool {
// Check whether the point is "behind" all the primitives.
int verticesSize = mesh.vertices.size();
for (int j = 0;
j < indices.size() - 2; // -2 in case the vertices aren't the right size -- we access j + 2 below
j += primitiveSize) {
if (!isPointBehindTrianglesPlane(point,
if (indices[j] < verticesSize &&
indices[j + 1] < verticesSize &&
indices[j + 2] < verticesSize &&
!isPointBehindTrianglesPlane(point,
mesh.vertices[indices[j]],
mesh.vertices[indices[j + 1]],
mesh.vertices[indices[j + 2]])) {

View file

@ -44,12 +44,6 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) {
return true;
}
qWarning() << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
qWarning() << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
qWarning() << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR));
qWarning() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
qWarning() << "Failed to create OffscreenGLCanvas";
return false;
}

View file

@ -187,6 +187,15 @@ bool OffscreenQmlRenderThread::event(QEvent *e) {
void OffscreenQmlRenderThread::setupFbo() {
using namespace oglplus;
_textures.setSize(_size);
// Before making any ogl calls, clear any outstanding errors
// FIXME: Something upstream is polluting the context with a GL_INVALID_ENUM,
// likely from glewExperimental = true
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
qDebug() << "Clearing outstanding GL error to set up QML FBO:" << glewGetErrorString(err);
}
_depthStencil.reset(new Renderbuffer());
Context::Bound(Renderbuffer::Target::Renderbuffer, *_depthStencil)
.Storage(
@ -205,22 +214,19 @@ void OffscreenQmlRenderThread::init() {
connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate);
if (!_canvas.makeCurrent()) {
qWarning("Failed to make context current on render thread");
// Failed to make GL context current, this OffscreenQmlSurface is basically dead
qWarning("Failed to make context current on QML Renderer Thread");
return;
}
_renderControl->initialize(_canvas.getContext());
setupFbo();
_escrow.setRecycler([this](GLuint texture){
_textures.recycleTexture(texture);
});
_canvas.doneCurrent();
}
void OffscreenQmlRenderThread::cleanup() {
if (!_canvas.makeCurrent()) {
qFatal("Failed to make context current on render thread");
return;
}
_renderControl->invalidate();
_fbo.reset();
@ -228,7 +234,6 @@ void OffscreenQmlRenderThread::cleanup() {
_textures.clear();
_canvas.doneCurrent();
_canvas.getContextObject()->moveToThread(QCoreApplication::instance()->thread());
_quit = true;
@ -236,40 +241,33 @@ void OffscreenQmlRenderThread::cleanup() {
void OffscreenQmlRenderThread::resize() {
// Lock _newSize changes
QMutexLocker locker(&_mutex);
{
QMutexLocker locker(&_mutex);
// Update our members
if (_quickWindow) {
_quickWindow->setGeometry(QRect(QPoint(), _newSize));
_quickWindow->contentItem()->setSize(_newSize);
// Update our members
if (_quickWindow) {
_quickWindow->setGeometry(QRect(QPoint(), _newSize));
_quickWindow->contentItem()->setSize(_newSize);
}
// Qt bug in 5.4 forces this check of pixel ratio,
// even though we're rendering offscreen.
qreal pixelRatio = 1.0;
if (_renderControl && _renderControl->_renderWindow) {
pixelRatio = _renderControl->_renderWindow->devicePixelRatio();
}
uvec2 newOffscreenSize = toGlm(_newSize * pixelRatio);
if (newOffscreenSize == _size) {
return;
}
qDebug() << "Offscreen UI resizing to " << _newSize.width() << "x" << _newSize.height() << " with pixel ratio " << pixelRatio;
_size = newOffscreenSize;
}
// Qt bug in 5.4 forces this check of pixel ratio,
// even though we're rendering offscreen.
qreal pixelRatio = 1.0;
if (_renderControl && _renderControl->_renderWindow) {
pixelRatio = _renderControl->_renderWindow->devicePixelRatio();
}
uvec2 newOffscreenSize = toGlm(_newSize * pixelRatio);
_textures.setSize(newOffscreenSize);
if (newOffscreenSize == _size) {
return;
}
_size = newOffscreenSize;
// Clear out any fbos with the old size
if (!_canvas.makeCurrent()) {
qWarning("Failed to make context current on render thread");
return;
}
qDebug() << "Offscreen UI resizing to " << _newSize.width() << "x" << _newSize.height() << " with pixel ratio " << pixelRatio;
locker.unlock();
_textures.setSize(_size);
setupFbo();
_canvas.doneCurrent();
}
void OffscreenQmlRenderThread::render() {
@ -278,11 +276,6 @@ void OffscreenQmlRenderThread::render() {
return;
}
if (!_canvas.makeCurrent()) {
qWarning("Failed to make context current on render thread");
return;
}
QMutexLocker locker(&_mutex);
_renderControl->sync();
_waitCondition.wakeOne();
@ -329,10 +322,10 @@ OffscreenQmlSurface::~OffscreenQmlSurface() {
QObject::disconnect(&_updateTimer);
QObject::disconnect(qApp);
qDebug() << "Stopping QML render thread " << _renderer->currentThreadId();
qDebug() << "Stopping QML Renderer Thread " << _renderer->currentThreadId();
_renderer->_queue.add(STOP);
if (!_renderer->wait(MAX_SHUTDOWN_WAIT_SECS * USECS_PER_SECOND)) {
qWarning() << "Failed to shut down the QML render thread";
qWarning() << "Failed to shut down the QML Renderer Thread";
}
delete _rootItem;
@ -417,7 +410,11 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {
}
QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
_qmlComponent->loadUrl(qmlSource);
// Synchronous loading may take a while; restart the deadlock timer
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
_qmlComponent->loadUrl(qmlSource, QQmlComponent::PreferSynchronous);
if (_qmlComponent->isLoading()) {
connect(_qmlComponent, &QQmlComponent::statusChanged, this,
[this, f](QQmlComponent::Status){

View file

@ -87,18 +87,15 @@ void GLBackend::init() {
static std::once_flag once;
std::call_once(once, [] {
qCDebug(gpulogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
qCDebug(gpulogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
qCDebug(gpulogging) << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR));
qCDebug(gpulogging) << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
glewExperimental = true;
GLenum err = glewInit();
glGetError();
glGetError(); // clear the potential error from glewExperimental
if (GLEW_OK != err) {
/* Problem: glewInit failed, something is seriously wrong. */
// glewInit failed, something is seriously wrong.
qCDebug(gpulogging, "Error: %s\n", glewGetErrorString(err));
}
qCDebug(gpulogging, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));

View file

@ -10,11 +10,74 @@
//
#include "Resource.h"
#include <atomic>
#include <SharedUtil.h>
#include <NumericalConstants.h>
#include <QDebug>
using namespace gpu;
class AllocationDebugger {
public:
void operator+=(size_t size) {
_allocatedMemory += size;
maybeReport();
}
void operator-=(size_t size) {
_allocatedMemory -= size;
maybeReport();
}
private:
QString formatSize(size_t size) {
float num = size;
QStringList list;
list << "KB" << "MB" << "GB" << "TB";
QStringListIterator i(list);
QString unit("bytes");
while (num >= K && i.hasNext()) {
unit = i.next();
num /= K;
}
return QString().setNum(num, 'f', 2) + " " + unit;
}
void maybeReport() {
auto now = usecTimestampNow();
if (now - _lastReportTime < MAX_REPORT_FREQUENCY) {
return;
}
size_t current = _allocatedMemory;
size_t last = _lastReportedMemory;
size_t delta = (current > last) ? (current - last) : (last - current);
if (delta > MIN_REPORT_DELTA) {
_lastReportTime = now;
_lastReportedMemory = current;
qDebug() << "Total allocation " << formatSize(current);
}
}
std::atomic<size_t> _allocatedMemory;
std::atomic<size_t> _lastReportedMemory;
std::atomic<uint64_t> _lastReportTime;
static const float K;
// Report changes of 5 megabytes
static const size_t MIN_REPORT_DELTA = 1024 * 1024 * 5;
// Report changes no more frequently than every 15 seconds
static const uint64_t MAX_REPORT_FREQUENCY = USECS_PER_SECOND * 15;
};
const float AllocationDebugger::K = 1024.0f;
static AllocationDebugger allocationDebugger;
Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) {
allocationDebugger += size;
if ( !dataAllocated ) {
qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer.";
return NOT_ALLOCATED;
@ -38,6 +101,7 @@ Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size)
}
void Resource::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) {
allocationDebugger -= size;
if (dataAllocated) {
delete[] dataAllocated;
}

View file

@ -219,6 +219,8 @@ public:
udt::Socket::StatsVector sampleStatsForAllConnections() { return _nodeSocket.sampleStatsForAllConnections(); }
void setConnectionMaxBandwidth(int maxBandwidth) { _nodeSocket.setConnectionMaxBandwidth(maxBandwidth); }
public slots:
void reset();
void eraseAllNodes();

View file

@ -20,13 +20,19 @@ using namespace std::chrono;
static const double USECS_PER_SECOND = 1000000.0;
void CongestionControl::setMaxBandwidth(int maxBandwidth) {
_maxBandwidth = maxBandwidth;
setPacketSendPeriod(_packetSendPeriod);
}
void CongestionControl::setPacketSendPeriod(double newSendPeriod) {
Q_ASSERT_X(newSendPeriod >= 0, "CongestionControl::setPacketPeriod", "Can not set a negative packet send period");
if (_maxBandwidth > 0) {
auto maxBandwidth = _maxBandwidth.load();
if (maxBandwidth > 0) {
// anytime the packet send period is about to be increased, make sure it stays below the minimum period,
// calculated based on the maximum desired bandwidth
double minPacketSendPeriod = USECS_PER_SECOND / (((double) _maxBandwidth) / _mss);
double minPacketSendPeriod = USECS_PER_SECOND / (((double) maxBandwidth) / _mss);
_packetSendPeriod = std::max(newSendPeriod, minPacketSendPeriod);
} else {
_packetSendPeriod = newSendPeriod;
@ -39,7 +45,7 @@ DefaultCC::DefaultCC() :
_mss = udt::MAX_PACKET_SIZE_WITH_UDP_HEADER;
_congestionWindowSize = 16.0;
_packetSendPeriod = 1.0;
setPacketSendPeriod(1.0);
}
void DefaultCC::onACK(SequenceNumber ackNum) {
@ -73,10 +79,10 @@ void DefaultCC::onACK(SequenceNumber ackNum) {
if (_receiveRate > 0) {
// if we have a valid receive rate we set the send period to whatever the receive rate dictates
_packetSendPeriod = USECS_PER_SECOND / _receiveRate;
setPacketSendPeriod(USECS_PER_SECOND / _receiveRate);
} else {
// no valid receive rate, packet send period is dictated by estimated RTT and current congestion window size
_packetSendPeriod = (_rtt + synInterval()) / _congestionWindowSize;
setPacketSendPeriod((_rtt + synInterval()) / _congestionWindowSize);
}
}
} else {
@ -148,8 +154,8 @@ void DefaultCC::onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {
if (rangeStart > _lastDecreaseMaxSeq) {
_lastDecreasePeriod = _packetSendPeriod;
_packetSendPeriod = ceil(_packetSendPeriod * INTER_PACKET_ARRIVAL_INCREASE);
setPacketSendPeriod(ceil(_packetSendPeriod * INTER_PACKET_ARRIVAL_INCREASE));
// use EWMA to update the average number of NAKs per congestion
static const double NAK_EWMA_ALPHA = 0.125;
@ -175,7 +181,7 @@ void DefaultCC::onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {
// there have been fewer than MAX_DECREASES_PER_CONGESTION_EPOCH AND this NAK matches the random count at which we
// decided we would decrease the packet send period
_packetSendPeriod = ceil(_packetSendPeriod * INTER_PACKET_ARRIVAL_INCREASE);
setPacketSendPeriod(ceil(_packetSendPeriod * INTER_PACKET_ARRIVAL_INCREASE));
_lastDecreaseMaxSeq = _sendCurrSeqNum;
}
}
@ -198,12 +204,12 @@ void DefaultCC::stopSlowStart() {
if (_receiveRate > 0) {
// Set the sending rate to the receiving rate.
_packetSendPeriod = USECS_PER_SECOND / _receiveRate;
setPacketSendPeriod(USECS_PER_SECOND / _receiveRate);
} else {
// If no receiving rate is observed, we have to compute the sending
// rate according to the current window size, and decrease it
// using the method below.
_packetSendPeriod = _congestionWindowSize / (_rtt + synInterval());
setPacketSendPeriod(_congestionWindowSize / (_rtt + synInterval()));
}
}

View file

@ -12,6 +12,7 @@
#ifndef hifi_CongestionControl_h
#define hifi_CongestionControl_h
#include <atomic>
#include <memory>
#include <vector>
#include <memory>
@ -37,6 +38,7 @@ public:
virtual ~CongestionControl() {}
int synInterval() const { return _synInterval; }
void setMaxBandwidth(int maxBandwidth);
virtual void init() {}
virtual void onACK(SequenceNumber ackNum) {}
@ -49,7 +51,6 @@ protected:
void setMSS(int mss) { _mss = mss; }
void setMaxCongestionWindowSize(int window) { _maxCongestionWindowSize = window; }
void setBandwidth(int bandwidth) { _bandwidth = bandwidth; }
void setMaxBandwidth(int maxBandwidth) { _maxBandwidth = maxBandwidth; }
virtual void setInitialSendSequenceNumber(SequenceNumber seqNum) = 0;
void setSendCurrentSequenceNumber(SequenceNumber seqNum) { _sendCurrSeqNum = seqNum; }
void setReceiveRate(int rate) { _receiveRate = rate; }
@ -60,7 +61,7 @@ protected:
double _congestionWindowSize { 16.0 }; // Congestion window size, in packets
int _bandwidth { 0 }; // estimated bandwidth, packets per second
int _maxBandwidth { -1 }; // Maximum desired bandwidth, packets per second
std::atomic<int> _maxBandwidth { -1 }; // Maximum desired bandwidth, bytes per second
double _maxCongestionWindowSize { 0.0 }; // maximum cwnd size, in packets
int _mss { 0 }; // Maximum Packet Size, including all packet headers

View file

@ -80,6 +80,10 @@ void Connection::resetRTT() {
_rttVariance = _rtt / 2;
}
void Connection::setMaxBandwidth(int maxBandwidth) {
_congestionControl->setMaxBandwidth(maxBandwidth);
}
SendQueue& Connection::getSendQueue() {
if (!_sendQueue) {

View file

@ -76,6 +76,8 @@ public:
HifiSockAddr getDestination() const { return _destination; }
void setMaxBandwidth(int maxBandwidth);
signals:
void packetSent();
void connectionInactive(const HifiSockAddr& sockAddr);

View file

@ -176,7 +176,9 @@ Connection& Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
auto it = _connectionsHash.find(sockAddr);
if (it == _connectionsHash.end()) {
auto connection = std::unique_ptr<Connection>(new Connection(this, sockAddr, _ccFactory->create()));
auto congestionControl = _ccFactory->create();
congestionControl->setMaxBandwidth(_maxBandwidth);
auto connection = std::unique_ptr<Connection>(new Connection(this, sockAddr, std::move(congestionControl)));
// we queue the connection to cleanup connection in case it asks for it during its own rate control sync
QObject::connect(connection.get(), &Connection::connectionInactive, this, &Socket::cleanupConnection);
@ -350,6 +352,17 @@ void Socket::setCongestionControlFactory(std::unique_ptr<CongestionControlVirtua
_synInterval = _ccFactory->synInterval();
}
void Socket::setConnectionMaxBandwidth(int maxBandwidth) {
qInfo() << "Setting socket's maximum bandwith to" << maxBandwidth << ". ("
<< _connectionsHash.size() << "live connections)";
_maxBandwidth = maxBandwidth;
for (auto& pair : _connectionsHash) {
auto& connection = pair.second;
connection->setMaxBandwidth(_maxBandwidth);
}
}
ConnectionStats::Stats Socket::sampleStatsForConnection(const HifiSockAddr& destination) {
auto it = _connectionsHash.find(destination);
if (it != _connectionsHash.end()) {

View file

@ -72,6 +72,7 @@ public:
{ _unfilteredHandlers[senderSockAddr] = handler; }
void setCongestionControlFactory(std::unique_ptr<CongestionControlVirtualFactory> ccFactory);
void setConnectionMaxBandwidth(int maxBandwidth);
void messageReceived(std::unique_ptr<Packet> packet);
void messageFailed(Connection* connection, Packet::MessageNumber messageNumber);
@ -109,8 +110,10 @@ private:
std::unordered_map<HifiSockAddr, SequenceNumber> _unreliableSequenceNumbers;
std::unordered_map<HifiSockAddr, std::unique_ptr<Connection>> _connectionsHash;
int _synInterval = 10; // 10ms
QTimer* _synTimer;
int _synInterval { 10 }; // 10ms
QTimer* _synTimer { nullptr };
int _maxBandwidth { -1 };
std::unique_ptr<CongestionControlVirtualFactory> _ccFactory { new CongestionControlFactory<DefaultCC>() };

View file

@ -29,7 +29,7 @@
using namespace std;
static int modelPointerTypeId = qRegisterMetaType<QPointer<Model> >();
static int nakedModelPointerTypeId = qRegisterMetaType<ModelPointer>();
static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<NetworkGeometry> >();
static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
@ -821,21 +821,21 @@ QStringList Model::getJointNames() const {
class Blender : public QRunnable {
public:
Blender(Model* model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
Blender(ModelPointer model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients);
virtual void run();
private:
QPointer<Model> _model;
ModelPointer _model;
int _blendNumber;
QWeakPointer<NetworkGeometry> _geometry;
QVector<FBXMesh> _meshes;
QVector<float> _blendshapeCoefficients;
};
Blender::Blender(Model* model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
Blender::Blender(ModelPointer model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients) :
_model(model),
_blendNumber(blendNumber),
@ -847,7 +847,7 @@ Blender::Blender(Model* model, int blendNumber, const QWeakPointer<NetworkGeomet
void Blender::run() {
PROFILE_RANGE(__FUNCTION__);
QVector<glm::vec3> vertices, normals;
if (!_model.isNull()) {
if (_model) {
int offset = 0;
foreach (const FBXMesh& mesh, _meshes) {
if (mesh.blendshapes.isEmpty()) {
@ -877,7 +877,7 @@ void Blender::run() {
}
// post the result to the geometry cache, which will dispatch to the model if still alive
QMetaObject::invokeMethod(DependencyManager::get<ModelBlender>().data(), "setBlendedVertices",
Q_ARG(const QPointer<Model>&, _model), Q_ARG(int, _blendNumber),
Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber),
Q_ARG(const QWeakPointer<NetworkGeometry>&, _geometry), Q_ARG(const QVector<glm::vec3>&, vertices),
Q_ARG(const QVector<glm::vec3>&, normals));
}
@ -1088,7 +1088,7 @@ float Model::getLimbLength(int jointIndex) const {
bool Model::maybeStartBlender() {
const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
if (fbxGeometry.hasBlendedMeshes()) {
QThreadPool::globalInstance()->start(new Blender(this, ++_blendNumber, _geometry,
QThreadPool::globalInstance()->start(new Blender(getThisPointer(), ++_blendNumber, _geometry,
fbxGeometry.meshes, _blendshapeCoefficients));
return true;
}
@ -1277,20 +1277,25 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) {
}
return;
}
_modelsRequiringBlends.insert(model);
{
Lock lock(_mutex);
_modelsRequiringBlends.insert(model);
}
}
void ModelBlender::setBlendedVertices(const QPointer<Model>& model, int blendNumber,
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber,
const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
if (!model.isNull()) {
if (model) {
model->setBlendedVertices(blendNumber, geometry, vertices, normals);
}
_pendingBlenders--;
while (!_modelsRequiringBlends.empty()) {
auto firstItem = _modelsRequiringBlends.begin();
if (firstItem != _modelsRequiringBlends.end()) {
_modelsRequiringBlends.erase(firstItem);
ModelPointer nextModel = firstItem->lock();
{
Lock lock(_mutex);
for (auto i = _modelsRequiringBlends.begin(); i != _modelsRequiringBlends.end();) {
auto weakPtr = *i;
_modelsRequiringBlends.erase(i++); // remove front of the set
ModelPointer nextModel = weakPtr.lock();
if (nextModel && nextModel->maybeStartBlender()) {
_pendingBlenders++;
return;

View file

@ -384,7 +384,7 @@ protected:
RigPointer _rig;
};
Q_DECLARE_METATYPE(QPointer<Model>)
Q_DECLARE_METATYPE(ModelPointer)
Q_DECLARE_METATYPE(QWeakPointer<NetworkGeometry>)
/// Handle management of pending models that need blending
@ -398,15 +398,19 @@ public:
void noteRequiresBlend(ModelPointer model);
public slots:
void setBlendedVertices(const QPointer<Model>& model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
void setBlendedVertices(ModelPointer model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
private:
using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>;
ModelBlender();
virtual ~ModelBlender();
std::set<ModelWeakPointer, std::owner_less<ModelWeakPointer>> _modelsRequiringBlends;
int _pendingBlenders;
Mutex _mutex;
};

View file

@ -158,6 +158,13 @@ void ScriptEngine::disconnectNonEssentialSignals() {
}
void ScriptEngine::runInThread() {
Q_ASSERT_X(!_isThreaded, "ScriptEngine::runInThread()", "runInThread should not be called more than once");
if (_isThreaded) {
qCWarning(scriptengine) << "ScriptEngine already running in thread: " << getFilename();
return;
}
_isThreaded = true;
QThread* workerThread = new QThread(); // thread is not owned, so we need to manage the delete
QString scriptEngineName = QString("Script Thread:") + getFilename();

View file

@ -82,7 +82,6 @@ void LogHandler::flushRepeatedMessages() {
}
QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& context, const QString& message) {
if (message.isEmpty()) {
return QString();
}

View file

@ -114,7 +114,6 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
}
} else {
auto argumentObject = context->argument(0);
qDebug() << argumentObject.toString();
if (!argumentObject.property(TITLE_PROPERTY).isUndefined()) {
title = argumentObject.property(TITLE_PROPERTY).toString();
}
@ -172,7 +171,6 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
setupServer();
retVal = builder(newTab);
retVal->_toolWindow = true;
offscreenUi->getRootContext()->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
registerObject(url.toLower(), retVal);
return QVariant();
});
@ -330,10 +328,8 @@ void QmlWindowClass::close() {
if (_qmlWindow) {
if (_toolWindow) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto qmlWindow = _qmlWindow;
offscreenUi->executeOnUiThread([=] {
auto toolWindow = offscreenUi->getToolWindow();
offscreenUi->getRootContext()->engine()->setObjectOwnership(qmlWindow, QQmlEngine::JavaScriptOwnership);
auto invokeResult = QMetaObject::invokeMethod(toolWindow, "removeTabForUrl", Qt::DirectConnection,
Q_ARG(QVariant, _source));
Q_ASSERT(invokeResult);

View file

@ -30,7 +30,7 @@ bool OculusBaseDisplayPlugin::isSupported() const {
void OculusBaseDisplayPlugin::customizeContext() {
glewExperimental = true;
GLenum err = glewInit();
glGetError();
glGetError(); // clear the potential error from glewExperimental
Parent::customizeContext();
}

View file

@ -101,7 +101,7 @@ void OpenVrDisplayPlugin::customizeContext() {
std::call_once(once, []{
glewExperimental = true;
GLenum err = glewInit();
glGetError();
glGetError(); // clear the potential error from glewExperimental
});
Parent::customizeContext();
}