Merge branch 'master' of github.com:highfidelity/hifi into feat/geocaching

This commit is contained in:
Zach Pomerantz 2016-03-25 10:09:37 -07:00
commit 5d306a74d5
44 changed files with 583 additions and 408 deletions

View file

@ -369,14 +369,6 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
reverbTime = _zoneReverbSettings[i].reverbTime; reverbTime = _zoneReverbSettings[i].reverbTime;
wetLevel = _zoneReverbSettings[i].wetLevel; 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; break;
} }
} }

View file

@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
ExternalProject_Add( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL https://github.com/ValveSoftware/openvr/archive/v0.9.15.zip URL https://github.com/ValveSoftware/openvr/archive/v0.9.19.zip
URL_MD5 0ff8560b49b6da1150fcc47360e8ceca URL_MD5 843f9dde488584d8af1f3ecf2252b4e0
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND "" BUILD_COMMAND ""
INSTALL_COMMAND "" INSTALL_COMMAND ""

View file

@ -308,7 +308,7 @@
"name": "reverb", "name": "reverb",
"type": "table", "type": "table",
"label": "Reverb Settings", "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, "numbered": true,
"columns": [ "columns": [
{ {
@ -325,9 +325,9 @@
}, },
{ {
"name": "wet_level", "name": "wet_level",
"label": "Wet Level", "label": "Wet/Dry Mix",
"can_set": true, "can_set": true,
"placeholder": "(in db)" "placeholder": "(in percent)"
} }
] ]
} }

View file

@ -451,6 +451,37 @@
var elPreviewCameraButton = document.getElementById("preview-camera-button"); 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) { if (window.EventBridge !== undefined) {
var properties; var properties;
EventBridge.scriptEventReceived.connect(function(data) { EventBridge.scriptEventReceived.connect(function(data) {
@ -1185,6 +1216,7 @@
<div class="label">Ambient URL</div> <div class="label">Ambient URL</div>
<div class="value"> <div class="value">
<input type="text" id="property-zone-key-ambient-url" class="url"> <input type="text" id="property-zone-key-ambient-url" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
@ -1262,6 +1294,7 @@
<div class="label">Skybox URL</div> <div class="label">Skybox URL</div>
<div class="value"> <div class="value">
<input type="text" id="property-zone-skybox-url" class="url"> <input type="text" id="property-zone-skybox-url" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
@ -1273,6 +1306,7 @@
<div class="label">Source URL</div> <div class="label">Source URL</div>
<div class="value"> <div class="value">
<input type="text" id="property-web-source-url" class="url"> <input type="text" id="property-web-source-url" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
@ -1286,12 +1320,14 @@
<div class="label">Href - Hifi://address</div> <div class="label">Href - Hifi://address</div>
<div class="value"> <div class="value">
<input id="property-hyperlink-href" class="url"> <input id="property-hyperlink-href" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
<div class="hyperlink-section property"> <div class="hyperlink-section property">
<div class="label">Description</div> <div class="label">Description</div>
<div class="value"> <div class="value">
<input id="property-hyperlink-description" class="url"> <input id="property-hyperlink-description" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
@ -1375,16 +1411,19 @@
<div class="label">X-axis Texture URL</div> <div class="label">X-axis Texture URL</div>
<div class="value"> <div class="value">
<input type="text" id="property-x-texture-url" class="url"> <input type="text" id="property-x-texture-url" class="url">
<div class="update-url-version"></div>
</div> </div>
<div class="label">Y-axis Texture URL</div> <div class="label">Y-axis Texture URL</div>
<div class="value"> <div class="value">
<input type="text" id="property-y-texture-url" class="url"> <input type="text" id="property-y-texture-url" class="url">
<div class="update-url-version"></div>
</div> </div>
<div class="label">Z-axis Texture URL</div> <div class="label">Z-axis Texture URL</div>
<div class="value"> <div class="value">
<input type="text" id="property-z-texture-url" class="url"> <input type="text" id="property-z-texture-url" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
@ -1566,6 +1605,7 @@
<div class="label">Collision Sound URL</div> <div class="label">Collision Sound URL</div>
<div class="value"> <div class="value">
<input id="property-collision-sound-url" class="url"> <input id="property-collision-sound-url" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
@ -1583,6 +1623,7 @@
</div> </div>
<div class="value"> <div class="value">
<input id="property-script-url" class="url"> <input id="property-script-url" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
@ -1595,6 +1636,7 @@
<div class="label">Model URL</div> <div class="label">Model URL</div>
<div class="value"> <div class="value">
<input type="text" id="property-model-url" class="url"> <input type="text" id="property-model-url" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
@ -1613,12 +1655,14 @@
<div class="label">Compound Shape URL</div> <div class="label">Compound Shape URL</div>
<div class="value"> <div class="value">
<input type="text" id="property-compound-shape-url" class="url"> <input type="text" id="property-compound-shape-url" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
<div class="model-section property"> <div class="model-section property">
<div class="label">Animation URL</div> <div class="label">Animation URL</div>
<div class="value"> <div class="value">
<input type="text" id="property-model-animation-url" class="url"> <input type="text" id="property-model-animation-url" class="url">
<div class="update-url-version"></div>
</div> </div>
</div> </div>
<div class="model-section property"> <div class="model-section property">

View file

@ -10,50 +10,11 @@
var EventBridge; var EventBridge;
EventBridgeConnectionProxy = function(parent) { openEventBridge = function(callback) {
this.parent = parent; new QWebChannel(qt.webChannelTransport, function(channel) {
this.realSignal = this.parent.realBridge.scriptEventReceived console.log("uid " + EventBridgeUid);
this.webWindowId = this.parent.webWindow.windowId; EventBridge = channel.objects[EventBridgeUid];
} callback(EventBridge);
EventBridgeConnectionProxy.prototype.connect = function(callback) {
var that = this;
this.realSignal.connect(function(id, message) {
if (id === that.webWindowId) { callback(message); }
}); });
} }
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); }
});
}
}

View file

@ -4,21 +4,17 @@
<script type="text/javascript" src="jquery-2.1.4.min.js"></script> <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="qrc:///qtwebchannel/qwebchannel.js"></script>
<script type="text/javascript" src="eventBridgeLoader.js"></script> <script type="text/javascript" src="eventBridgeLoader.js"></script>
<script> <script>
var myBridge;
window.onload = function() { window.onload = function() {
openEventBridge(function(eventBridge) { openEventBridge(function() {
myBridge = eventBridge; EventBridge.scriptEventReceived.connect(function(message) {
myBridge.scriptEventReceived.connect(function(message) {
console.log("HTML side received message: " + message); console.log("HTML side received message: " + message);
}); });
}); });
} }
testClick = function() { testClick = function() {
myBridge.emitWebEvent("HTML side sending message - button click"); EventBridge.emitWebEvent(["Foo", "Bar", { "baz": 1} ]);
} }
</script> </script>
</head> </head>

