mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 04:07:11 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into feat/geocaching
This commit is contained in:
commit
5d306a74d5
44 changed files with 583 additions and 408 deletions
|
@ -369,14 +369,6 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
|
|||
reverbTime = _zoneReverbSettings[i].reverbTime;
|
||||
wetLevel = _zoneReverbSettings[i].wetLevel;
|
||||
|
||||
// Modulate wet level with distance to wall
|
||||
float MIN_ATTENUATION_DISTANCE = 2.0f;
|
||||
float MAX_ATTENUATION = -12; // dB
|
||||
glm::vec3 distanceToWalls = (box.getDimensions() / 2.0f) - glm::abs(streamPosition - box.calcCenter());
|
||||
float distanceToClosestWall = glm::min(distanceToWalls.x, distanceToWalls.z);
|
||||
if (distanceToClosestWall < MIN_ATTENUATION_DISTANCE) {
|
||||
wetLevel += MAX_ATTENUATION * (1.0f - distanceToClosestWall / MIN_ATTENUATION_DISTANCE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
4
cmake/externals/openvr/CMakeLists.txt
vendored
4
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://github.com/ValveSoftware/openvr/archive/v0.9.15.zip
|
||||
URL_MD5 0ff8560b49b6da1150fcc47360e8ceca
|
||||
URL https://github.com/ValveSoftware/openvr/archive/v0.9.19.zip
|
||||
URL_MD5 843f9dde488584d8af1f3ecf2252b4e0
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
|
@ -308,7 +308,7 @@
|
|||
"name": "reverb",
|
||||
"type": "table",
|
||||
"label": "Reverb Settings",
|
||||
"help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet level of -10 db. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet level of -5 db.",
|
||||
"help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet/dry mix of 25%. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet/dry mix of 50%.",
|
||||
"numbered": true,
|
||||
"columns": [
|
||||
{
|
||||
|
@ -325,9 +325,9 @@
|
|||
},
|
||||
{
|
||||
"name": "wet_level",
|
||||
"label": "Wet Level",
|
||||
"label": "Wet/Dry Mix",
|
||||
"can_set": true,
|
||||
"placeholder": "(in db)"
|
||||
"placeholder": "(in percent)"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -451,6 +451,37 @@
|
|||
|
||||
var elPreviewCameraButton = document.getElementById("preview-camera-button");
|
||||
|
||||
var urlUpdaters = document.getElementsByClassName("update-url-version");
|
||||
var PARAM_REGEXP = /(?:\?)(\S+)/; // Check if this has any parameters.
|
||||
var TIMESTAMP_REGEXP = /(&?HFTime=\d+)/;
|
||||
|
||||
var refreshEvent = function(event){
|
||||
var urlElement = event.target.parentElement.getElementsByClassName("url")[0];
|
||||
var content = urlElement.value;
|
||||
var date = new Date();
|
||||
var timeStamp = date.getTime();
|
||||
|
||||
if(content.length > 0){
|
||||
if(PARAM_REGEXP.test(content)){
|
||||
// Has params, so lets remove existing definition and append again.
|
||||
content = content.replace(TIMESTAMP_REGEXP,"") + "&";
|
||||
}else{
|
||||
content += "?";
|
||||
}
|
||||
content = content.replace("?&","?");
|
||||
urlElement.value = content + "HFTime=" + timeStamp;
|
||||
}
|
||||
|
||||
var evt = document.createEvent("HTMLEvents");
|
||||
evt.initEvent("change", true, true );
|
||||
urlElement.dispatchEvent(evt);
|
||||
};
|
||||
|
||||
for(var index = 0; index < urlUpdaters.length; index++){
|
||||
var urlUpdater = urlUpdaters[index];
|
||||
urlUpdater.addEventListener("click", refreshEvent);
|
||||
}
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
var properties;
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
|
@ -1185,6 +1216,7 @@
|
|||
<div class="label">Ambient URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-zone-key-ambient-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1262,6 +1294,7 @@
|
|||
<div class="label">Skybox URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-zone-skybox-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1273,6 +1306,7 @@
|
|||
<div class="label">Source URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-web-source-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1286,12 +1320,14 @@
|
|||
<div class="label">Href - Hifi://address</div>
|
||||
<div class="value">
|
||||
<input id="property-hyperlink-href" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hyperlink-section property">
|
||||
<div class="label">Description</div>
|
||||
<div class="value">
|
||||
<input id="property-hyperlink-description" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1375,16 +1411,19 @@
|
|||
<div class="label">X-axis Texture URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-x-texture-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
|
||||
<div class="label">Y-axis Texture URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-y-texture-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
|
||||
<div class="label">Z-axis Texture URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-z-texture-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1566,6 +1605,7 @@
|
|||
<div class="label">Collision Sound URL</div>
|
||||
<div class="value">
|
||||
<input id="property-collision-sound-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1583,6 +1623,7 @@
|
|||
</div>
|
||||
<div class="value">
|
||||
<input id="property-script-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1595,6 +1636,7 @@
|
|||
<div class="label">Model URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-model-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1613,12 +1655,14 @@
|
|||
<div class="label">Compound Shape URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-compound-shape-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="model-section property">
|
||||
<div class="label">Animation URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-model-animation-url" class="url">
|
||||
<div class="update-url-version"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="model-section property">
|
||||
|
|
|
@ -10,50 +10,11 @@
|
|||
|
||||
var EventBridge;
|
||||
|
||||
EventBridgeConnectionProxy = function(parent) {
|
||||
this.parent = parent;
|
||||
this.realSignal = this.parent.realBridge.scriptEventReceived
|
||||
this.webWindowId = this.parent.webWindow.windowId;
|
||||
}
|
||||
|
||||
EventBridgeConnectionProxy.prototype.connect = function(callback) {
|
||||
var that = this;
|
||||
this.realSignal.connect(function(id, message) {
|
||||
if (id === that.webWindowId) { callback(message); }
|
||||
openEventBridge = function(callback) {
|
||||
new QWebChannel(qt.webChannelTransport, function(channel) {
|
||||
console.log("uid " + EventBridgeUid);
|
||||
EventBridge = channel.objects[EventBridgeUid];
|
||||
callback(EventBridge);
|
||||
});
|
||||
}
|
||||
|
||||
EventBridgeProxy = function(webWindow) {
|
||||
this.webWindow = webWindow;
|
||||
this.realBridge = this.webWindow.eventBridge;
|
||||
this.scriptEventReceived = new EventBridgeConnectionProxy(this);
|
||||
}
|
||||
|
||||
EventBridgeProxy.prototype.emitWebEvent = function(data) {
|
||||
this.realBridge.emitWebEvent(data);
|
||||
}
|
||||
|
||||
openEventBridge = function(callback) {
|
||||
EVENT_BRIDGE_URI = "ws://localhost:51016";
|
||||
socket = new WebSocket(this.EVENT_BRIDGE_URI);
|
||||
|
||||
socket.onclose = function() {
|
||||
console.error("web channel closed");
|
||||
};
|
||||
|
||||
socket.onerror = function(error) {
|
||||
console.error("web channel error: " + error);
|
||||
};
|
||||
|
||||
socket.onopen = function() {
|
||||
channel = new QWebChannel(socket, function(channel) {
|
||||
console.log("Document url is " + document.URL);
|
||||
var webWindow = channel.objects[document.URL.toLowerCase()];
|
||||
console.log("WebWindow is " + webWindow)
|
||||
eventBridgeProxy = new EventBridgeProxy(webWindow);
|
||||
EventBridge = eventBridgeProxy;
|
||||
if (callback) { callback(eventBridgeProxy); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,21 +4,17 @@
|
|||
<script type="text/javascript" src="jquery-2.1.4.min.js"></script>
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
||||
|
||||
<script>
|
||||
var myBridge;
|
||||
|
||||
window.onload = function() {
|
||||
openEventBridge(function(eventBridge) {
|
||||
myBridge = eventBridge;
|
||||
myBridge.scriptEventReceived.connect(function(message) {
|
||||
openEventBridge(function() {
|
||||
EventBridge.scriptEventReceived.connect(function(message) {
|
||||
console.log("HTML side received message: " + message);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
testClick = function() {
|
||||
myBridge.emitWebEvent("HTML side sending message - button click");
|
||||
EventBridge.emitWebEvent(["Foo", "Bar", { "baz": 1} ]);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
|
|
@ -134,8 +134,18 @@ textarea {
|
|||
resize: vertical;
|
||||
}
|
||||
|
||||
.update-url-version{
|
||||
width:17px;
|
||||
height:17px;
|
||||
float:right;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAQAAACRZI9xAAABAUlEQVQoz33RvyvEARjH8SdlUX6UbhFJfgwmE3aTxWEiitxqN/gH7IxCKTcymLiJFIOJsihiUKcrCYnuXoZv+F45n2d4lvfz6fPpCSFs6BO1JllvLmXk5F37dC0vJ5NGmlBxoFp7Rn6RDpRRtG5KtynrijhLoBBGQcllKkFWAXsyCbKEFxeGqmJmFZFLkE639vX+12hCSVft0nUR8RbLcRN/aSHGIqL2tVYPconL32qLtaiL88Rl0qtXc+pTDv1OPGMlidtgS8Wp2RR05FEFM98/GlRQ9mTHvB7jVt2rKGPAT9xh2z6qfnToHV1SjZpN23Tl051di1oco9W/pdttaBRfEhFXOZV7vEsAAAAASUVORK5CYII=);
|
||||
padding:0 !important;
|
||||
margin:0 2px 0 0 !important;
|
||||
}
|
||||
|
||||
input.url {
|
||||
width: 100%;
|
||||
width:85%;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
input.coord {
|
||||
|
|
|
@ -8,26 +8,14 @@ webWindow.eventBridge.webEventReceived.connect(function(data) {
|
|||
print("JS Side event received: " + data);
|
||||
});
|
||||
|
||||
var titles = ["A", "B", "C"];
|
||||
var titleIndex = 0;
|
||||
|
||||
Script.setInterval(function() {
|
||||
webWindow.eventBridge.emitScriptEvent("JS Event sent");
|
||||
var size = webWindow.size;
|
||||
var position = webWindow.position;
|
||||
print("Window url: " + webWindow.url)
|
||||
print("Window visible: " + webWindow.visible)
|
||||
print("Window size: " + size.x + "x" + size.y)
|
||||
print("Window pos: " + position.x + "x" + position.y)
|
||||
webWindow.setVisible(!webWindow.visible);
|
||||
webWindow.setTitle(titles[titleIndex]);
|
||||
webWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100);
|
||||
titleIndex += 1;
|
||||
titleIndex %= titles.length;
|
||||
}, 2 * 1000);
|
||||
var message = [ Math.random(), Math.random() ];
|
||||
print("JS Side sending: " + message);
|
||||
webWindow.emitScriptEvent(message);
|
||||
}, 5 * 1000);
|
||||
|
||||
Script.setTimeout(function() {
|
||||
print("Closing script");
|
||||
Script.scriptEnding.connect(function(){
|
||||
webWindow.close();
|
||||
Script.stop();
|
||||
}, 15 * 1000)
|
||||
webWindow.deleteLater();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtWebEngine 1.1
|
||||
import QtWebChannel 1.0
|
||||
|
||||
import "windows" as Windows
|
||||
import "controls" as Controls
|
||||
|
@ -15,11 +16,22 @@ Windows.Window {
|
|||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false
|
||||
property alias source: webview.url
|
||||
property alias webChannel: webview.webChannel
|
||||
// A unique identifier to let the HTML JS find the event bridge
|
||||
// object (our C++ wrapper)
|
||||
property string uid;
|
||||
|
||||
// This is for JS/QML communication, which is unused in a WebWindow,
|
||||
// but not having this here results in spurious warnings about a
|
||||
// missing signal
|
||||
signal sendToScript(var message);
|
||||
|
||||
Controls.WebView {
|
||||
id: webview
|
||||
url: "about:blank"
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
onUrlChanged: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||
Component.onCompleted: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||
}
|
||||
} // dialog
|
||||
|
|
|
@ -37,14 +37,33 @@ Windows.Window {
|
|||
Repeater {
|
||||
model: 4
|
||||
Tab {
|
||||
// Force loading of the content even if the tab is not visible
|
||||
// (required for letting the C++ code access the webview)
|
||||
active: true
|
||||
enabled: false;
|
||||
// we need to store the original url here for future identification
|
||||
enabled: false
|
||||
property string originalUrl: "";
|
||||
onEnabledChanged: toolWindow.updateVisiblity();
|
||||
|
||||
Controls.WebView {
|
||||
id: webView;
|
||||
// we need to store the original url here for future identification
|
||||
// A unique identifier to let the HTML JS find the event bridge
|
||||
// object (our C++ wrapper)
|
||||
property string uid;
|
||||
anchors.fill: parent
|
||||
enabled: false
|
||||
|
||||
// This is for JS/QML communication, which is unused in a WebWindow,
|
||||
// but not having this here results in spurious warnings about a
|
||||
// missing signal
|
||||
signal sendToScript(var message);
|
||||
|
||||
onUrlChanged: webView.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||
onEnabledChanged: toolWindow.updateVisiblity();
|
||||
onLoadingChanged: {
|
||||
if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
|
||||
webView.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,20 +132,23 @@ Windows.Window {
|
|||
|
||||
var tab = tabView.getTab(index);
|
||||
tab.title = "";
|
||||
tab.originalUrl = "";
|
||||
tab.enabled = false;
|
||||
tab.originalUrl = "";
|
||||
tab.item.url = "about:blank";
|
||||
tab.item.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;
|
||||
}
|
||||
|
||||
var existingTabIndex = findIndexForUrl(properties.source);
|
||||
if (existingTabIndex >= 0) {
|
||||
console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source)
|
||||
return tabView.getTab(existingTabIndex);
|
||||
console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source);
|
||||
var tab = tabView.getTab(existingTabIndex);
|
||||
return tab.item;
|
||||
}
|
||||
|
||||
var freeTabIndex = findFreeTab();
|
||||
|
@ -135,25 +157,22 @@ Windows.Window {
|
|||
return;
|
||||
}
|
||||
|
||||
var newTab = tabView.getTab(freeTabIndex);
|
||||
newTab.title = properties.title || "Unknown";
|
||||
newTab.originalUrl = properties.source;
|
||||
newTab.item.url = properties.source;
|
||||
newTab.active = true;
|
||||
|
||||
if (properties.width) {
|
||||
tabView.width = Math.min(Math.max(tabView.width, properties.width),
|
||||
toolWindow.maxSize.x);
|
||||
tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
|
||||
}
|
||||
|
||||
if (properties.height) {
|
||||
tabView.height = Math.min(Math.max(tabView.height, properties.height),
|
||||
toolWindow.maxSize.y);
|
||||
tabView.height = Math.min(Math.max(tabView.height, properties.height), toolWindow.maxSize.y);
|
||||
}
|
||||
|
||||
console.log("Updating visibility based on child tab added");
|
||||
newTab.enabledChanged.connect(updateVisiblity)
|
||||
updateVisiblity();
|
||||
return newTab
|
||||
var tab = tabView.getTab(freeTabIndex);
|
||||
tab.title = properties.title || "Unknown";
|
||||
tab.enabled = true;
|
||||
tab.originalUrl = properties.source;
|
||||
|
||||
var result = tab.item;
|
||||
result.enabled = true;
|
||||
result.url = properties.source;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ WebEngineView {
|
|||
request.openIn(newWindow.webView)
|
||||
}
|
||||
|
||||
|
||||
profile: desktop.browserProfile
|
||||
// This breaks the webchannel used for passing messages. Fixed in Qt 5.6
|
||||
// See https://bugreports.qt.io/browse/QTBUG-49521
|
||||
//profile: desktop.browserProfile
|
||||
}
|
||||
|
|
|
@ -1415,7 +1415,7 @@ void Application::paintGL() {
|
|||
_lastFramesPerSecondUpdate = now;
|
||||
}
|
||||
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
|
||||
PerformanceTimer perfTimer("paintGL");
|
||||
|
||||
if (nullptr == _displayPlugin) {
|
||||
|
@ -2554,11 +2554,12 @@ void Application::idle(uint64_t now) {
|
|||
return;
|
||||
}
|
||||
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
|
||||
// We're going to execute idle processing, so restart the last idle timer
|
||||
_lastTimeUpdated.start();
|
||||
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
static uint64_t lastIdleStart{ now };
|
||||
uint64_t idleStartToStartDuration = now - lastIdleStart;
|
||||
if (idleStartToStartDuration != 0) {
|
||||
|
@ -3146,6 +3147,9 @@ void Application::updateDialogs(float deltaTime) {
|
|||
}
|
||||
|
||||
void Application::update(float deltaTime) {
|
||||
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1);
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::update()");
|
||||
|
||||
|
@ -3246,9 +3250,13 @@ void Application::update(float deltaTime) {
|
|||
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
|
||||
|
||||
if (_physicsEnabled) {
|
||||
PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
|
||||
PerformanceTimer perfTimer("physics");
|
||||
|
||||
{
|
||||
PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
|
||||
PerformanceTimer perfTimer("updateStates)");
|
||||
static VectorOfMotionStates motionStates;
|
||||
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
|
||||
|
@ -3281,12 +3289,14 @@ void Application::update(float deltaTime) {
|
|||
});
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE_EX("StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("stepSimulation");
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_physicsEngine->stepSimulation();
|
||||
});
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("havestChanges");
|
||||
if (_physicsEngine->hasOutgoingChanges()) {
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
|
@ -3321,17 +3331,24 @@ void Application::update(float deltaTime) {
|
|||
|
||||
qApp->setAvatarSimrateSample(1.0f / deltaTime);
|
||||
|
||||
avatarManager->updateOtherAvatars(deltaTime);
|
||||
{
|
||||
PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
avatarManager->updateOtherAvatars(deltaTime);
|
||||
}
|
||||
|
||||
qApp->updateMyAvatarLookAtPosition();
|
||||
|
||||
// update sensorToWorldMatrix for camera and hand controllers
|
||||
myAvatar->updateSensorToWorldMatrix();
|
||||
|
||||
avatarManager->updateMyAvatar(deltaTime);
|
||||
{
|
||||
PROFILE_RANGE_EX("MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
avatarManager->updateMyAvatar(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE_EX("Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("overlays");
|
||||
_overlays.update(deltaTime);
|
||||
}
|
||||
|
@ -3351,6 +3368,7 @@ void Application::update(float deltaTime) {
|
|||
|
||||
// Update my voxel servers with my current voxel query...
|
||||
{
|
||||
PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
PerformanceTimer perfTimer("queryOctree");
|
||||
quint64 sinceLastQuery = now - _lastQueriedTime;
|
||||
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
|
||||
|
@ -4677,13 +4695,18 @@ qreal Application::getDevicePixelRatio() {
|
|||
}
|
||||
|
||||
DisplayPlugin* Application::getActiveDisplayPlugin() {
|
||||
std::unique_lock<std::recursive_mutex> lock(_displayPluginLock);
|
||||
if (nullptr == _displayPlugin && QThread::currentThread() == thread()) {
|
||||
updateDisplayMode();
|
||||
Q_ASSERT(_displayPlugin);
|
||||
DisplayPlugin* result = nullptr;
|
||||
if (QThread::currentThread() == thread()) {
|
||||
if (nullptr == _displayPlugin) {
|
||||
updateDisplayMode();
|
||||
Q_ASSERT(_displayPlugin);
|
||||
}
|
||||
result = _displayPlugin.get();
|
||||
} else {
|
||||
std::unique_lock<std::mutex> lock(_displayPluginLock);
|
||||
result = _displayPlugin.get();
|
||||
}
|
||||
|
||||
return _displayPlugin.get();
|
||||
return result;
|
||||
}
|
||||
|
||||
const DisplayPlugin* Application::getActiveDisplayPlugin() const {
|
||||
|
@ -4801,20 +4824,26 @@ void Application::updateDisplayMode() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (_displayPlugin) {
|
||||
_displayPlugin->deactivate();
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
// FIXME probably excessive and useless context switching
|
||||
_offscreenContext->makeCurrent();
|
||||
newDisplayPlugin->activate();
|
||||
_offscreenContext->makeCurrent();
|
||||
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
|
||||
_offscreenContext->makeCurrent();
|
||||
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
|
||||
_displayPlugin = newDisplayPlugin;
|
||||
// Make the switch atomic from the perspective of other threads
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_displayPluginLock);
|
||||
|
||||
if (_displayPlugin) {
|
||||
_displayPlugin->deactivate();
|
||||
}
|
||||
|
||||
// FIXME probably excessive and useless context switching
|
||||
_offscreenContext->makeCurrent();
|
||||
newDisplayPlugin->activate();
|
||||
_offscreenContext->makeCurrent();
|
||||
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
|
||||
_offscreenContext->makeCurrent();
|
||||
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
|
||||
_displayPlugin = newDisplayPlugin;
|
||||
}
|
||||
|
||||
|
||||
emit activeDisplayPluginChanged();
|
||||
|
||||
|
|
|
@ -383,7 +383,7 @@ private:
|
|||
|
||||
OffscreenGLCanvas* _offscreenContext { nullptr };
|
||||
DisplayPluginPointer _displayPlugin;
|
||||
std::recursive_mutex _displayPluginLock;
|
||||
std::mutex _displayPluginLock;
|
||||
InputPluginList _activeInputPlugins;
|
||||
|
||||
bool _activatingDisplayPlugin { false };
|
||||
|
|
|
@ -107,7 +107,7 @@ Menu::Menu() {
|
|||
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
// Edit > Stop All Scripts... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0,
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0,
|
||||
scriptEngines.data(), SLOT(stopAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
@ -140,7 +140,7 @@ Menu::Menu() {
|
|||
// Edit > Reload All Content [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
|
||||
// Edit > Package Model... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0,
|
||||
|
@ -153,7 +153,7 @@ Menu::Menu() {
|
|||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
|
||||
// Audio > Mute
|
||||
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false,
|
||||
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false,
|
||||
audioIO.data(), SLOT(toggleMute()));
|
||||
|
||||
// Audio > Show Level Meter
|
||||
|
@ -458,7 +458,7 @@ Menu::Menu() {
|
|||
avatar, SLOT(setEnableMeshVisible(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false,
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, true,
|
||||
avatar, SLOT(setUseAnimPreAndPostRotations(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
|
||||
avatar, SLOT(setEnableInverseKinematics(bool)));
|
||||
|
@ -534,7 +534,7 @@ Menu::Menu() {
|
|||
|
||||
// Developer > Audio >>>
|
||||
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true,
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true,
|
||||
audioIO.data(), SLOT(toggleAudioNoiseReduction()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false,
|
||||
audioIO.data(), SLOT(toggleServerEcho()));
|
||||
|
@ -617,7 +617,7 @@ Menu::Menu() {
|
|||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
|
||||
NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
||||
#endif
|
||||
}
|
||||
|
@ -651,7 +651,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
|
|||
} else if (properties.isCheckable) {
|
||||
menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName,
|
||||
properties.shortcutKeySequence, properties.isChecked,
|
||||
MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()),
|
||||
MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()),
|
||||
requestedPosition, properties.grouping);
|
||||
} else {
|
||||
menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence,
|
||||
|
|
|
@ -187,7 +187,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
|
||||
// simple frustum check
|
||||
float boundingRadius = getBoundingRadius();
|
||||
bool inView = qApp->getViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
|
||||
bool inView = qApp->getDisplayViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
|
||||
|
||||
if (_shouldAnimate && !_shouldSkipRender && inView) {
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "AnimationLogging.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
bool AnimClip::usePreAndPostPoseFromAnim = false;
|
||||
bool AnimClip::usePreAndPostPoseFromAnim = true;
|
||||
|
||||
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) :
|
||||
AnimNode(AnimNode::Type::Clip, id),
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <GeometryUtil.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <DebugDraw.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
|
||||
#include "AnimationLogging.h"
|
||||
#include "AnimClip.h"
|
||||
|
@ -852,6 +853,8 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh
|
|||
|
||||
void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
|
||||
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xffff00ff, 0);
|
||||
|
||||
setModelOffset(rootTransform);
|
||||
|
||||
if (_animNode) {
|
||||
|
|
|
@ -565,10 +565,10 @@ void AudioClient::updateReverbOptions() {
|
|||
_zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
|
||||
reverbChanged = true;
|
||||
}
|
||||
//if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
|
||||
// _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
|
||||
// reverbChanged = true;
|
||||
//}
|
||||
if (_zoneReverbOptions.getWetDryMix() != _receivedAudioStream.getWetLevel()) {
|
||||
_zoneReverbOptions.setWetDryMix(_receivedAudioStream.getWetLevel());
|
||||
reverbChanged = true;
|
||||
}
|
||||
|
||||
if (_reverbOptions != &_zoneReverbOptions) {
|
||||
_reverbOptions = &_zoneReverbOptions;
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <gl/GLWidget.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <gl/Config.h>
|
||||
#include <gl/GLEscrow.h>
|
||||
|
@ -404,7 +404,11 @@ void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overla
|
|||
|
||||
void OpenGLDisplayPlugin::updateTextures() {
|
||||
// FIXME intrduce a GPU wait instead of a CPU/GPU sync point?
|
||||
#if THREADED_PRESENT
|
||||
if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) {
|
||||
#else
|
||||
if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) {
|
||||
#endif
|
||||
updateFrameData();
|
||||
}
|
||||
|
||||
|
@ -527,6 +531,9 @@ void OpenGLDisplayPlugin::internalPresent() {
|
|||
|
||||
void OpenGLDisplayPlugin::present() {
|
||||
incrementPresentCount();
|
||||
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
|
||||
|
||||
updateTextures();
|
||||
if (_currentSceneTexture) {
|
||||
// Write all layers to a local framebuffer
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
#include <gl/OglplusHelpers.h>
|
||||
#include <gl/GLEscrow.h>
|
||||
|
||||
#define THREADED_PRESENT 1
|
||||
#include <gl/GLEscrow.h>
|
||||
|
||||
class OpenGLDisplayPlugin : public DisplayPlugin {
|
||||
protected:
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <gpu/GLBackend.h>
|
||||
#include <CursorManager.h>
|
||||
#include <gl/GLWidget.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
|
||||
#include "../Logging.h"
|
||||
#include "../CompositorHelper.h"
|
||||
|
@ -106,6 +107,9 @@ void HmdDisplayPlugin::compositePointer() {
|
|||
}
|
||||
|
||||
void HmdDisplayPlugin::internalPresent() {
|
||||
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
|
||||
|
||||
// Composite together the scene, overlay and mouse cursor
|
||||
hmdPresent();
|
||||
|
||||
|
@ -149,6 +153,8 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
});
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
postPreview();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
|
||||
|
|
|
@ -31,6 +31,7 @@ public:
|
|||
protected:
|
||||
virtual void hmdPresent() = 0;
|
||||
virtual bool isHmdMounted() const = 0;
|
||||
virtual void postPreview() {};
|
||||
|
||||
void internalActivate() override;
|
||||
void compositeOverlay() override;
|
||||
|
|
|
@ -113,16 +113,18 @@ QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) {
|
|||
return _originalTextures;
|
||||
}
|
||||
|
||||
// TODO: Remove this line and enforce passing a texturemap as stringified JSON
|
||||
QString jsonTextures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}";
|
||||
// Legacy: a ,\n-delimited list of filename:"texturepath"
|
||||
if (*textures.cbegin() != '{') {
|
||||
textures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}";
|
||||
}
|
||||
|
||||
QJsonParseError error;
|
||||
QJsonDocument texturesAsJson = QJsonDocument::fromJson(jsonTextures.toUtf8(), &error);
|
||||
QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << _textures;
|
||||
return _originalTextures;
|
||||
}
|
||||
QJsonObject texturesAsJsonObject = texturesAsJson.object();
|
||||
return texturesAsJsonObject.toVariantMap();
|
||||
return texturesJson.object().toVariantMap();
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::remapTextures() {
|
||||
|
|
|
@ -320,13 +320,12 @@ void ImageReader::run() {
|
|||
}
|
||||
|
||||
QMetaObject::invokeMethod(texture.data(), "setImage",
|
||||
Q_ARG(const QImage&, image),
|
||||
Q_ARG(void*, theTexture),
|
||||
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
||||
QThread::currentThread()->setPriority(originalPriority);
|
||||
}
|
||||
|
||||
void NetworkTexture::setImage(const QImage& image, void* voidTexture, int originalWidth,
|
||||
void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
||||
int originalHeight) {
|
||||
_originalWidth = originalWidth;
|
||||
_originalHeight = originalHeight;
|
||||
|
|
|
@ -136,7 +136,7 @@ protected:
|
|||
|
||||
Q_INVOKABLE void loadContent(const QByteArray& content);
|
||||
// FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on...
|
||||
Q_INVOKABLE void setImage(const QImage& image, void* texture, int originalWidth, int originalHeight);
|
||||
Q_INVOKABLE void setImage(void* texture, int originalWidth, int originalHeight);
|
||||
|
||||
|
||||
private:
|
||||
|
|
|
@ -201,7 +201,7 @@ void DefaultCC::onTimeout() {
|
|||
|
||||
void DefaultCC::stopSlowStart() {
|
||||
_slowStart = false;
|
||||
|
||||
|
||||
if (_receiveRate > 0) {
|
||||
// Set the sending rate to the receiving rate.
|
||||
setPacketSendPeriod(USECS_PER_SECOND / _receiveRate);
|
||||
|
|
|
@ -103,6 +103,7 @@ SendQueue& Connection::getSendQueue() {
|
|||
QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission);
|
||||
QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive);
|
||||
QObject::connect(_sendQueue.get(), &SendQueue::timeout, this, &Connection::queueTimeout);
|
||||
QObject::connect(_sendQueue.get(), &SendQueue::shortCircuitLoss, this, &Connection::queueShortCircuitLoss);
|
||||
|
||||
// set defaults on the send queue from our congestion control object and estimatedTimeout()
|
||||
_sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
||||
|
@ -140,6 +141,12 @@ void Connection::queueTimeout() {
|
|||
});
|
||||
}
|
||||
|
||||
void Connection::queueShortCircuitLoss(quint32 sequenceNumber) {
|
||||
updateCongestionControlAndSendQueue([this, sequenceNumber]{
|
||||
_congestionControl->onLoss(SequenceNumber { sequenceNumber }, SequenceNumber { sequenceNumber });
|
||||
});
|
||||
}
|
||||
|
||||
void Connection::sendReliablePacket(std::unique_ptr<Packet> packet) {
|
||||
Q_ASSERT_X(packet->isReliable(), "Connection::send", "Trying to send an unreliable packet reliably.");
|
||||
getSendQueue().queuePacket(std::move(packet));
|
||||
|
|
|
@ -87,6 +87,7 @@ private slots:
|
|||
void recordRetransmission();
|
||||
void queueInactive();
|
||||
void queueTimeout();
|
||||
void queueShortCircuitLoss(quint32 sequenceNumber);
|
||||
|
||||
private:
|
||||
void sendACK(bool wasCausedBySyncTimeout = true);
|
||||
|
|
|
@ -128,13 +128,13 @@ void SendQueue::stop() {
|
|||
_emptyCondition.notify_one();
|
||||
}
|
||||
|
||||
void SendQueue::sendPacket(const Packet& packet) {
|
||||
_socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination);
|
||||
int SendQueue::sendPacket(const Packet& packet) {
|
||||
return _socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination);
|
||||
}
|
||||
|
||||
void SendQueue::ack(SequenceNumber ack) {
|
||||
// this is a response from the client, re-set our timeout expiry and our last response time
|
||||
_lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch());
|
||||
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
if (_lastACKSequenceNumber == (uint32_t) ack) {
|
||||
return;
|
||||
|
@ -164,7 +164,7 @@ void SendQueue::ack(SequenceNumber ack) {
|
|||
|
||||
void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
||||
// this is a response from the client, re-set our timeout expiry
|
||||
_lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch());
|
||||
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
||||
|
@ -177,8 +177,8 @@ void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
|||
|
||||
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
|
||||
// this is a response from the client, re-set our timeout expiry
|
||||
_lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch());
|
||||
|
||||
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
||||
_naks.clear();
|
||||
|
@ -232,15 +232,16 @@ SequenceNumber SendQueue::getNextSequenceNumber() {
|
|||
return _currentSequenceNumber;
|
||||
}
|
||||
|
||||
void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber) {
|
||||
bool SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber) {
|
||||
// write the sequence number and send the packet
|
||||
newPacket->writeSequenceNumber(sequenceNumber);
|
||||
sendPacket(*newPacket);
|
||||
|
||||
|
||||
// Save packet/payload size before we move it
|
||||
auto packetSize = newPacket->getDataSize();
|
||||
auto payloadSize = newPacket->getPayloadSize();
|
||||
|
||||
auto bytesWritten = sendPacket(*newPacket);
|
||||
|
||||
{
|
||||
// Insert the packet we have just sent in the sent list
|
||||
QWriteLocker locker(&_sentLock);
|
||||
|
@ -249,8 +250,24 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket,
|
|||
entry.second.swap(newPacket);
|
||||
}
|
||||
Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list");
|
||||
|
||||
|
||||
emit packetSent(packetSize, payloadSize);
|
||||
|
||||
if (bytesWritten < 0) {
|
||||
// this is a short-circuit loss - we failed to put this packet on the wire
|
||||
// so immediately add it to the loss list
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
||||
_naks.append(sequenceNumber);
|
||||
}
|
||||
|
||||
emit shortCircuitLoss(quint32(sequenceNumber));
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void SendQueue::run() {
|
||||
|
@ -285,12 +302,14 @@ void SendQueue::run() {
|
|||
auto nextPacketTimestamp = p_high_resolution_clock::now();
|
||||
|
||||
while (_state == State::Running) {
|
||||
bool sentAPacket = maybeResendPacket();
|
||||
bool attemptedToSendPacket = maybeResendPacket();
|
||||
|
||||
// if we didn't find a packet to re-send AND we think we can fit a new packet on the wire
|
||||
// (this is according to the current flow window size) then we send out a new packet
|
||||
if (!sentAPacket) {
|
||||
sentAPacket = maybeSendNewPacket();
|
||||
auto newPacketCount = 0;
|
||||
if (!attemptedToSendPacket) {
|
||||
newPacketCount = maybeSendNewPacket();
|
||||
attemptedToSendPacket = (newPacketCount > 0);
|
||||
}
|
||||
|
||||
// since we're a while loop, give the thread a chance to process events
|
||||
|
@ -300,12 +319,13 @@ void SendQueue::run() {
|
|||
// If the send queue has been innactive, skip the sleep for
|
||||
// Either _isRunning will have been set to false and we'll break
|
||||
// Or something happened and we'll keep going
|
||||
if (_state != State::Running || isInactive(sentAPacket)) {
|
||||
if (_state != State::Running || isInactive(attemptedToSendPacket)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// push the next packet timestamp forwards by the current packet send period
|
||||
nextPacketTimestamp += std::chrono::microseconds(_packetSendPeriod);
|
||||
auto nextPacketDelta = (newPacketCount == 2 ? 2 : 1) * _packetSendPeriod;
|
||||
nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta);
|
||||
|
||||
// sleep as long as we need until next packet send, if we can
|
||||
const auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - p_high_resolution_clock::now());
|
||||
|
@ -314,7 +334,7 @@ void SendQueue::run() {
|
|||
}
|
||||
}
|
||||
|
||||
bool SendQueue::maybeSendNewPacket() {
|
||||
int SendQueue::maybeSendNewPacket() {
|
||||
if (!isFlowWindowFull()) {
|
||||
// we didn't re-send a packet, so time to send a new one
|
||||
|
||||
|
@ -324,38 +344,43 @@ bool SendQueue::maybeSendNewPacket() {
|
|||
// grab the first packet we will send
|
||||
std::unique_ptr<Packet> firstPacket = _packets.takePacket();
|
||||
Q_ASSERT(firstPacket);
|
||||
|
||||
std::unique_ptr<Packet> secondPacket;
|
||||
bool shouldSendPairTail = false;
|
||||
|
||||
if (((uint32_t) nextNumber & 0xF) == 0) {
|
||||
// the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets
|
||||
// pull off a second packet if we can before we unlock
|
||||
shouldSendPairTail = true;
|
||||
|
||||
secondPacket = _packets.takePacket();
|
||||
|
||||
|
||||
// attempt to send the first packet
|
||||
if (sendNewPacketAndAddToSentList(move(firstPacket), nextNumber)) {
|
||||
std::unique_ptr<Packet> secondPacket;
|
||||
bool shouldSendPairTail = false;
|
||||
|
||||
if (((uint32_t) nextNumber & 0xF) == 0) {
|
||||
// the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets
|
||||
// pull off a second packet if we can before we unlock
|
||||
shouldSendPairTail = true;
|
||||
|
||||
secondPacket = _packets.takePacket();
|
||||
}
|
||||
|
||||
// do we have a second in a pair to send as well?
|
||||
if (secondPacket) {
|
||||
sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber());
|
||||
} else if (shouldSendPairTail) {
|
||||
// we didn't get a second packet to send in the probe pair
|
||||
// send a control packet of type ProbePairTail so the receiver can still do
|
||||
// proper bandwidth estimation
|
||||
static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail);
|
||||
_socket->writeBasePacket(*pairTailPacket, _destination);
|
||||
}
|
||||
|
||||
// we attempted to send two packets, return 2
|
||||
return 2;
|
||||
} else {
|
||||
// we attempted to send a single packet, return 1
|
||||
return 1;
|
||||
}
|
||||
|
||||
// definitely send the first packet
|
||||
sendNewPacketAndAddToSentList(move(firstPacket), nextNumber);
|
||||
|
||||
// do we have a second in a pair to send as well?
|
||||
if (secondPacket) {
|
||||
sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber());
|
||||
} else if (shouldSendPairTail) {
|
||||
// we didn't get a second packet to send in the probe pair
|
||||
// send a control packet of type ProbePairTail so the receiver can still do
|
||||
// proper bandwidth estimation
|
||||
static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail);
|
||||
_socket->writeBasePacket(*pairTailPacket, _destination);
|
||||
}
|
||||
|
||||
// We sent our packet(s), return here
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No packets were sent
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SendQueue::maybeResendPacket() {
|
||||
|
@ -375,8 +400,9 @@ bool SendQueue::maybeResendPacket() {
|
|||
|
||||
// see if we can find the packet to re-send
|
||||
auto it = _sentPackets.find(resendNumber);
|
||||
|
||||
|
||||
if (it != _sentPackets.end()) {
|
||||
|
||||
auto& entry = it->second;
|
||||
// we found the packet - grab it
|
||||
auto& resendPacket = *(entry.second);
|
||||
|
@ -437,7 +463,7 @@ bool SendQueue::maybeResendPacket() {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool SendQueue::isInactive(bool sentAPacket) {
|
||||
bool SendQueue::isInactive(bool attemptedToSendPacket) {
|
||||
// check for connection timeout first
|
||||
|
||||
// that will be the case if we have had 16 timeouts since hearing back from the client, and it has been
|
||||
|
@ -447,7 +473,8 @@ bool SendQueue::isInactive(bool sentAPacket) {
|
|||
|
||||
auto sinceLastResponse = (QDateTime::currentMSecsSinceEpoch() - _lastReceiverResponse);
|
||||
|
||||
if (sinceLastResponse >= quint64(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) &&
|
||||
if (sinceLastResponse > 0 &&
|
||||
sinceLastResponse >= int64_t(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) &&
|
||||
_lastReceiverResponse > 0 &&
|
||||
sinceLastResponse > MIN_MS_BEFORE_INACTIVE) {
|
||||
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
|
||||
|
@ -462,7 +489,7 @@ bool SendQueue::isInactive(bool sentAPacket) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!sentAPacket) {
|
||||
if (!attemptedToSendPacket) {
|
||||
// During our processing above we didn't send any packets
|
||||
|
||||
// If that is still the case we should use a condition_variable_any to sleep until we have data to handle.
|
||||
|
|
|
@ -79,6 +79,7 @@ signals:
|
|||
|
||||
void queueInactive();
|
||||
|
||||
void shortCircuitLoss(quint32 sequenceNumber);
|
||||
void timeout();
|
||||
|
||||
private slots:
|
||||
|
@ -91,13 +92,13 @@ private:
|
|||
|
||||
void sendHandshake();
|
||||
|
||||
void sendPacket(const Packet& packet);
|
||||
void sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber);
|
||||
int sendPacket(const Packet& packet);
|
||||
bool sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber);
|
||||
|
||||
bool maybeSendNewPacket(); // Figures out what packet to send next
|
||||
int maybeSendNewPacket(); // Figures out what packet to send next
|
||||
bool maybeResendPacket(); // Determines whether to resend a packet and which one
|
||||
|
||||
bool isInactive(bool sentAPacket);
|
||||
bool isInactive(bool attemptedToSendPacket);
|
||||
void deactivate(); // makes the queue inactive and cleans it up
|
||||
|
||||
bool isFlowWindowFull() const;
|
||||
|
@ -122,7 +123,7 @@ private:
|
|||
|
||||
std::atomic<int> _estimatedTimeout { 0 }; // Estimated timeout, set from CC
|
||||
std::atomic<int> _syncInterval { udt::DEFAULT_SYN_INTERVAL_USECS }; // Sync interval, set from CC
|
||||
std::atomic<uint64_t> _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK)
|
||||
std::atomic<int64_t> _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK)
|
||||
|
||||
std::atomic<int> _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render
|
|||
void CullSpatialSelection::configure(const Config& config) {
|
||||
_justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum);
|
||||
_freezeFrustum = config.freezeFrustum;
|
||||
_skipCulling = config.skipCulling;
|
||||
}
|
||||
|
||||
void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext,
|
||||
|
@ -191,60 +192,112 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re
|
|||
// visibility cull if partially selected ( octree cell contianing it was partial)
|
||||
// distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item)
|
||||
|
||||
// inside & fit items: easy, just filter
|
||||
{
|
||||
PerformanceTimer perfTimer("insideFitItems");
|
||||
for (auto id : inSelection.insideItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// inside & subcell items: filter & distance cull
|
||||
{
|
||||
PerformanceTimer perfTimer("insideSmallItems");
|
||||
for (auto id : inSelection.insideSubcellItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
if (test.solidAngleTest(itemBound.bound)) {
|
||||
if (_skipCulling) {
|
||||
// inside & fit items: filter only, culling is disabled
|
||||
{
|
||||
PerformanceTimer perfTimer("insideFitItems");
|
||||
for (auto id : inSelection.insideItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// partial & fit items: filter & frustum cull
|
||||
{
|
||||
PerformanceTimer perfTimer("partialFitItems");
|
||||
for (auto id : inSelection.partialItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
if (test.frustumTest(itemBound.bound)) {
|
||||
// inside & subcell items: filter only, culling is disabled
|
||||
{
|
||||
PerformanceTimer perfTimer("insideSmallItems");
|
||||
for (auto id : inSelection.insideSubcellItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// partial & subcell items:: filter & frutum cull & solidangle cull
|
||||
{
|
||||
PerformanceTimer perfTimer("partialSmallItems");
|
||||
for (auto id : inSelection.partialSubcellItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
if (test.frustumTest(itemBound.bound)) {
|
||||
// partial & fit items: filter only, culling is disabled
|
||||
{
|
||||
PerformanceTimer perfTimer("partialFitItems");
|
||||
for (auto id : inSelection.partialItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// partial & subcell items: filter only, culling is disabled
|
||||
{
|
||||
PerformanceTimer perfTimer("partialSmallItems");
|
||||
for (auto id : inSelection.partialSubcellItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// inside & fit items: easy, just filter
|
||||
{
|
||||
PerformanceTimer perfTimer("insideFitItems");
|
||||
for (auto id : inSelection.insideItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// inside & subcell items: filter & distance cull
|
||||
{
|
||||
PerformanceTimer perfTimer("insideSmallItems");
|
||||
for (auto id : inSelection.insideSubcellItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
if (test.solidAngleTest(itemBound.bound)) {
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// partial & fit items: filter & frustum cull
|
||||
{
|
||||
PerformanceTimer perfTimer("partialFitItems");
|
||||
for (auto id : inSelection.partialItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
if (test.frustumTest(itemBound.bound)) {
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// partial & subcell items:: filter & frutum cull & solidangle cull
|
||||
{
|
||||
PerformanceTimer perfTimer("partialSmallItems");
|
||||
for (auto id : inSelection.partialSubcellItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
if (test.frustumTest(itemBound.bound)) {
|
||||
if (test.solidAngleTest(itemBound.bound)) {
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
details._rendered += (int)outItems.size();
|
||||
|
|
|
@ -70,14 +70,16 @@ namespace render {
|
|||
Q_OBJECT
|
||||
Q_PROPERTY(int numItems READ getNumItems)
|
||||
Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum)
|
||||
Q_PROPERTY(bool skipCulling MEMBER skipCulling WRITE setSkipCulling)
|
||||
public:
|
||||
int numItems{ 0 };
|
||||
int getNumItems() { return numItems; }
|
||||
|
||||
bool freezeFrustum{ false };
|
||||
bool skipCulling{ false };
|
||||
public slots:
|
||||
void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); }
|
||||
|
||||
void setSkipCulling(bool enabled) { skipCulling = enabled; emit dirty(); }
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
@ -85,6 +87,7 @@ namespace render {
|
|||
class CullSpatialSelection {
|
||||
bool _freezeFrustum{ false }; // initialized by Config
|
||||
bool _justFrozeFrustum{ false };
|
||||
bool _skipCulling{ false };
|
||||
ViewFrustum _frozenFrutstum;
|
||||
public:
|
||||
using Config = CullSpatialSelectionConfig;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "NsightHelpers.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(NSIGHT_FOUND)
|
||||
#include "nvToolsExt.h"
|
||||
|
||||
|
@ -15,8 +16,28 @@ ProfileRange::ProfileRange(const char *name) {
|
|||
nvtxRangePush(name);
|
||||
}
|
||||
|
||||
ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {
|
||||
|
||||
nvtxEventAttributes_t eventAttrib = {0};
|
||||
eventAttrib.version = NVTX_VERSION;
|
||||
eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
|
||||
eventAttrib.colorType = NVTX_COLOR_ARGB;
|
||||
eventAttrib.color = argbColor;
|
||||
eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
|
||||
eventAttrib.message.ascii = name;
|
||||
eventAttrib.payload.llValue = payload;
|
||||
eventAttrib.payloadType = NVTX_PAYLOAD_TYPE_UNSIGNED_INT64;
|
||||
|
||||
nvtxRangePushEx(&eventAttrib);
|
||||
}
|
||||
|
||||
ProfileRange::~ProfileRange() {
|
||||
nvtxRangePop();
|
||||
}
|
||||
|
||||
#else
|
||||
ProfileRange::ProfileRange(const char *name) {}
|
||||
ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {}
|
||||
ProfileRange::~ProfileRange() {}
|
||||
#endif
|
||||
#endif // _WIN32
|
||||
|
|
|
@ -9,16 +9,21 @@
|
|||
#ifndef hifi_gl_NsightHelpers_h
|
||||
#define hifi_gl_NsightHelpers_h
|
||||
|
||||
#if defined(NSIGHT_FOUND)
|
||||
class ProfileRange {
|
||||
public:
|
||||
ProfileRange(const char *name);
|
||||
~ProfileRange();
|
||||
};
|
||||
#ifdef _WIN32
|
||||
#include <stdint.h>
|
||||
|
||||
class ProfileRange {
|
||||
public:
|
||||
ProfileRange(const char *name);
|
||||
ProfileRange(const char *name, uint32_t argbColor, uint64_t payload);
|
||||
~ProfileRange();
|
||||
};
|
||||
|
||||
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
|
||||
#define PROFILE_RANGE_EX(name, argbColor, payload) ProfileRange profileRangeThis(name, argbColor, payload);
|
||||
#else
|
||||
#define PROFILE_RANGE(name)
|
||||
#define PROFILE_RANGE_EX(name, argbColor, payload)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <QtQml/QQmlContext>
|
||||
|
||||
#include <QtWebChannel/QWebChannel>
|
||||
|
||||
#include <QtScript/QScriptContext>
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
|
@ -31,19 +33,39 @@ static const char* const URL_PROPERTY = "source";
|
|||
// Method called by Qt scripts to create a new web window in the overlay
|
||||
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||
return QmlWindowClass::internalConstructor("QmlWebWindow.qml", context, engine,
|
||||
[&](QObject* object) { return new QmlWebWindowClass(object); });
|
||||
[&](QObject* object) { return new QmlWebWindowClass(object); });
|
||||
}
|
||||
|
||||
QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) {
|
||||
_uid = QUuid::createUuid().toString();
|
||||
asQuickItem()->setProperty("uid", _uid);
|
||||
auto webchannelVar = qmlWindow->property("webChannel");
|
||||
_webchannel = qvariant_cast<QWebChannel*>(webchannelVar);
|
||||
Q_ASSERT(_webchannel);
|
||||
_webchannel->registerObject(_uid, this);
|
||||
}
|
||||
|
||||
void QmlWebWindowClass::emitScriptEvent(const QVariant& scriptMessage) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, scriptMessage));
|
||||
} else {
|
||||
emit scriptEventReceived(scriptMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME remove.
|
||||
void QmlWebWindowClass::handleNavigation(const QString& url) {
|
||||
void QmlWebWindowClass::emitWebEvent(const QVariant& webMessage) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage));
|
||||
} else {
|
||||
emit webEventReceived(webMessage);
|
||||
}
|
||||
}
|
||||
|
||||
QString QmlWebWindowClass::getURL() const {
|
||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||
if (_qmlWindow.isNull()) {
|
||||
return QVariant();
|
||||
}
|
||||
return _qmlWindow->property(URL_PROPERTY);
|
||||
});
|
||||
return result.toString();
|
||||
|
@ -54,6 +76,8 @@ extern QString fixupHifiUrl(const QString& urlString);
|
|||
|
||||
void QmlWebWindowClass::setURL(const QString& urlString) {
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
_qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString));
|
||||
if (!_qmlWindow.isNull()) {
|
||||
_qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,10 +11,13 @@
|
|||
|
||||
#include "QmlWindowClass.h"
|
||||
|
||||
class QWebChannel;
|
||||
|
||||
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
||||
class QmlWebWindowClass : public QmlWindowClass {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString url READ getURL CONSTANT)
|
||||
Q_PROPERTY(QString uid READ getUid CONSTANT)
|
||||
|
||||
public:
|
||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
||||
|
@ -23,12 +26,18 @@ public:
|
|||
public slots:
|
||||
QString getURL() const;
|
||||
void setURL(const QString& url);
|
||||
const QString& getUid() const { return _uid; }
|
||||
void emitScriptEvent(const QVariant& scriptMessage);
|
||||
void emitWebEvent(const QVariant& webMessage);
|
||||
|
||||
signals:
|
||||
void urlChanged();
|
||||
void scriptEventReceived(const QVariant& message);
|
||||
void webEventReceived(const QVariant& message);
|
||||
|
||||
private slots:
|
||||
void handleNavigation(const QString& url);
|
||||
private:
|
||||
QString _uid;
|
||||
QWebChannel* _webchannel { nullptr };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,10 +26,6 @@
|
|||
|
||||
#include "OffscreenUi.h"
|
||||
|
||||
QWebSocketServer* QmlWindowClass::_webChannelServer { nullptr };
|
||||
static QWebChannel webChannel;
|
||||
static const uint16_t WEB_CHANNEL_PORT = 51016;
|
||||
static std::atomic<int> nextWindowId;
|
||||
static const char* const SOURCE_PROPERTY = "source";
|
||||
static const char* const TITLE_PROPERTY = "title";
|
||||
static const char* const WIDTH_PROPERTY = "width";
|
||||
|
@ -37,54 +33,6 @@ static const char* const HEIGHT_PROPERTY = "height";
|
|||
static const char* const VISIBILE_PROPERTY = "visible";
|
||||
static const char* const TOOLWINDOW_PROPERTY = "toolWindow";
|
||||
|
||||
void QmlScriptEventBridge::emitWebEvent(const QString& data) {
|
||||
QMetaObject::invokeMethod(this, "webEventReceived", Qt::QueuedConnection, Q_ARG(QString, data));
|
||||
}
|
||||
|
||||
void QmlScriptEventBridge::emitScriptEvent(const QString& data) {
|
||||
QMetaObject::invokeMethod(this, "scriptEventReceived", Qt::QueuedConnection,
|
||||
Q_ARG(int, _webWindow->getWindowId()), Q_ARG(QString, data));
|
||||
}
|
||||
|
||||
class QmlWebTransport : public QWebChannelAbstractTransport {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QmlWebTransport(QWebSocket* webSocket) : _webSocket(webSocket) {
|
||||
// Translate from the websocket layer to the webchannel layer
|
||||
connect(webSocket, &QWebSocket::textMessageReceived, [this](const QString& message) {
|
||||
QJsonParseError error;
|
||||
QJsonDocument document = QJsonDocument::fromJson(message.toUtf8(), &error);
|
||||
if (error.error || !document.isObject()) {
|
||||
qWarning() << "Unable to parse incoming JSON message" << message;
|
||||
return;
|
||||
}
|
||||
emit messageReceived(document.object(), this);
|
||||
});
|
||||
}
|
||||
|
||||
virtual void sendMessage(const QJsonObject &message) override {
|
||||
// Translate from the webchannel layer to the websocket layer
|
||||
_webSocket->sendTextMessage(QJsonDocument(message).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
|
||||
private:
|
||||
QWebSocket* const _webSocket;
|
||||
};
|
||||
|
||||
|
||||
void QmlWindowClass::setupServer() {
|
||||
if (!_webChannelServer) {
|
||||
_webChannelServer = new QWebSocketServer("EventBridge Server", QWebSocketServer::NonSecureMode);
|
||||
if (!_webChannelServer->listen(QHostAddress::LocalHost, WEB_CHANNEL_PORT)) {
|
||||
qFatal("Failed to open web socket server.");
|
||||
}
|
||||
|
||||
QObject::connect(_webChannelServer, &QWebSocketServer::newConnection, [] {
|
||||
webChannel.connectTo(new QmlWebTransport(_webChannelServer->nextPendingConnection()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
||||
QScriptContext* context, QScriptEngine* engine,
|
||||
std::function<QmlWindowClass*(QObject*)> builder)
|
||||
|
@ -168,10 +116,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
|||
}
|
||||
|
||||
offscreenUi->returnFromUiThread([&] {
|
||||
setupServer();
|
||||
retVal = builder(newTab);
|
||||
retVal->_toolWindow = true;
|
||||
registerObject(url.toLower(), retVal);
|
||||
return QVariant();
|
||||
});
|
||||
} else {
|
||||
|
@ -179,10 +125,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
|||
QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, qmlSource),
|
||||
Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) {
|
||||
setupServer();
|
||||
retVal = builder(object);
|
||||
context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
|
||||
registerObject(url.toLower(), retVal);
|
||||
if (!title.isEmpty()) {
|
||||
retVal->setTitle(title);
|
||||
}
|
||||
|
@ -209,12 +153,9 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine*
|
|||
});
|
||||
}
|
||||
|
||||
QmlWindowClass::QmlWindowClass(QObject* qmlWindow)
|
||||
: _windowId(++nextWindowId), _qmlWindow(qmlWindow)
|
||||
{
|
||||
qDebug() << "Created window with ID " << _windowId;
|
||||
QmlWindowClass::QmlWindowClass(QObject* qmlWindow) : _qmlWindow(qmlWindow) {
|
||||
Q_ASSERT(_qmlWindow);
|
||||
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow));
|
||||
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
|
||||
// Forward messages received from QML on to the script
|
||||
connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection);
|
||||
}
|
||||
|
@ -228,19 +169,11 @@ QmlWindowClass::~QmlWindowClass() {
|
|||
close();
|
||||
}
|
||||
|
||||
void QmlWindowClass::registerObject(const QString& name, QObject* object) {
|
||||
webChannel.registerObject(name, object);
|
||||
}
|
||||
|
||||
void QmlWindowClass::deregisterObject(QObject* object) {
|
||||
webChannel.deregisterObject(object);
|
||||
}
|
||||
|
||||
QQuickItem* QmlWindowClass::asQuickItem() const {
|
||||
if (_toolWindow) {
|
||||
return DependencyManager::get<OffscreenUi>()->getToolWindow();
|
||||
}
|
||||
return dynamic_cast<QQuickItem*>(_qmlWindow);
|
||||
return _qmlWindow.isNull() ? nullptr : dynamic_cast<QQuickItem*>(_qmlWindow.data());
|
||||
}
|
||||
|
||||
void QmlWindowClass::setVisible(bool visible) {
|
||||
|
@ -260,32 +193,34 @@ void QmlWindowClass::setVisible(bool visible) {
|
|||
|
||||
bool QmlWindowClass::isVisible() const {
|
||||
// The tool window itself has special logic based on whether any tabs are enabled
|
||||
if (_toolWindow) {
|
||||
auto targetTab = dynamic_cast<QQuickItem*>(_qmlWindow);
|
||||
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
|
||||
return QVariant::fromValue(targetTab->isEnabled());
|
||||
}).toBool();
|
||||
} else {
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
|
||||
return QVariant::fromValue(targetWindow->isVisible());
|
||||
}).toBool();
|
||||
}
|
||||
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
|
||||
if (_qmlWindow.isNull()) {
|
||||
return QVariant::fromValue(false);
|
||||
}
|
||||
if (_toolWindow) {
|
||||
return QVariant::fromValue(dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled());
|
||||
} else {
|
||||
return QVariant::fromValue(asQuickItem()->isVisible());
|
||||
}
|
||||
}).toBool();
|
||||
}
|
||||
|
||||
glm::vec2 QmlWindowClass::getPosition() const {
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||
return targetWindow->position();
|
||||
if (_qmlWindow.isNull()) {
|
||||
return QVariant(QPointF(0, 0));
|
||||
}
|
||||
return asQuickItem()->position();
|
||||
});
|
||||
return toGlm(result.toPointF());
|
||||
}
|
||||
|
||||
|
||||
void QmlWindowClass::setPosition(const glm::vec2& position) {
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
targetWindow->setPosition(QPointF(position.x, position.y));
|
||||
if (!_qmlWindow.isNull()) {
|
||||
asQuickItem()->setPosition(QPointF(position.x, position.y));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -299,17 +234,21 @@ glm::vec2 toGlm(const QSizeF& size) {
|
|||
}
|
||||
|
||||
glm::vec2 QmlWindowClass::getSize() const {
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||
if (_qmlWindow.isNull()) {
|
||||
return QVariant(QSizeF(0, 0));
|
||||
}
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
return QSizeF(targetWindow->width(), targetWindow->height());
|
||||
});
|
||||
return toGlm(result.toSizeF());
|
||||
}
|
||||
|
||||
void QmlWindowClass::setSize(const glm::vec2& size) {
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
targetWindow->setSize(QSizeF(size.x, size.y));
|
||||
if (!_qmlWindow.isNull()) {
|
||||
asQuickItem()->setSize(QSizeF(size.x, size.y));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -318,9 +257,10 @@ void QmlWindowClass::setSize(int width, int height) {
|
|||
}
|
||||
|
||||
void QmlWindowClass::setTitle(const QString& title) {
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
targetWindow->setProperty(TITLE_PROPERTY, title);
|
||||
if (!_qmlWindow.isNull()) {
|
||||
asQuickItem()->setProperty(TITLE_PROPERTY, title);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -345,7 +285,12 @@ void QmlWindowClass::hasClosed() {
|
|||
}
|
||||
|
||||
void QmlWindowClass::raise() {
|
||||
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::QueuedConnection);
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->executeOnUiThread([=] {
|
||||
if (!_qmlWindow.isNull()) {
|
||||
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#include "QmlWindowClass.moc"
|
||||
|
|
|
@ -10,42 +10,21 @@
|
|||
#define hifi_ui_QmlWindowClass_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <GLMHelpers.h>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QtQuick/QQuickItem>
|
||||
#include <QtWebChannel/QWebChannelAbstractTransport>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
class QScriptEngine;
|
||||
class QScriptContext;
|
||||
class QmlWindowClass;
|
||||
class QWebSocketServer;
|
||||
class QWebSocket;
|
||||
|
||||
class QmlScriptEventBridge : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QmlScriptEventBridge(const QmlWindowClass* webWindow) : _webWindow(webWindow) {}
|
||||
|
||||
public slots :
|
||||
void emitWebEvent(const QString& data);
|
||||
void emitScriptEvent(const QString& data);
|
||||
|
||||
signals:
|
||||
void webEventReceived(const QString& data);
|
||||
void scriptEventReceived(int windowId, const QString& data);
|
||||
|
||||
private:
|
||||
const QmlWindowClass* _webWindow { nullptr };
|
||||
QWebSocket *_socket { nullptr };
|
||||
};
|
||||
|
||||
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
||||
class QmlWindowClass : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT)
|
||||
Q_PROPERTY(int windowId READ getWindowId CONSTANT)
|
||||
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition)
|
||||
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize)
|
||||
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition NOTIFY positionChanged)
|
||||
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
|
||||
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged)
|
||||
|
||||
public:
|
||||
|
@ -64,21 +43,19 @@ public slots:
|
|||
glm::vec2 getSize() const;
|
||||
void setSize(const glm::vec2& size);
|
||||
void setSize(int width, int height);
|
||||
|
||||
void setTitle(const QString& title);
|
||||
|
||||
|
||||
// Ugh.... do not want to do
|
||||
Q_INVOKABLE void raise();
|
||||
Q_INVOKABLE void close();
|
||||
Q_INVOKABLE int getWindowId() const { return _windowId; };
|
||||
Q_INVOKABLE QmlScriptEventBridge* getEventBridge() const { return _eventBridge; };
|
||||
Q_INVOKABLE QObject* getEventBridge() { return this; };
|
||||
|
||||
// Scripts can use this to send a message to the QML object
|
||||
void sendToQml(const QVariant& message);
|
||||
|
||||
signals:
|
||||
void visibilityChanged(bool visible); // Tool window
|
||||
void positionChanged();
|
||||
void sizeChanged();
|
||||
void moved(glm::vec2 position);
|
||||
void resized(QSizeF size);
|
||||
void closed();
|
||||
|
@ -92,19 +69,13 @@ protected:
|
|||
static QScriptValue internalConstructor(const QString& qmlSource,
|
||||
QScriptContext* context, QScriptEngine* engine,
|
||||
std::function<QmlWindowClass*(QObject*)> function);
|
||||
static void setupServer();
|
||||
static void registerObject(const QString& name, QObject* object);
|
||||
static void deregisterObject(QObject* object);
|
||||
static QWebSocketServer* _webChannelServer;
|
||||
|
||||
QQuickItem* asQuickItem() const;
|
||||
QmlScriptEventBridge* const _eventBridge { new QmlScriptEventBridge(this) };
|
||||
|
||||
// FIXME needs to be initialized in the ctor once we have support
|
||||
// for tool window panes in QML
|
||||
bool _toolWindow { false };
|
||||
const int _windowId;
|
||||
QObject* _qmlWindow;
|
||||
QPointer<QObject> _qmlWindow;
|
||||
QString _source;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "OculusDisplayPlugin.h"
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include "OculusHelpers.h"
|
||||
|
||||
const QString OculusDisplayPlugin::NAME("Oculus Rift");
|
||||
|
@ -54,6 +55,9 @@ void OculusDisplayPlugin::updateFrameData() {
|
|||
}
|
||||
|
||||
void OculusDisplayPlugin::hmdPresent() {
|
||||
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
|
||||
|
||||
if (!_currentSceneTexture) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <PerfStat.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include "OpenVrHelpers.h"
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
|
||||
|
@ -69,6 +69,9 @@ void OpenVrDisplayPlugin::internalActivate() {
|
|||
_compositor = vr::VRCompositor();
|
||||
Q_ASSERT(_compositor);
|
||||
|
||||
// enable async time warp
|
||||
// _compositor->ForceInterleavedReprojectionOn(true);
|
||||
|
||||
// set up default sensor space such that the UI overlay will align with the front of the room.
|
||||
auto chaperone = vr::VRChaperone();
|
||||
if (chaperone) {
|
||||
|
@ -119,14 +122,11 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
|
|||
float vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
|
||||
|
||||
#if THREADED_PRESENT
|
||||
// TODO: this seems awfuly long, 44ms total, but it produced the best results.
|
||||
// 3 frames of prediction + vsyncToPhotons = 44ms total
|
||||
const float NUM_PREDICTION_FRAMES = 3.0f;
|
||||
float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons;
|
||||
#else
|
||||
uint64_t frameCounter;
|
||||
float timeSinceLastVsync;
|
||||
_system->GetTimeSinceLastVsync(&timeSinceLastVsync, &frameCounter);
|
||||
float predictedSecondsFromNow = 3.0f * frameDuration - timeSinceLastVsync + vsyncToPhotons;
|
||||
float predictedSecondsFromNow = frameDuration + vsyncToPhotons;
|
||||
#endif
|
||||
|
||||
vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||
|
@ -144,6 +144,9 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
|
|||
}
|
||||
|
||||
void OpenVrDisplayPlugin::hmdPresent() {
|
||||
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
|
||||
|
||||
// Flip y-axis since GL UV coords are backwards.
|
||||
static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 };
|
||||
static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 };
|
||||
|
@ -152,6 +155,10 @@ void OpenVrDisplayPlugin::hmdPresent() {
|
|||
|
||||
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);
|
||||
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds);
|
||||
}
|
||||
|
||||
void OpenVrDisplayPlugin::postPreview() {
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
|
||||
|
||||
vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
||||
|
|
|
@ -35,6 +35,7 @@ protected:
|
|||
|
||||
void hmdPresent() override;
|
||||
bool isHmdMounted() const override;
|
||||
void postPreview() override;
|
||||
|
||||
private:
|
||||
vr::IVRSystem* _system { nullptr };
|
||||
|
|
|
@ -51,7 +51,7 @@ vr::IVRSystem* acquireOpenVrSystem() {
|
|||
if (!activeHmd) {
|
||||
qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building";
|
||||
vr::EVRInitError eError = vr::VRInitError_None;
|
||||
activeHmd = vr::VR_Init(&eError);
|
||||
activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene);
|
||||
qCDebug(displayplugins) << "openvr display: HMD is " << activeHmd << " error is " << eError;
|
||||
}
|
||||
if (activeHmd) {
|
||||
|
|
|
@ -44,6 +44,14 @@ ready = function() {
|
|||
var domainServer = remote.getGlobal('domainServer');
|
||||
var acMonitor = remote.getGlobal('acMonitor');
|
||||
|
||||
var pendingLines = {
|
||||
'ds': new Array(),
|
||||
'ac': new Array()
|
||||
};
|
||||
|
||||
var UPDATE_INTERVAL = 16; // Update log at ~60 fps
|
||||
var interval = setInterval(flushPendingLines, UPDATE_INTERVAL);
|
||||
|
||||
var logWatchers = {
|
||||
'ds': {
|
||||
},
|
||||
|
@ -83,7 +91,7 @@ ready = function() {
|
|||
var logTail = new Tail(cleanFilePath, '\n', { start: start, interval: 500 });
|
||||
|
||||
logTail.on('line', function(msg) {
|
||||
appendLogMessage(msg, stream);
|
||||
pendingLines[stream].push(msg);
|
||||
});
|
||||
|
||||
logTail.on('error', function(error) {
|
||||
|
@ -107,6 +115,7 @@ ready = function() {
|
|||
}
|
||||
|
||||
window.onbeforeunload = function(e) {
|
||||
clearInterval(interval);
|
||||
domainServer.removeListener('logs-updated', updateLogFiles);
|
||||
acMonitor.removeListener('logs-updated', updateLogFiles);
|
||||
};
|
||||
|
@ -164,14 +173,23 @@ ready = function() {
|
|||
return !filter || message.toLowerCase().indexOf(filter) >= 0;
|
||||
}
|
||||
|
||||
function appendLogMessage(msg, name) {
|
||||
function appendLogMessages(name) {
|
||||
var array = pendingLines[name];
|
||||
if (array.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (array.length > maxLogLines) {
|
||||
array = array.slice(-maxLogLines);
|
||||
}
|
||||
|
||||
console.log(name, array.length);
|
||||
|
||||
var id = name == "ds" ? "domain-server" : "assignment-client";
|
||||
var $pidLog = $('#' + id);
|
||||
|
||||
var size = ++tabStates[id].size;
|
||||
var size = tabStates[id].size + array.length;
|
||||
if (size > maxLogLines) {
|
||||
$pidLog.find('div.log-line:first').remove();
|
||||
removed = true;
|
||||
$pidLog.find('div.log-line:lt(' + (size - maxLogLines) + ')').remove();
|
||||
}
|
||||
|
||||
var wasAtBottom = false;
|
||||
|
@ -179,17 +197,25 @@ ready = function() {
|
|||
wasAtBottom = $pidLog[0].scrollTop >= ($pidLog[0].scrollHeight - $pidLog.height());
|
||||
}
|
||||
|
||||
var $logLine = $('<div class="log-line">').text(msg);
|
||||
if (!shouldDisplayLogMessage(msg)) {
|
||||
$logLine.hide();
|
||||
for (line in array) {
|
||||
var $logLine = $('<div class="log-line">').text(array[line]);
|
||||
if (!shouldDisplayLogMessage(array[line])) {
|
||||
$logLine.hide();
|
||||
}
|
||||
|
||||
$pidLog.append($logLine);
|
||||
}
|
||||
|
||||
$pidLog.append($logLine);
|
||||
delete pendingLines[name];
|
||||
pendingLines[name] = new Array();
|
||||
|
||||
if (wasAtBottom) {
|
||||
$pidLog.scrollTop($pidLog[0].scrollHeight);
|
||||
}
|
||||
|
||||
}
|
||||
function flushPendingLines() {
|
||||
appendLogMessages('ds');
|
||||
appendLogMessages('ac');
|
||||
}
|
||||
|
||||
// handle filtering of table rows on input change
|
||||
|
|
|
@ -77,7 +77,7 @@ UDTTest::UDTTest(int& argc, char** argv) :
|
|||
|
||||
// randomize the seed for packet size randomization
|
||||
srand(time(NULL));
|
||||
|
||||
|
||||
_socket.bind(QHostAddress::AnyIPv4, _argumentParser.value(PORT_OPTION).toUInt());
|
||||
qDebug() << "Test socket is listening on" << _socket.localPort();
|
||||
|
||||
|
|
Loading…
Reference in a new issue