View file

@ -134,8 +134,18 @@ textarea {
resize: vertical; 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 { input.url {
width: 100%; width:85%;
padding-right: 20px;
} }
input.coord { input.coord {

View file

@ -8,26 +8,14 @@ webWindow.eventBridge.webEventReceived.connect(function(data) {
print("JS Side event received: " + data); print("JS Side event received: " + data);
}); });
var titles = ["A", "B", "C"];
var titleIndex = 0;
Script.setInterval(function() { Script.setInterval(function() {
webWindow.eventBridge.emitScriptEvent("JS Event sent"); var message = [ Math.random(), Math.random() ];
var size = webWindow.size; print("JS Side sending: " + message);
var position = webWindow.position; webWindow.emitScriptEvent(message);
print("Window url: " + webWindow.url) }, 5 * 1000);
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);
Script.setTimeout(function() { Script.scriptEnding.connect(function(){
print("Closing script");
webWindow.close(); webWindow.close();
Script.stop(); webWindow.deleteLater();
}, 15 * 1000) });

View file

@ -1,6 +1,7 @@
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 1.2 import QtQuick.Controls 1.2
import QtWebEngine 1.1 import QtWebEngine 1.1
import QtWebChannel 1.0
import "windows" as Windows import "windows" as Windows
import "controls" as Controls import "controls" as Controls
@ -15,11 +16,22 @@ Windows.Window {
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
destroyOnCloseButton: false destroyOnCloseButton: false
property alias source: webview.url 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 { Controls.WebView {
id: webview id: webview
url: "about:blank" url: "about:blank"
anchors.fill: parent anchors.fill: parent
focus: true focus: true
onUrlChanged: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";");
Component.onCompleted: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";");
} }
} // dialog } // dialog

View file

@ -37,14 +37,33 @@ Windows.Window {
Repeater { Repeater {
model: 4 model: 4
Tab { Tab {
// Force loading of the content even if the tab is not visible
// (required for letting the C++ code access the webview)
active: true active: true
enabled: false; enabled: false
// we need to store the original url here for future identification
property string originalUrl: ""; property string originalUrl: "";
onEnabledChanged: toolWindow.updateVisiblity();
Controls.WebView { Controls.WebView {
id: 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 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); var tab = tabView.getTab(index);
tab.title = ""; tab.title = "";
tab.originalUrl = "";
tab.enabled = false; tab.enabled = false;
tab.originalUrl = "";
tab.item.url = "about:blank";
tab.item.enabled = false;
} }
function addWebTab(properties) { function addWebTab(properties) {
if (!properties.source) { if (!properties.source) {
console.warn("Attempted to open Web Tool Pane without URL") console.warn("Attempted to open Web Tool Pane without URL");
return; return;
} }
var existingTabIndex = findIndexForUrl(properties.source); var existingTabIndex = findIndexForUrl(properties.source);
if (existingTabIndex >= 0) { if (existingTabIndex >= 0) {
console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source) console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source);
return tabView.getTab(existingTabIndex); var tab = tabView.getTab(existingTabIndex);
return tab.item;
} }
var freeTabIndex = findFreeTab(); var freeTabIndex = findFreeTab();
@ -135,25 +157,22 @@ Windows.Window {
return; 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) { if (properties.width) {
tabView.width = Math.min(Math.max(tabView.width, properties.width), tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
toolWindow.maxSize.x);
} }
if (properties.height) { if (properties.height) {
tabView.height = Math.min(Math.max(tabView.height, properties.height), tabView.height = Math.min(Math.max(tabView.height, properties.height), toolWindow.maxSize.y);
toolWindow.maxSize.y);
} }
console.log("Updating visibility based on child tab added"); var tab = tabView.getTab(freeTabIndex);
newTab.enabledChanged.connect(updateVisiblity) tab.title = properties.title || "Unknown";
updateVisiblity(); tab.enabled = true;
return newTab tab.originalUrl = properties.source;
var result = tab.item;
result.enabled = true;
result.url = properties.source;
return result;
} }
} }

View file

@ -59,6 +59,7 @@ WebEngineView {
request.openIn(newWindow.webView) request.openIn(newWindow.webView)
} }
// This breaks the webchannel used for passing messages. Fixed in Qt 5.6
profile: desktop.browserProfile // See https://bugreports.qt.io/browse/QTBUG-49521
//profile: desktop.browserProfile
} }

View file

@ -1415,7 +1415,7 @@ void Application::paintGL() {
_lastFramesPerSecondUpdate = now; _lastFramesPerSecondUpdate = now;
} }
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
PerformanceTimer perfTimer("paintGL"); PerformanceTimer perfTimer("paintGL");
if (nullptr == _displayPlugin) { if (nullptr == _displayPlugin) {
@ -2554,11 +2554,12 @@ void Application::idle(uint64_t now) {
return; return;
} }
PROFILE_RANGE(__FUNCTION__);
// We're going to execute idle processing, so restart the last idle timer // We're going to execute idle processing, so restart the last idle timer
_lastTimeUpdated.start(); _lastTimeUpdated.start();
{ {
PROFILE_RANGE(__FUNCTION__);
static uint64_t lastIdleStart{ now }; static uint64_t lastIdleStart{ now };
uint64_t idleStartToStartDuration = now - lastIdleStart; uint64_t idleStartToStartDuration = now - lastIdleStart;
if (idleStartToStartDuration != 0) { if (idleStartToStartDuration != 0) {
@ -3146,6 +3147,9 @@ void Application::updateDialogs(float deltaTime) {
} }
void Application::update(float deltaTime) { void Application::update(float deltaTime) {
PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1);
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::update()"); PerformanceWarning warn(showWarnings, "Application::update()");
@ -3246,9 +3250,13 @@ void Application::update(float deltaTime) {
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>(); QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
if (_physicsEnabled) { if (_physicsEnabled) {
PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("physics"); PerformanceTimer perfTimer("physics");
{ {
PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("updateStates)"); PerformanceTimer perfTimer("updateStates)");
static VectorOfMotionStates motionStates; static VectorOfMotionStates motionStates;
_entitySimulation.getObjectsToRemoveFromPhysics(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"); PerformanceTimer perfTimer("stepSimulation");
getEntities()->getTree()->withWriteLock([&] { getEntities()->getTree()->withWriteLock([&] {
_physicsEngine->stepSimulation(); _physicsEngine->stepSimulation();
}); });
} }
{ {
PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("havestChanges"); PerformanceTimer perfTimer("havestChanges");
if (_physicsEngine->hasOutgoingChanges()) { if (_physicsEngine->hasOutgoingChanges()) {
getEntities()->getTree()->withWriteLock([&] { getEntities()->getTree()->withWriteLock([&] {
@ -3321,17 +3331,24 @@ void Application::update(float deltaTime) {
qApp->setAvatarSimrateSample(1.0f / deltaTime); qApp->setAvatarSimrateSample(1.0f / deltaTime);
avatarManager->updateOtherAvatars(deltaTime); {
PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
avatarManager->updateOtherAvatars(deltaTime);
}
qApp->updateMyAvatarLookAtPosition(); qApp->updateMyAvatarLookAtPosition();
// update sensorToWorldMatrix for camera and hand controllers // update sensorToWorldMatrix for camera and hand controllers
myAvatar->updateSensorToWorldMatrix(); 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"); PerformanceTimer perfTimer("overlays");
_overlays.update(deltaTime); _overlays.update(deltaTime);
} }
@ -3351,6 +3368,7 @@ void Application::update(float deltaTime) {
// Update my voxel servers with my current voxel query... // Update my voxel servers with my current voxel query...
{ {
PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("queryOctree"); PerformanceTimer perfTimer("queryOctree");
quint64 sinceLastQuery = now - _lastQueriedTime; quint64 sinceLastQuery = now - _lastQueriedTime;
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND; const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
@ -4677,13 +4695,18 @@ qreal Application::getDevicePixelRatio() {
} }
DisplayPlugin* Application::getActiveDisplayPlugin() { DisplayPlugin* Application::getActiveDisplayPlugin() {
std::unique_lock<std::recursive_mutex> lock(_displayPluginLock); DisplayPlugin* result = nullptr;
if (nullptr == _displayPlugin && QThread::currentThread() == thread()) { if (QThread::currentThread() == thread()) {
updateDisplayMode(); if (nullptr == _displayPlugin) {
Q_ASSERT(_displayPlugin); updateDisplayMode();
Q_ASSERT(_displayPlugin);
}
result = _displayPlugin.get();
} else {
std::unique_lock<std::mutex> lock(_displayPluginLock);
result = _displayPlugin.get();
} }
return result;
return _displayPlugin.get();
} }
const DisplayPlugin* Application::getActiveDisplayPlugin() const { const DisplayPlugin* Application::getActiveDisplayPlugin() const {
@ -4801,20 +4824,26 @@ void Application::updateDisplayMode() {
return; return;
} }
if (_displayPlugin) {
_displayPlugin->deactivate();
}
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
// FIXME probably excessive and useless context switching // Make the switch atomic from the perspective of other threads
_offscreenContext->makeCurrent(); {
newDisplayPlugin->activate(); std::unique_lock<std::mutex> lock(_displayPluginLock);
_offscreenContext->makeCurrent();
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); if (_displayPlugin) {
_offscreenContext->makeCurrent(); _displayPlugin->deactivate();
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); }
_displayPlugin = newDisplayPlugin;
// 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(); emit activeDisplayPluginChanged();

View file

@ -383,7 +383,7 @@ private:
OffscreenGLCanvas* _offscreenContext { nullptr }; OffscreenGLCanvas* _offscreenContext { nullptr };
DisplayPluginPointer _displayPlugin; DisplayPluginPointer _displayPlugin;
std::recursive_mutex _displayPluginLock; std::mutex _displayPluginLock;
InputPluginList _activeInputPlugins; InputPluginList _activeInputPlugins;
bool _activatingDisplayPlugin { false }; bool _activatingDisplayPlugin { false };

View file

@ -458,7 +458,7 @@ Menu::Menu() {
avatar, SLOT(setEnableMeshVisible(bool))); avatar, SLOT(setEnableMeshVisible(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false, addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, true,
avatar, SLOT(setUseAnimPreAndPostRotations(bool))); avatar, SLOT(setUseAnimPreAndPostRotations(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true, addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
avatar, SLOT(setEnableInverseKinematics(bool))); avatar, SLOT(setEnableInverseKinematics(bool)));

View file

@ -187,7 +187,7 @@ void Avatar::simulate(float deltaTime) {
// simple frustum check // simple frustum check
float boundingRadius = getBoundingRadius(); float boundingRadius = getBoundingRadius();
bool inView = qApp->getViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius); bool inView = qApp->getDisplayViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
if (_shouldAnimate && !_shouldSkipRender && inView) { if (_shouldAnimate && !_shouldSkipRender && inView) {
{ {

View file

@ -13,7 +13,7 @@
#include "AnimationLogging.h" #include "AnimationLogging.h"
#include "AnimUtil.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) : AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) :
AnimNode(AnimNode::Type::Clip, id), AnimNode(AnimNode::Type::Clip, id),

View file

@ -20,6 +20,7 @@
#include <GeometryUtil.h> #include <GeometryUtil.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <DebugDraw.h> #include <DebugDraw.h>
#include <shared/NsightHelpers.h>
#include "AnimationLogging.h" #include "AnimationLogging.h"
#include "AnimClip.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) { void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
PROFILE_RANGE_EX(__FUNCTION__, 0xffff00ff, 0);
setModelOffset(rootTransform); setModelOffset(rootTransform);
if (_animNode) { if (_animNode) {

View file

@ -565,10 +565,10 @@ void AudioClient::updateReverbOptions() {
_zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime()); _zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
reverbChanged = true; reverbChanged = true;
} }
//if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) { if (_zoneReverbOptions.getWetDryMix() != _receivedAudioStream.getWetLevel()) {
// _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel()); _zoneReverbOptions.setWetDryMix(_receivedAudioStream.getWetLevel());
// reverbChanged = true; reverbChanged = true;
//} }
if (_reverbOptions != &_zoneReverbOptions) { if (_reverbOptions != &_zoneReverbOptions) {
_reverbOptions = &_zoneReverbOptions; _reverbOptions = &_zoneReverbOptions;

View file

@ -21,7 +21,7 @@
#include <gl/GLWidget.h> #include <gl/GLWidget.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <DependencyManager.h> #include <DependencyManager.h>
#include <shared/NsightHelpers.h>
#include <plugins/PluginContainer.h> #include <plugins/PluginContainer.h>
#include <gl/Config.h> #include <gl/Config.h>
#include <gl/GLEscrow.h> #include <gl/GLEscrow.h>
@ -404,7 +404,11 @@ void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overla
void OpenGLDisplayPlugin::updateTextures() { void OpenGLDisplayPlugin::updateTextures() {
// FIXME intrduce a GPU wait instead of a CPU/GPU sync point? // FIXME intrduce a GPU wait instead of a CPU/GPU sync point?
#if THREADED_PRESENT
if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) { if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) {
#else
if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) {
#endif
updateFrameData(); updateFrameData();
} }
@ -527,6 +531,9 @@ void OpenGLDisplayPlugin::internalPresent() {
void OpenGLDisplayPlugin::present() { void OpenGLDisplayPlugin::present() {
incrementPresentCount(); incrementPresentCount();
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
updateTextures(); updateTextures();
if (_currentSceneTexture) { if (_currentSceneTexture) {
// Write all layers to a local framebuffer // Write all layers to a local framebuffer

View file

@ -17,9 +17,9 @@
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <SimpleMovingAverage.h> #include <SimpleMovingAverage.h>
#include <gl/OglplusHelpers.h> #include <gl/OglplusHelpers.h>
#include <gl/GLEscrow.h>
#define THREADED_PRESENT 1 #define THREADED_PRESENT 1
#include <gl/GLEscrow.h>
class OpenGLDisplayPlugin : public DisplayPlugin { class OpenGLDisplayPlugin : public DisplayPlugin {
protected: protected:

View file

@ -19,6 +19,7 @@
#include <gpu/GLBackend.h> #include <gpu/GLBackend.h>
#include <CursorManager.h> #include <CursorManager.h>
#include <gl/GLWidget.h> #include <gl/GLWidget.h>
#include <shared/NsightHelpers.h>
#include "../Logging.h" #include "../Logging.h"
#include "../CompositorHelper.h" #include "../CompositorHelper.h"
@ -106,6 +107,9 @@ void HmdDisplayPlugin::compositePointer() {
} }
void HmdDisplayPlugin::internalPresent() { void HmdDisplayPlugin::internalPresent() {
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
// Composite together the scene, overlay and mouse cursor // Composite together the scene, overlay and mouse cursor
hmdPresent(); hmdPresent();
@ -149,6 +153,8 @@ void HmdDisplayPlugin::internalPresent() {
}); });
swapBuffers(); swapBuffers();
} }
postPreview();
} }
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) { void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {

View file

@ -31,6 +31,7 @@ public:
protected: protected:
virtual void hmdPresent() = 0; virtual void hmdPresent() = 0;
virtual bool isHmdMounted() const = 0; virtual bool isHmdMounted() const = 0;
virtual void postPreview() {};
void internalActivate() override; void internalActivate() override;
void compositeOverlay() override; void compositeOverlay() override;

View file

@ -113,16 +113,18 @@ QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) {
return _originalTextures; return _originalTextures;
} }
// TODO: Remove this line and enforce passing a texturemap as stringified JSON // Legacy: a ,\n-delimited list of filename:"texturepath"
QString jsonTextures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}"; if (*textures.cbegin() != '{') {
textures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}";
}
QJsonParseError error; QJsonParseError error;
QJsonDocument texturesAsJson = QJsonDocument::fromJson(jsonTextures.toUtf8(), &error); QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error);
if (error.error != QJsonParseError::NoError) { if (error.error != QJsonParseError::NoError) {
qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << _textures; qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << _textures;
return _originalTextures; return _originalTextures;
} }
QJsonObject texturesAsJsonObject = texturesAsJson.object(); return texturesJson.object().toVariantMap();
return texturesAsJsonObject.toVariantMap();
} }
void RenderableModelEntityItem::remapTextures() { void RenderableModelEntityItem::remapTextures() {

View file

@ -320,13 +320,12 @@ void ImageReader::run() {
} }
QMetaObject::invokeMethod(texture.data(), "setImage", QMetaObject::invokeMethod(texture.data(), "setImage",
Q_ARG(const QImage&, image),
Q_ARG(void*, theTexture), Q_ARG(void*, theTexture),
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
QThread::currentThread()->setPriority(originalPriority); QThread::currentThread()->setPriority(originalPriority);
} }
void NetworkTexture::setImage(const QImage& image, void* voidTexture, int originalWidth, void NetworkTexture::setImage(void* voidTexture, int originalWidth,
int originalHeight) { int originalHeight) {
_originalWidth = originalWidth; _originalWidth = originalWidth;
_originalHeight = originalHeight; _originalHeight = originalHeight;

View file

@ -136,7 +136,7 @@ protected:
Q_INVOKABLE void loadContent(const QByteArray& content); 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... // 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: private:

View file

@ -103,6 +103,7 @@ SendQueue& Connection::getSendQueue() {
QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission); QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission);
QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive); QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive);
QObject::connect(_sendQueue.get(), &SendQueue::timeout, this, &Connection::queueTimeout); 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() // set defaults on the send queue from our congestion control object and estimatedTimeout()
_sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod); _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) { void Connection::sendReliablePacket(std::unique_ptr<Packet> packet) {
Q_ASSERT_X(packet->isReliable(), "Connection::send", "Trying to send an unreliable packet reliably."); Q_ASSERT_X(packet->isReliable(), "Connection::send", "Trying to send an unreliable packet reliably.");
getSendQueue().queuePacket(std::move(packet)); getSendQueue().queuePacket(std::move(packet));

View file

@ -87,6 +87,7 @@ private slots:
void recordRetransmission(); void recordRetransmission();
void queueInactive(); void queueInactive();
void queueTimeout(); void queueTimeout();
void queueShortCircuitLoss(quint32 sequenceNumber);
private: private:
void sendACK(bool wasCausedBySyncTimeout = true); void sendACK(bool wasCausedBySyncTimeout = true);

View file

@ -128,13 +128,13 @@ void SendQueue::stop() {
_emptyCondition.notify_one(); _emptyCondition.notify_one();
} }
void SendQueue::sendPacket(const Packet& packet) { int SendQueue::sendPacket(const Packet& packet) {
_socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination); return _socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination);
} }
void SendQueue::ack(SequenceNumber ack) { void SendQueue::ack(SequenceNumber ack) {
// this is a response from the client, re-set our timeout expiry and our last response time // 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) { if (_lastACKSequenceNumber == (uint32_t) ack) {
return; return;
@ -164,7 +164,7 @@ void SendQueue::ack(SequenceNumber ack) {
void SendQueue::nak(SequenceNumber start, SequenceNumber end) { void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
// this is a response from the client, re-set our timeout expiry // 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); std::lock_guard<std::mutex> nakLocker(_naksLock);
@ -177,7 +177,7 @@ void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) { void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
// this is a response from the client, re-set our timeout expiry // 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); std::lock_guard<std::mutex> nakLocker(_naksLock);
@ -232,15 +232,16 @@ SequenceNumber SendQueue::getNextSequenceNumber() {
return _currentSequenceNumber; 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 // write the sequence number and send the packet
newPacket->writeSequenceNumber(sequenceNumber); newPacket->writeSequenceNumber(sequenceNumber);
sendPacket(*newPacket);
// Save packet/payload size before we move it // Save packet/payload size before we move it
auto packetSize = newPacket->getDataSize(); auto packetSize = newPacket->getDataSize();
auto payloadSize = newPacket->getPayloadSize(); auto payloadSize = newPacket->getPayloadSize();
auto bytesWritten = sendPacket(*newPacket);
{ {
// Insert the packet we have just sent in the sent list // Insert the packet we have just sent in the sent list
QWriteLocker locker(&_sentLock); QWriteLocker locker(&_sentLock);
@ -251,6 +252,22 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket,
Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list"); Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list");
emit packetSent(packetSize, payloadSize); 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() { void SendQueue::run() {
@ -285,12 +302,14 @@ void SendQueue::run() {
auto nextPacketTimestamp = p_high_resolution_clock::now(); auto nextPacketTimestamp = p_high_resolution_clock::now();
while (_state == State::Running) { 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 // 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 // (this is according to the current flow window size) then we send out a new packet
if (!sentAPacket) { auto newPacketCount = 0;
sentAPacket = maybeSendNewPacket(); if (!attemptedToSendPacket) {
newPacketCount = maybeSendNewPacket();
attemptedToSendPacket = (newPacketCount > 0);
} }
// since we're a while loop, give the thread a chance to process events // 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 // If the send queue has been innactive, skip the sleep for
// Either _isRunning will have been set to false and we'll break // Either _isRunning will have been set to false and we'll break
// Or something happened and we'll keep going // Or something happened and we'll keep going
if (_state != State::Running || isInactive(sentAPacket)) { if (_state != State::Running || isInactive(attemptedToSendPacket)) {
return; return;
} }
// push the next packet timestamp forwards by the current packet send period // 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 // 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()); 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()) { if (!isFlowWindowFull()) {
// we didn't re-send a packet, so time to send a new one // we didn't re-send a packet, so time to send a new one
@ -325,37 +345,42 @@ bool SendQueue::maybeSendNewPacket() {
std::unique_ptr<Packet> firstPacket = _packets.takePacket(); std::unique_ptr<Packet> firstPacket = _packets.takePacket();
Q_ASSERT(firstPacket); Q_ASSERT(firstPacket);
std::unique_ptr<Packet> secondPacket;
bool shouldSendPairTail = false;
if (((uint32_t) nextNumber & 0xF) == 0) { // attempt to send the first packet
// the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets if (sendNewPacketAndAddToSentList(move(firstPacket), nextNumber)) {
// pull off a second packet if we can before we unlock std::unique_ptr<Packet> secondPacket;
shouldSendPairTail = true; bool shouldSendPairTail = false;
secondPacket = _packets.takePacket(); 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 // No packets were sent
return false; return 0;
} }
bool SendQueue::maybeResendPacket() { bool SendQueue::maybeResendPacket() {
@ -377,6 +402,7 @@ bool SendQueue::maybeResendPacket() {
auto it = _sentPackets.find(resendNumber); auto it = _sentPackets.find(resendNumber);
if (it != _sentPackets.end()) { if (it != _sentPackets.end()) {
auto& entry = it->second; auto& entry = it->second;
// we found the packet - grab it // we found the packet - grab it
auto& resendPacket = *(entry.second); auto& resendPacket = *(entry.second);
@ -437,7 +463,7 @@ bool SendQueue::maybeResendPacket() {
return false; return false;
} }
bool SendQueue::isInactive(bool sentAPacket) { bool SendQueue::isInactive(bool attemptedToSendPacket) {
// check for connection timeout first // 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 // 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); 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 && _lastReceiverResponse > 0 &&
sinceLastResponse > MIN_MS_BEFORE_INACTIVE) { sinceLastResponse > MIN_MS_BEFORE_INACTIVE) {
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER, // If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
@ -462,7 +489,7 @@ bool SendQueue::isInactive(bool sentAPacket) {
return true; return true;
} }
if (!sentAPacket) { if (!attemptedToSendPacket) {
// During our processing above we didn't send any packets // 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. // If that is still the case we should use a condition_variable_any to sleep until we have data to handle.

View file

@ -79,6 +79,7 @@ signals:
void queueInactive(); void queueInactive();
void shortCircuitLoss(quint32 sequenceNumber);
void timeout(); void timeout();
private slots: private slots:
@ -91,13 +92,13 @@ private:
void sendHandshake(); void sendHandshake();
void sendPacket(const Packet& packet); int sendPacket(const Packet& packet);
void sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber); 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 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 void deactivate(); // makes the queue inactive and cleans it up
bool isFlowWindowFull() const; bool isFlowWindowFull() const;
@ -122,7 +123,7 @@ private:
std::atomic<int> _estimatedTimeout { 0 }; // Estimated timeout, set from CC 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<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 std::atomic<int> _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC

View file

@ -114,6 +114,7 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render
void CullSpatialSelection::configure(const Config& config) { void CullSpatialSelection::configure(const Config& config) {
_justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum); _justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum);
_freezeFrustum = config.freezeFrustum; _freezeFrustum = config.freezeFrustum;
_skipCulling = config.skipCulling;
} }
void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, 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) // 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) // 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 if (_skipCulling) {
{ // inside & fit items: filter only, culling is disabled
PerformanceTimer perfTimer("insideFitItems"); {
for (auto id : inSelection.insideItems) { PerformanceTimer perfTimer("insideFitItems");
auto& item = scene->getItem(id); for (auto id : inSelection.insideItems) {
if (_filter.test(item.getKey())) { auto& item = scene->getItem(id);
ItemBound itemBound(id, item.getBound()); if (_filter.test(item.getKey())) {
outItems.emplace_back(itemBound); ItemBound itemBound(id, item.getBound());
}
}
}
// 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); outItems.emplace_back(itemBound);
} }
} }
} }
}
// partial & fit items: filter & frustum cull // inside & subcell items: filter only, culling is disabled
{ {
PerformanceTimer perfTimer("partialFitItems"); PerformanceTimer perfTimer("insideSmallItems");
for (auto id : inSelection.partialItems) { for (auto id : inSelection.insideSubcellItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (_filter.test(item.getKey())) { if (_filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound()); ItemBound itemBound(id, item.getBound());
if (test.frustumTest(itemBound.bound)) {
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
} }
} }
} }
}
// partial & subcell items:: filter & frutum cull & solidangle cull // partial & fit items: filter only, culling is disabled
{ {
PerformanceTimer perfTimer("partialSmallItems"); PerformanceTimer perfTimer("partialFitItems");
for (auto id : inSelection.partialSubcellItems) { for (auto id : inSelection.partialItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (_filter.test(item.getKey())) { if (_filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound()); ItemBound itemBound(id, item.getBound());
if (test.frustumTest(itemBound.bound)) { 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)) { if (test.solidAngleTest(itemBound.bound)) {
outItems.emplace_back(itemBound); 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(); details._rendered += (int)outItems.size();

View file

@ -70,14 +70,16 @@ namespace render {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int numItems READ getNumItems) Q_PROPERTY(int numItems READ getNumItems)
Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum)
Q_PROPERTY(bool skipCulling MEMBER skipCulling WRITE setSkipCulling)
public: public:
int numItems{ 0 }; int numItems{ 0 };
int getNumItems() { return numItems; } int getNumItems() { return numItems; }
bool freezeFrustum{ false }; bool freezeFrustum{ false };
bool skipCulling{ false };
public slots: public slots:
void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); }
void setSkipCulling(bool enabled) { skipCulling = enabled; emit dirty(); }
signals: signals:
void dirty(); void dirty();
}; };
@ -85,6 +87,7 @@ namespace render {
class CullSpatialSelection { class CullSpatialSelection {
bool _freezeFrustum{ false }; // initialized by Config bool _freezeFrustum{ false }; // initialized by Config
bool _justFrozeFrustum{ false }; bool _justFrozeFrustum{ false };
bool _skipCulling{ false };
ViewFrustum _frozenFrutstum; ViewFrustum _frozenFrutstum;
public: public:
using Config = CullSpatialSelectionConfig; using Config = CullSpatialSelectionConfig;

View file

@ -8,6 +8,7 @@
#include "NsightHelpers.h" #include "NsightHelpers.h"
#ifdef _WIN32
#if defined(NSIGHT_FOUND) #if defined(NSIGHT_FOUND)
#include "nvToolsExt.h" #include "nvToolsExt.h"
@ -15,8 +16,28 @@ ProfileRange::ProfileRange(const char *name) {
nvtxRangePush(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() { ProfileRange::~ProfileRange() {
nvtxRangePop(); nvtxRangePop();
} }
#else
ProfileRange::ProfileRange(const char *name) {}
ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {}
ProfileRange::~ProfileRange() {}
#endif #endif
#endif // _WIN32

View file

@ -9,16 +9,21 @@
#ifndef hifi_gl_NsightHelpers_h #ifndef hifi_gl_NsightHelpers_h
#define hifi_gl_NsightHelpers_h #define hifi_gl_NsightHelpers_h
#if defined(NSIGHT_FOUND) #ifdef _WIN32
class ProfileRange { #include <stdint.h>
public:
ProfileRange(const char *name); class ProfileRange {
~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(name) ProfileRange profileRangeThis(name);
#define PROFILE_RANGE_EX(name, argbColor, payload) ProfileRange profileRangeThis(name, argbColor, payload);
#else #else
#define PROFILE_RANGE(name) #define PROFILE_RANGE(name)
#define PROFILE_RANGE_EX(name, argbColor, payload)
#endif #endif
#endif #endif

View file

@ -14,6 +14,8 @@
#include <QtQml/QQmlContext> #include <QtQml/QQmlContext>
#include <QtWebChannel/QWebChannel>
#include <QtScript/QScriptContext> #include <QtScript/QScriptContext>
#include <QtScript/QScriptEngine> #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 // Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
return QmlWindowClass::internalConstructor("QmlWebWindow.qml", context, 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) { 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::emitWebEvent(const QVariant& webMessage) {
void QmlWebWindowClass::handleNavigation(const QString& url) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage));
} else {
emit webEventReceived(webMessage);
}
} }
QString QmlWebWindowClass::getURL() const { QString QmlWebWindowClass::getURL() const {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant { QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
if (_qmlWindow.isNull()) {
return QVariant();
}
return _qmlWindow->property(URL_PROPERTY); return _qmlWindow->property(URL_PROPERTY);
}); });
return result.toString(); return result.toString();
@ -54,6 +76,8 @@ extern QString fixupHifiUrl(const QString& urlString);
void QmlWebWindowClass::setURL(const QString& urlString) { void QmlWebWindowClass::setURL(const QString& urlString) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
_qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString)); if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString));
}
}); });
} }

View file

@ -11,10 +11,13 @@
#include "QmlWindowClass.h" #include "QmlWindowClass.h"
class QWebChannel;
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping // FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
class QmlWebWindowClass : public QmlWindowClass { class QmlWebWindowClass : public QmlWindowClass {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString url READ getURL CONSTANT) Q_PROPERTY(QString url READ getURL CONSTANT)
Q_PROPERTY(QString uid READ getUid CONSTANT)
public: public:
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
@ -23,12 +26,18 @@ public:
public slots: public slots:
QString getURL() const; QString getURL() const;
void setURL(const QString& url); void setURL(const QString& url);
const QString& getUid() const { return _uid; }
void emitScriptEvent(const QVariant& scriptMessage);
void emitWebEvent(const QVariant& webMessage);
signals: signals:
void urlChanged(); void urlChanged();
void scriptEventReceived(const QVariant& message);
void webEventReceived(const QVariant& message);
private slots: private:
void handleNavigation(const QString& url); QString _uid;
QWebChannel* _webchannel { nullptr };
}; };
#endif #endif

View file

@ -26,10 +26,6 @@
#include "OffscreenUi.h" #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 SOURCE_PROPERTY = "source";
static const char* const TITLE_PROPERTY = "title"; static const char* const TITLE_PROPERTY = "title";
static const char* const WIDTH_PROPERTY = "width"; 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 VISIBILE_PROPERTY = "visible";
static const char* const TOOLWINDOW_PROPERTY = "toolWindow"; 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, QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
QScriptContext* context, QScriptEngine* engine, QScriptContext* context, QScriptEngine* engine,
std::function<QmlWindowClass*(QObject*)> builder) std::function<QmlWindowClass*(QObject*)> builder)
@ -168,10 +116,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
} }
offscreenUi->returnFromUiThread([&] { offscreenUi->returnFromUiThread([&] {
setupServer();
retVal = builder(newTab); retVal = builder(newTab);
retVal->_toolWindow = true; retVal->_toolWindow = true;
registerObject(url.toLower(), retVal);
return QVariant(); return QVariant();
}); });
} else { } else {
@ -179,10 +125,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection, QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection,
Q_ARG(const QString&, qmlSource), Q_ARG(const QString&, qmlSource),
Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) { Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) {
setupServer();
retVal = builder(object); retVal = builder(object);
context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership); context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
registerObject(url.toLower(), retVal);
if (!title.isEmpty()) { if (!title.isEmpty()) {
retVal->setTitle(title); retVal->setTitle(title);
} }
@ -209,12 +153,9 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine*
}); });
} }
QmlWindowClass::QmlWindowClass(QObject* qmlWindow) QmlWindowClass::QmlWindowClass(QObject* qmlWindow) : _qmlWindow(qmlWindow) {
: _windowId(++nextWindowId), _qmlWindow(qmlWindow)
{
qDebug() << "Created window with ID " << _windowId;
Q_ASSERT(_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 // Forward messages received from QML on to the script
connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection); connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection);
} }
@ -228,19 +169,11 @@ QmlWindowClass::~QmlWindowClass() {
close(); 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 { QQuickItem* QmlWindowClass::asQuickItem() const {
if (_toolWindow) { if (_toolWindow) {
return DependencyManager::get<OffscreenUi>()->getToolWindow(); return DependencyManager::get<OffscreenUi>()->getToolWindow();
} }
return dynamic_cast<QQuickItem*>(_qmlWindow); return _qmlWindow.isNull() ? nullptr : dynamic_cast<QQuickItem*>(_qmlWindow.data());
} }
void QmlWindowClass::setVisible(bool visible) { void QmlWindowClass::setVisible(bool visible) {
@ -260,32 +193,34 @@ void QmlWindowClass::setVisible(bool visible) {
bool QmlWindowClass::isVisible() const { bool QmlWindowClass::isVisible() const {
// The tool window itself has special logic based on whether any tabs are enabled // The tool window itself has special logic based on whether any tabs are enabled
if (_toolWindow) { return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
auto targetTab = dynamic_cast<QQuickItem*>(_qmlWindow); if (_qmlWindow.isNull()) {
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] { return QVariant::fromValue(false);
return QVariant::fromValue(targetTab->isEnabled()); }
}).toBool(); if (_toolWindow) {
} else { return QVariant::fromValue(dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled());
QQuickItem* targetWindow = asQuickItem(); } else {
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] { return QVariant::fromValue(asQuickItem()->isVisible());
return QVariant::fromValue(targetWindow->isVisible()); }
}).toBool(); }).toBool();
}
} }
glm::vec2 QmlWindowClass::getPosition() const { glm::vec2 QmlWindowClass::getPosition() const {
QQuickItem* targetWindow = asQuickItem();
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant { 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()); return toGlm(result.toPointF());
} }
void QmlWindowClass::setPosition(const glm::vec2& position) { void QmlWindowClass::setPosition(const glm::vec2& position) {
QQuickItem* targetWindow = asQuickItem();
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { 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 { glm::vec2 QmlWindowClass::getSize() const {
QQuickItem* targetWindow = asQuickItem();
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant { 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 QSizeF(targetWindow->width(), targetWindow->height());
}); });
return toGlm(result.toSizeF()); return toGlm(result.toSizeF());
} }
void QmlWindowClass::setSize(const glm::vec2& size) { void QmlWindowClass::setSize(const glm::vec2& size) {
QQuickItem* targetWindow = asQuickItem();
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { 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) { void QmlWindowClass::setTitle(const QString& title) {
QQuickItem* targetWindow = asQuickItem();
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { 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() { 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" #include "QmlWindowClass.moc"

View file

@ -10,42 +10,21 @@
#define hifi_ui_QmlWindowClass_h #define hifi_ui_QmlWindowClass_h
#include <QtCore/QObject> #include <QtCore/QObject>
#include <GLMHelpers.h> #include <QtCore/QPointer>
#include <QtScript/QScriptValue> #include <QtScript/QScriptValue>
#include <QtQuick/QQuickItem> #include <QtQuick/QQuickItem>
#include <QtWebChannel/QWebChannelAbstractTransport>
#include <GLMHelpers.h>
class QScriptEngine; class QScriptEngine;
class QScriptContext; 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 // FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
class QmlWindowClass : public QObject { class QmlWindowClass : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT) Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT)
Q_PROPERTY(int windowId READ getWindowId CONSTANT) Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition NOTIFY positionChanged)
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition) Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged) Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged)
public: public:
@ -64,21 +43,19 @@ public slots:
glm::vec2 getSize() const; glm::vec2 getSize() const;
void setSize(const glm::vec2& size); void setSize(const glm::vec2& size);
void setSize(int width, int height); void setSize(int width, int height);
void setTitle(const QString& title); void setTitle(const QString& title);
// Ugh.... do not want to do
Q_INVOKABLE void raise(); Q_INVOKABLE void raise();
Q_INVOKABLE void close(); Q_INVOKABLE void close();
Q_INVOKABLE int getWindowId() const { return _windowId; }; Q_INVOKABLE QObject* getEventBridge() { return this; };
Q_INVOKABLE QmlScriptEventBridge* getEventBridge() const { return _eventBridge; };
// Scripts can use this to send a message to the QML object // Scripts can use this to send a message to the QML object
void sendToQml(const QVariant& message); void sendToQml(const QVariant& message);
signals: signals:
void visibilityChanged(bool visible); // Tool window void visibilityChanged(bool visible); // Tool window
void positionChanged();
void sizeChanged();
void moved(glm::vec2 position); void moved(glm::vec2 position);
void resized(QSizeF size); void resized(QSizeF size);
void closed(); void closed();
@ -92,19 +69,13 @@ protected:
static QScriptValue internalConstructor(const QString& qmlSource, static QScriptValue internalConstructor(const QString& qmlSource,
QScriptContext* context, QScriptEngine* engine, QScriptContext* context, QScriptEngine* engine,
std::function<QmlWindowClass*(QObject*)> function); 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; QQuickItem* asQuickItem() const;
QmlScriptEventBridge* const _eventBridge { new QmlScriptEventBridge(this) };
// FIXME needs to be initialized in the ctor once we have support // FIXME needs to be initialized in the ctor once we have support
// for tool window panes in QML // for tool window panes in QML
bool _toolWindow { false }; bool _toolWindow { false };
const int _windowId; QPointer<QObject> _qmlWindow;
QObject* _qmlWindow;
QString _source; QString _source;
}; };

View file

@ -6,6 +6,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "OculusDisplayPlugin.h" #include "OculusDisplayPlugin.h"
#include <shared/NsightHelpers.h>
#include "OculusHelpers.h" #include "OculusHelpers.h"
const QString OculusDisplayPlugin::NAME("Oculus Rift"); const QString OculusDisplayPlugin::NAME("Oculus Rift");
@ -54,6 +55,9 @@ void OculusDisplayPlugin::updateFrameData() {
} }
void OculusDisplayPlugin::hmdPresent() { void OculusDisplayPlugin::hmdPresent() {
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
if (!_currentSceneTexture) { if (!_currentSceneTexture) {
return; return;
} }

View file

@ -21,7 +21,7 @@
#include <PerfStat.h> #include <PerfStat.h>
#include <plugins/PluginContainer.h> #include <plugins/PluginContainer.h>
#include <ViewFrustum.h> #include <ViewFrustum.h>
#include <shared/NsightHelpers.h>
#include "OpenVrHelpers.h" #include "OpenVrHelpers.h"
Q_DECLARE_LOGGING_CATEGORY(displayplugins) Q_DECLARE_LOGGING_CATEGORY(displayplugins)
@ -69,6 +69,9 @@ void OpenVrDisplayPlugin::internalActivate() {
_compositor = vr::VRCompositor(); _compositor = vr::VRCompositor();
Q_ASSERT(_compositor); 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. // set up default sensor space such that the UI overlay will align with the front of the room.
auto chaperone = vr::VRChaperone(); auto chaperone = vr::VRChaperone();
if (chaperone) { if (chaperone) {
@ -119,14 +122,11 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
float vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float); float vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
#if THREADED_PRESENT #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; const float NUM_PREDICTION_FRAMES = 3.0f;
float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons; float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons;
#else #else
uint64_t frameCounter; float predictedSecondsFromNow = frameDuration + vsyncToPhotons;
float timeSinceLastVsync;
_system->GetTimeSinceLastVsync(&timeSinceLastVsync, &frameCounter);
float predictedSecondsFromNow = 3.0f * frameDuration - timeSinceLastVsync + vsyncToPhotons;
#endif #endif
vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount]; vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
@ -144,6 +144,9 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
} }
void OpenVrDisplayPlugin::hmdPresent() { void OpenVrDisplayPlugin::hmdPresent() {
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
// Flip y-axis since GL UV coords are backwards. // Flip y-axis since GL UV coords are backwards.
static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 }; static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 };
static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 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_Left, &texture, &leftBounds);
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds); _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]; vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0); _compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);

View file

@ -35,6 +35,7 @@ protected:
void hmdPresent() override; void hmdPresent() override;
bool isHmdMounted() const override; bool isHmdMounted() const override;
void postPreview() override;
private: private:
vr::IVRSystem* _system { nullptr }; vr::IVRSystem* _system { nullptr };

View file

@ -51,7 +51,7 @@ vr::IVRSystem* acquireOpenVrSystem() {
if (!activeHmd) { if (!activeHmd) {
qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building"; qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building";
vr::EVRInitError eError = vr::VRInitError_None; 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; qCDebug(displayplugins) << "openvr display: HMD is " << activeHmd << " error is " << eError;
} }
if (activeHmd) { if (activeHmd) {

View file

@ -44,6 +44,14 @@ ready = function() {
var domainServer = remote.getGlobal('domainServer'); var domainServer = remote.getGlobal('domainServer');
var acMonitor = remote.getGlobal('acMonitor'); 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 = { var logWatchers = {
'ds': { 'ds': {
}, },
@ -83,7 +91,7 @@ ready = function() {
var logTail = new Tail(cleanFilePath, '\n', { start: start, interval: 500 }); var logTail = new Tail(cleanFilePath, '\n', { start: start, interval: 500 });
logTail.on('line', function(msg) { logTail.on('line', function(msg) {
appendLogMessage(msg, stream); pendingLines[stream].push(msg);
}); });
logTail.on('error', function(error) { logTail.on('error', function(error) {
@ -107,6 +115,7 @@ ready = function() {
} }
window.onbeforeunload = function(e) { window.onbeforeunload = function(e) {
clearInterval(interval);
domainServer.removeListener('logs-updated', updateLogFiles); domainServer.removeListener('logs-updated', updateLogFiles);
acMonitor.removeListener('logs-updated', updateLogFiles); acMonitor.removeListener('logs-updated', updateLogFiles);
}; };
@ -164,14 +173,23 @@ ready = function() {
return !filter || message.toLowerCase().indexOf(filter) >= 0; 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 id = name == "ds" ? "domain-server" : "assignment-client";
var $pidLog = $('#' + id); var $pidLog = $('#' + id);
var size = ++tabStates[id].size; var size = tabStates[id].size + array.length;
if (size > maxLogLines) { if (size > maxLogLines) {
$pidLog.find('div.log-line:first').remove(); $pidLog.find('div.log-line:lt(' + (size - maxLogLines) + ')').remove();
removed = true;
} }
var wasAtBottom = false; var wasAtBottom = false;
@ -179,17 +197,25 @@ ready = function() {
wasAtBottom = $pidLog[0].scrollTop >= ($pidLog[0].scrollHeight - $pidLog.height()); wasAtBottom = $pidLog[0].scrollTop >= ($pidLog[0].scrollHeight - $pidLog.height());
} }
var $logLine = $('<div class="log-line">').text(msg); for (line in array) {
if (!shouldDisplayLogMessage(msg)) { var $logLine = $('<div class="log-line">').text(array[line]);
$logLine.hide(); if (!shouldDisplayLogMessage(array[line])) {
$logLine.hide();
}
$pidLog.append($logLine);
} }
$pidLog.append($logLine); delete pendingLines[name];
pendingLines[name] = new Array();
if (wasAtBottom) { if (wasAtBottom) {
$pidLog.scrollTop($pidLog[0].scrollHeight); $pidLog.scrollTop($pidLog[0].scrollHeight);
} }
}
function flushPendingLines() {
appendLogMessages('ds');
appendLogMessages('ac');
} }
// handle filtering of table rows on input change // handle filtering of table rows on input change