From 13acfb325ae55db7ffde438c5e344a6658431f60 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 25 Oct 2018 15:06:15 -0700 Subject: [PATCH 01/10] fix collides with properties, fix ordering for start/finish/spread properties --- scripts/system/html/js/entityProperties.js | 216 ++++++++++----------- 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index d564a59a43..f73d634af9 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -710,15 +710,6 @@ const GROUPS = [ decimals: 2, propertyID: "particleRadius", }, - { - label: "Size Spread", - type: "slider", - min: 0, - max: 4, - step: 0.01, - decimals: 2, - propertyID: "radiusSpread", - }, { label: "Size Start", type: "slider", @@ -739,6 +730,15 @@ const GROUPS = [ propertyID: "radiusFinish", fallbackProperty: "particleRadius", }, + { + label: "Size Spread", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "radiusSpread", + }, ] }, { @@ -783,15 +783,6 @@ const GROUPS = [ decimals: 2, propertyID: "alpha", }, - { - label: "Alpha Spread", - type: "slider", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "alphaSpread", - }, { label: "Alpha Start", type: "slider", @@ -812,6 +803,15 @@ const GROUPS = [ propertyID: "alphaFinish", fallbackProperty: "alpha", }, + { + label: "Alpha Spread", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alphaSpread", + }, ] }, { @@ -853,17 +853,6 @@ const GROUPS = [ unit: "deg", propertyID: "particleSpin", }, - { - label: "Spin Spread", - type: "slider", - min: 0, - max: 360, - step: 1, - decimals: 0, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "spinSpread", - }, { label: "Spin Start", type: "slider", @@ -888,6 +877,17 @@ const GROUPS = [ propertyID: "spinFinish", fallbackProperty: "particleSpin", }, + { + label: "Spin Spread", + type: "slider", + min: 0, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinSpread", + }, { label: "Rotate with Entity", type: "bool", @@ -1355,15 +1355,15 @@ function getPropertyInputElement(propertyID) { } function enableChildren(el, selector) { - var elSelectors = el.querySelectorAll(selector); - for (var selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) { + let elSelectors = el.querySelectorAll(selector); + for (let selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) { elSelectors[selectorIndex].removeAttribute('disabled'); } } function disableChildren(el, selector) { - var elSelectors = el.querySelectorAll(selector); - for (var selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) { + let elSelectors = el.querySelectorAll(selector); + for (let selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) { elSelectors[selectorIndex].setAttribute('disabled', 'disabled'); } } @@ -1371,7 +1371,7 @@ function disableChildren(el, selector) { function enableProperties() { enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); enableChildren(document, ".colpick"); - var elLocked = getPropertyInputElement("locked"); + let elLocked = getPropertyInputElement("locked"); if (elLocked.checked === false) { removeStaticUserData(); @@ -1382,10 +1382,10 @@ function enableProperties() { function disableProperties() { disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); disableChildren(document, ".colpick"); - for (var pickKey in colorPickers) { + for (let pickKey in colorPickers) { colorPickers[pickKey].colpickHide(); } - var elLocked = getPropertyInputElement("locked"); + let elLocked = getPropertyInputElement("locked"); if (elLocked.checked === true) { if ($('#property-userData-editor').css('display') === "block") { @@ -1526,7 +1526,7 @@ function getPropertyValue(originalPropertyName) { * PROPERTY UPDATE FUNCTIONS */ -function updateProperty(originalPropertyName, propertyValue) { +function updateProperty(originalPropertyName, propertyValue, isParticleProperty) { let propertyUpdate = {}; // if this is a compound property name (i.e. animation.running) then split it by . up to 3 times let splitPropertyName = originalPropertyName.split('.'); @@ -1545,8 +1545,8 @@ function updateProperty(originalPropertyName, propertyValue) { propertyUpdate[originalPropertyName] = propertyValue; } // queue up particle property changes with the debounced sync to avoid - // causing particle emitting to reset each frame when updating values - if (properties[originalPropertyName].isParticleProperty) { + // causing particle emitting to reset excessively with each value change + if (isParticleProperty) { Object.keys(propertyUpdate).forEach(function (propertyUpdateKey) { particlePropertyUpdates[propertyUpdateKey] = propertyUpdate[propertyUpdateKey]; }); @@ -1569,30 +1569,29 @@ function updateProperties(propertiesToUpdate) { })); } - -function createEmitTextPropertyUpdateFunction(propertyName) { +function createEmitTextPropertyUpdateFunction(propertyName, isParticleProperty) { return function() { - updateProperty(propertyName, this.value); + updateProperty(propertyName, this.value, isParticleProperty); }; } -function createEmitCheckedPropertyUpdateFunction(propertyName, inverse) { +function createEmitCheckedPropertyUpdateFunction(propertyName, inverse, isParticleProperty) { return function() { - updateProperty(propertyName, inverse ? !this.checked : this.checked); + updateProperty(propertyName, inverse ? !this.checked : this.checked, isParticleProperty); }; } -function createEmitNumberPropertyUpdateFunction(propertyName, multiplier) { +function createEmitNumberPropertyUpdateFunction(propertyName, multiplier, isParticleProperty) { return function() { if (multiplier === undefined) { multiplier = 1; } let value = parseFloat(this.value) * multiplier; - updateProperty(propertyName, value); + updateProperty(propertyName, value, isParticleProperty); }; } -function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier) { +function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier, isParticleProperty) { return function () { if (multiplier === undefined) { multiplier = 1; @@ -1601,11 +1600,11 @@ function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier x: elX.value * multiplier, y: elY.value * multiplier }; - updateProperty(propertyName, newValue); + updateProperty(propertyName, newValue, isParticleProperty); }; } -function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multiplier) { +function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multiplier, isParticleProperty) { return function() { if (multiplier === undefined) { multiplier = 1; @@ -1615,26 +1614,26 @@ function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multi y: elY.value * multiplier, z: elZ.value * multiplier }; - updateProperty(propertyName, newValue); + updateProperty(propertyName, newValue, isParticleProperty); }; } -function createEmitColorPropertyUpdateFunction(propertyName, elRed, elGreen, elBlue) { +function createEmitColorPropertyUpdateFunction(propertyName, elRed, elGreen, elBlue, isParticleProperty) { return function() { - emitColorPropertyUpdate(propertyName, elRed.value, elGreen.value, elBlue.value); + emitColorPropertyUpdate(propertyName, elRed.value, elGreen.value, elBlue.value, isParticleProperty); }; } -function emitColorPropertyUpdate(propertyName, red, green, blue) { +function emitColorPropertyUpdate(propertyName, red, green, blue, isParticleProperty) { let newValue = { red: red, green: green, blue: blue }; - updateProperty(propertyName, newValue); + updateProperty(propertyName, newValue, isParticleProperty); } -function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) { +function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString, isParticleProperty) { if (subPropertyElement.checked) { if (propertyValue.indexOf(subPropertyString)) { propertyValue += subPropertyString + ','; @@ -1643,13 +1642,13 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen // We've unchecked, so remove propertyValue = propertyValue.replace(subPropertyString + ",", ""); } - updateProperty(propertyName, propertyValue); + updateProperty(propertyName, propertyValue, isParticleProperty); } -function createImageURLUpdateFunction(propertyName) { +function createImageURLUpdateFunction(propertyName, isParticleProperty) { return function () { - var newTextures = JSON.stringify({ "tex.picture": this.value }); - updateProperty(propertyName, newTextures); + let newTextures = JSON.stringify({ "tex.picture": this.value }); + updateProperty(propertyName, newTextures, isParticleProperty); }; } @@ -1672,7 +1671,7 @@ function createStringProperty(property, elProperty, elLabel) { elInput.readOnly = true; } - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName)); + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); elProperty.appendChild(elLabel); elProperty.appendChild(elInput); @@ -1708,10 +1707,10 @@ function createBoolProperty(property, elProperty, elLabel) { let subPropertyOf = propertyData.subPropertyOf; if (subPropertyOf !== undefined) { elInput.addEventListener('change', function() { - updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], elInput, propertyName); + updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], elInput, propertyName, property.isParticleProperty); }); } else { - elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse)); + elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse, property.isParticleProperty)); } return elInput; @@ -1744,7 +1743,7 @@ function createNumberProperty(property, elProperty, elLabel) { elInput.value = defaultValue; } - elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals)); + elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty)); elProperty.appendChild(elLabel); elProperty.appendChild(elInput); @@ -1790,7 +1789,7 @@ function createSliderProperty(property, elProperty, elLabel) { if (propertyData.multiplier !== undefined) { inputValue *= propertyData.multiplier; } - updateProperty(property.name, inputValue); + updateProperty(property.name, inputValue, property.isParticleProperty); }; elSlider.oninput = function (event) { let sliderValue = event.target.value; @@ -1806,7 +1805,7 @@ function createSliderProperty(property, elProperty, elLabel) { if (propertyData.multiplier !== undefined) { sliderValue *= propertyData.multiplier; } - updateProperty(property.name, sliderValue); + updateProperty(property.name, sliderValue, property.isParticleProperty); }; elDiv.appendChild(elLabel); @@ -1842,8 +1841,8 @@ function createVec3Property(property, elProperty, elLabel) { let elInputZ = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], propertyData.min, propertyData.max, propertyData.step); - let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, - elInputZ, propertyData.multiplier); + let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, elInputZ, + propertyData.multiplier, property.isParticleProperty); elInputX.addEventListener('change', inputChangeFunction); elInputY.addEventListener('change', inputChangeFunction); elInputZ.addEventListener('change', inputChangeFunction); @@ -1875,8 +1874,8 @@ function createVec2Property(property, elProperty, elLabel) { let elInputY = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], propertyData.min, propertyData.max, propertyData.step); - let inputChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elInputX, - elInputY, propertyData.multiplier); + let inputChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elInputX, elInputY, + propertyData.multiplier, property.isParticleProperty); elInputX.addEventListener('change', inputChangeFunction); elInputY.addEventListener('change', inputChangeFunction); @@ -1907,7 +1906,8 @@ function createColorProperty(property, elProperty, elLabel) { let elInputG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP); let elInputB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let inputChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elInputR, elInputG, elInputB); + let inputChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elInputR, elInputG, elInputB, + property.isParticleProperty); elInputR.addEventListener('change', inputChangeFunction); elInputG.addEventListener('change', inputChangeFunction); elInputB.addEventListener('change', inputChangeFunction); @@ -1963,7 +1963,7 @@ function createDropdownProperty(property, propertyID, elProperty, elLabel) { elInput.add(option); } - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName)); + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); elProperty.appendChild(elLabel); elProperty.appendChild(elInput); @@ -1990,7 +1990,7 @@ function createTextareaProperty(property, elProperty, elLabel) { elInput.readOnly = true; } - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName)); + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); elProperty.appendChild(elInput); @@ -2056,9 +2056,9 @@ function createTextureProperty(property, elProperty, elLabel) { elInput.imageLoad = imageLoad; elInput.oninput = function (event) { // Add throttle - var url = event.target.value; + let url = event.target.value; imageLoad(url); - updateProperty(property.name, url) + updateProperty(property.name, url, property.isParticleProperty) }; elInput.onchange = elInput.oninput; @@ -2199,7 +2199,7 @@ function reloadServerScripts() { function copySkyboxURLToAmbientURL() { let skyboxURL = getPropertyInputElement("skybox.url").value; getPropertyInputElement("ambientLight.ambientURL").value = skyboxURL; - updateProperty("ambientLight.ambientURL", skyboxURL); + updateProperty("ambientLight.ambientURL", skyboxURL, false); } @@ -2214,13 +2214,13 @@ function clearUserData() { showUserDataTextArea(); showNewJSONEditorButton(); hideSaveUserDataButton(); - updateProperty('userData', elUserData.value); + updateProperty('userData', elUserData.value, false); } function newJSONEditor() { deleteJSONEditor(); createJSONEditor(); - var data = {}; + let data = {}; setEditorJSON(data); hideUserDataTextArea(); hideNewJSONEditorButton(); @@ -2232,7 +2232,7 @@ function saveUserData() { } function setUserDataFromEditor(noUpdate) { - var json = null; + let json = null; try { json = editor.get(); } catch (e) { @@ -2241,7 +2241,7 @@ function setUserDataFromEditor(noUpdate) { if (json === null) { return; } else { - var text = editor.getText(); + let text = editor.getText(); if (noUpdate === true) { EventBridge.emitWebEvent( JSON.stringify({ @@ -2254,15 +2254,15 @@ function setUserDataFromEditor(noUpdate) { ); return; } else { - updateProperty('userData', text); + updateProperty('userData', text, false); } } } function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, removeKeys) { - var propertyUpdate = {}; - var parsedData = {}; - var keysToBeRemoved = removeKeys ? removeKeys : []; + let propertyUpdate = {}; + let parsedData = {}; + let keysToBeRemoved = removeKeys ? removeKeys : []; try { if ($('#property-userData-editor').css('height') !== "0px") { // if there is an expanded, we want to use its json. @@ -2277,14 +2277,14 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r if (!(groupName in parsedData)) { parsedData[groupName] = {}; } - var keys = Object.keys(updateKeyPair); + let keys = Object.keys(updateKeyPair); keys.forEach(function (key) { if (updateKeyPair[key] !== null && updateKeyPair[key] !== "null") { if (updateKeyPair[key] instanceof Element) { if (updateKeyPair[key].type === "checkbox") { parsedData[groupName][key] = updateKeyPair[key].checked; } else { - var val = isNaN(updateKeyPair[key].value) ? updateKeyPair[key].value : parseInt(updateKeyPair[key].value); + let val = isNaN(updateKeyPair[key].value) ? updateKeyPair[key].value : parseInt(updateKeyPair[key].value); parsedData[groupName][key] = val; } } else { @@ -2317,8 +2317,8 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r var editor = null; function createJSONEditor() { - var container = document.getElementById("property-userData-editor"); - var options = { + let container = document.getElementById("property-userData-editor"); + let options = { search: false, mode: 'tree', modes: ['code', 'tree'], @@ -2330,7 +2330,7 @@ function createJSONEditor() { alert('JSON editor:' + e); }, onChange: function() { - var currentJSONString = editor.getText(); + let currentJSONString = editor.getText(); if (currentJSONString === '{"":""}') { return; @@ -2431,13 +2431,13 @@ function clearMaterialData() { showMaterialDataTextArea(); showNewJSONMaterialEditorButton(); hideSaveMaterialDataButton(); - updateProperty('materialData', elMaterialData.value); + updateProperty('materialData', elMaterialData.value, false); } function newJSONMaterialEditor() { deleteJSONMaterialEditor(); createJSONMaterialEditor(); - var data = {}; + let data = {}; setMaterialEditorJSON(data); hideMaterialDataTextArea(); hideNewJSONMaterialEditorButton(); @@ -2449,7 +2449,7 @@ function saveMaterialData() { } function setMaterialDataFromEditor(noUpdate) { - var json = null; + let json = null; try { json = materialEditor.get(); } catch (e) { @@ -2458,7 +2458,7 @@ function setMaterialDataFromEditor(noUpdate) { if (json === null) { return; } else { - var text = materialEditor.getText(); + let text = materialEditor.getText(); if (noUpdate === true) { EventBridge.emitWebEvent( JSON.stringify({ @@ -2471,7 +2471,7 @@ function setMaterialDataFromEditor(noUpdate) { ); return; } else { - updateProperty('materialData', text); + updateProperty('materialData', text, false); } } } @@ -2479,8 +2479,8 @@ function setMaterialDataFromEditor(noUpdate) { var materialEditor = null; function createJSONMaterialEditor() { - var container = document.getElementById("property-materialData-editor"); - var options = { + let container = document.getElementById("property-materialData-editor"); + let options = { search: false, mode: 'tree', modes: ['code', 'tree'], @@ -2492,7 +2492,7 @@ function createJSONMaterialEditor() { alert('JSON editor:' + e); }, onChange: function() { - var currentJSONString = materialEditor.getText(); + let currentJSONString = materialEditor.getText(); if (currentJSONString === '{"":""}') { return; @@ -2582,11 +2582,11 @@ function saveJSONMaterialData(noUpdate) { } function bindAllNonJSONEditorElements() { - var inputs = $('input'); - var i; + let inputs = $('input'); + let i; for (i = 0; i < inputs.length; ++i) { - var input = inputs[i]; - var field = $(input); + let input = inputs[i]; + let field = $(input); // TODO FIXME: (JSHint) Functions declared within loops referencing // an outer scoped variable may lead to confusing semantics. field.on('focus', function(e) { @@ -2649,7 +2649,7 @@ function setDropdownValue(event) { */ function setTextareaScrolling(element) { - var isScrolling = element.scrollHeight > element.offsetHeight; + let isScrolling = element.scrollHeight > element.offsetHeight; element.setAttribute("scrolling", isScrolling ? "true" : "false"); } @@ -3229,22 +3229,22 @@ function loaded() { let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace"); let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh"); elParentMaterialNameString.addEventListener('change', function () { - updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + this.value); + updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + this.value, false); }); elParentMaterialNameNumber.addEventListener('change', function () { - updateProperty("parentMaterialName", this.value); + updateProperty("parentMaterialName", this.value, false); }); elParentMaterialNameCheckbox.addEventListener('change', function () { if (this.checked) { - updateProperty("parentMaterialName", elParentMaterialNameNumber.value); + updateProperty("parentMaterialName", elParentMaterialNameNumber.value, false); showParentMaterialNameBox(true, elParentMaterialNameNumber, elParentMaterialNameString); } else { - updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + elParentMaterialNameString.value); + updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + elParentMaterialNameString.value, false); showParentMaterialNameBox(false, elParentMaterialNameNumber, elParentMaterialNameString); } }); - getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures')); + getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures', false)); // Collapsible sections let elCollapsible = document.getElementsByClassName("section-header"); @@ -3339,12 +3339,12 @@ function loaded() { let propertyID = elDropdown.getAttribute("propertyID"); let property = properties[propertyID]; property.elInput = dt; - dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property.name)); + dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property.name, property.isParticleProperty)); } elDropdowns = document.getElementsByTagName("select"); while (elDropdowns.length > 0) { - var el = elDropdowns[0]; + let el = elDropdowns[0]; el.parentNode.removeChild(el); elDropdowns = document.getElementsByTagName("select"); } From 95d160a1701fe1ea3b290f1fe2b090dca0838111 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 28 Sep 2018 15:59:04 -0700 Subject: [PATCH 02/10] Working on mac GL issues --- CMakeLists.txt | 2 +- interface/src/Application.cpp | 45 ++-- interface/src/main.cpp | 13 ++ interface/src/ui/ApplicationOverlay.cpp | 1 + .../src/RenderableWebEntityItem.cpp | 1 + libraries/gl/src/gl/Context.cpp | 30 +-- libraries/gl/src/gl/Context.h | 11 +- libraries/gl/src/gl/ContextQt.cpp | 52 ++++- libraries/gl/src/gl/GLHelpers.cpp | 7 +- libraries/gl/src/gl/GLWidget.cpp | 4 +- libraries/gl/src/gl/GLWidget.h | 2 +- libraries/gl/src/gl/GLWindow.cpp | 2 +- libraries/gl/src/gl/OffscreenGLCanvas.cpp | 23 -- libraries/gl/src/gl/OffscreenGLCanvas.h | 3 - libraries/gl/src/gl/QOpenGLContextWrapper.cpp | 17 ++ libraries/gl/src/gl/QOpenGLContextWrapper.h | 5 + .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 199 ++++++++--------- .../gpu-gl-common/src/gpu/gl/GLBackend.h | 36 ++- .../qml/src/qml/impl/RenderEventHandler.cpp | 5 +- libraries/qml/src/qml/impl/TextureCache.cpp | 14 +- tests-manual/qml/qml/MacQml.qml | 77 +++++++ tests-manual/qml/src/MacQml.cpp | 78 +++++++ tests-manual/qml/src/MacQml.h | 16 ++ tests-manual/qml/src/StressWeb.cpp | 131 +++++++++++ tests-manual/qml/src/StressWeb.h | 34 +++ tests-manual/qml/src/TestCase.cpp | 25 +++ tests-manual/qml/src/TestCase.h | 23 ++ tests-manual/qml/src/main.cpp | 205 +++++------------- 28 files changed, 706 insertions(+), 355 deletions(-) create mode 100644 tests-manual/qml/qml/MacQml.qml create mode 100644 tests-manual/qml/src/MacQml.cpp create mode 100644 tests-manual/qml/src/MacQml.h create mode 100644 tests-manual/qml/src/StressWeb.cpp create mode 100644 tests-manual/qml/src/StressWeb.h create mode 100644 tests-manual/qml/src/TestCase.cpp create mode 100644 tests-manual/qml/src/TestCase.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d52a557cb1..6120e27b75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,7 @@ if (ANDROID) set(GLES_OPTION ON) set(PLATFORM_QT_COMPONENTS AndroidExtras WebView) else () - set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) + set(PLATFORM_QT_COMPONENTS WebEngine) endif () if (USE_GLES AND (NOT ANDROID)) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 58aad654db..700561325f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -52,6 +52,8 @@ #include #include +#include +#include #include #include @@ -971,9 +973,11 @@ OffscreenGLCanvas* _qmlShareContext { nullptr }; // and manually set THAT to be the shared context for the Chromium helper #if !defined(DISABLE_QML) OffscreenGLCanvas* _chromiumShareContext { nullptr }; -Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); #endif +Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); +Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context(); + Setting::Handle sessionRunTime{ "sessionRunTime", 0 }; const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 60.0f; @@ -1370,7 +1374,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _glWidget->setMouseTracking(true); // Make sure the window is set to the correct size by processing the pending events QCoreApplication::processEvents(); - _glWidget->createContext(); // Create the main thread context, the GPU backend initializeGL(); @@ -2727,46 +2730,58 @@ void Application::initializeGL() { _isGLInitialized = true; } + _glWidget->windowHandle()->setFormat(getDefaultOpenGLSurfaceFormat()); + + // When loading QtWebEngineWidgets, it creates a global share context on startup. + // We have to account for this possibility by checking here for an existing + // global share context + auto globalShareContext = qt_gl_global_share_context(); + _glWidget->createContext(globalShareContext); + if (!_glWidget->makeCurrent()) { qCWarning(interfaceapp, "Unable to make window context current"); } #if !defined(DISABLE_QML) - // Build a shared canvas / context for the Chromium processes - { - // Disable signed distance field font rendering on ATI/AMD GPUs, due to - // https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app - std::string vendor{ (const char*)glGetString(GL_VENDOR) }; - if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) { - qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text")); - } + // Disable signed distance field font rendering on ATI/AMD GPUs, due to + // https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app + std::string vendor{ (const char*)glGetString(GL_VENDOR) }; + if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) { + qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text")); + } + // Build a shared canvas / context for the Chromium processes + if (!globalShareContext) { // Chromium rendering uses some GL functions that prevent nSight from capturing // frames, so we only create the shared context if nsight is NOT active. if (!nsightActive()) { - _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext = new OffscreenGLCanvas(); _chromiumShareContext->setObjectName("ChromiumShareContext"); _chromiumShareContext->create(_glWidget->qglContext()); if (!_chromiumShareContext->makeCurrent()) { qCWarning(interfaceapp, "Unable to make chromium shared context current"); } - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + globalShareContext = _chromiumShareContext->getContext(); + qt_gl_set_global_share_context(globalShareContext); _chromiumShareContext->doneCurrent(); // Restore the GL widget context if (!_glWidget->makeCurrent()) { qCWarning(interfaceapp, "Unable to make window context current"); } - } else { - qCWarning(interfaceapp) << "nSight detected, disabling chrome rendering"; } } #endif + if (!globalShareContext) { + globalShareContext = _glWidget->qglContext(); + qt_gl_set_global_share_context(globalShareContext); + } + // Build a shared canvas / context for the QML rendering { _qmlShareContext = new OffscreenGLCanvas(); _qmlShareContext->setObjectName("QmlShareContext"); - _qmlShareContext->create(_glWidget->qglContext()); + _qmlShareContext->create(globalShareContext); if (!_qmlShareContext->makeCurrent()) { qCWarning(interfaceapp, "Unable to make QML shared context current"); } diff --git a/interface/src/main.cpp b/interface/src/main.cpp index d9396ae4d1..12a02ffd32 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "AddressManager.h" #include "Application.h" @@ -40,6 +41,18 @@ extern "C" { #endif int main(int argc, const char* argv[]) { + auto format = getDefaultOpenGLSurfaceFormat(); +#ifdef Q_OS_MAC + // Deal with some weirdness in the chromium context sharing on Mac. + // The primary share context needs to be 3.2, so that the Chromium will + // succeed in it's creation of it's command stub contexts. + format.setVersion(3, 2); + // This appears to resolve the issues with corrupted fonts on OSX. No + // idea why. + qputenv("QT_ENABLE_GLYPH_CACHE_WORKAROUND", "true"); + // https://i.kym-cdn.com/entries/icons/original/000/008/342/ihave.jpg +#endif + QSurfaceFormat::setDefaultFormat(format); setupHifiApplication(BuildInfo::INTERFACE_NAME); QStringList arguments; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 108f20b2dd..2fb40c8c30 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -115,6 +115,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { batch.resetViewTransform(); batch.setResourceTexture(0, _uiTexture); geometryCache->renderUnitQuad(batch, glm::vec4(1), _qmlGeometryId); + batch.setResourceTexture(0, nullptr); } void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index bc9ac84c91..bce5225a5c 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -261,6 +261,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { DependencyManager::get()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId); batch.popProjectionJitter(); + batch.setResourceTexture(0, nullptr); } bool WebEntityRenderer::hasWebSurface() { diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index ad7e51fbd3..cd2b19beec 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -25,6 +25,7 @@ #include "GLLogging.h" #include "Config.h" #include "GLHelpers.h" +#include "QOpenGLContextWrapper.h" using namespace gl; @@ -68,8 +69,6 @@ void Context::updateSwapchainMemoryUsage(size_t prevSize, size_t newSize) { } -Context* Context::PRIMARY = nullptr; - Context::Context() {} Context::Context(QWindow* window) { @@ -97,9 +96,6 @@ void Context::release() { _context = nullptr; #endif _window = nullptr; - if (PRIMARY == this) { - PRIMARY = nullptr; - } updateSwapchainMemoryCounter(); } @@ -235,16 +231,10 @@ typedef HGLRC(APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShare GLAPI PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; GLAPI PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; +Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context(); -void Context::create() { - if (!PRIMARY) { - PRIMARY = static_cast(qApp->property(hifi::properties::gl::PRIMARY_CONTEXT).value()); - } - - if (PRIMARY) { - _version = PRIMARY->_version; - } +void Context::create(QOpenGLContext* shareContext) { assert(0 != _hwnd); assert(0 == _hdc); auto hwnd = _hwnd; @@ -338,7 +328,10 @@ void Context::create() { contextAttribs.push_back(0); } contextAttribs.push_back(0); - auto shareHglrc = PRIMARY ? PRIMARY->_hglrc : 0; + if (!shareContext) { + shareContext = qt_gl_global_share_context(); + } + HGLRC shareHglrc = (HGLRC)QOpenGLContextWrapper::nativeContext(shareContext); _hglrc = wglCreateContextAttribsARB(_hdc, shareHglrc, &contextAttribs[0]); } @@ -346,11 +339,6 @@ void Context::create() { throw std::runtime_error("Could not create GL context"); } - if (!PRIMARY) { - PRIMARY = this; - qApp->setProperty(hifi::properties::gl::PRIMARY_CONTEXT, QVariant::fromValue((void*)PRIMARY)); - } - if (!makeCurrent()) { throw std::runtime_error("Could not make context current"); } @@ -368,7 +356,7 @@ OffscreenContext::~OffscreenContext() { _window->deleteLater(); } -void OffscreenContext::create() { +void OffscreenContext::create(QOpenGLContext* shareContext) { if (!_window) { _window = new QWindow(); _window->setFlags(Qt::MSWindowsOwnDC); @@ -379,5 +367,5 @@ void OffscreenContext::create() { qCDebug(glLogging) << "New Offscreen GLContext, window size = " << windowSize.width() << " , " << windowSize.height(); QGuiApplication::processEvents(); } - Parent::create(); + Parent::create(shareContext); } diff --git a/libraries/gl/src/gl/Context.h b/libraries/gl/src/gl/Context.h index b6160cbd6c..05cb361725 100644 --- a/libraries/gl/src/gl/Context.h +++ b/libraries/gl/src/gl/Context.h @@ -21,6 +21,7 @@ class QSurface; class QWindow; class QOpenGLContext; class QThread; +class QOpenGLDebugMessage; #if defined(Q_OS_WIN) #define GL_CUSTOM_CONTEXT @@ -30,7 +31,6 @@ namespace gl { class Context { protected: QWindow* _window { nullptr }; - static Context* PRIMARY; static void destroyContext(QOpenGLContext* context); #if defined(GL_CUSTOM_CONTEXT) uint32_t _version { 0x0401 }; @@ -48,6 +48,9 @@ namespace gl { public: static bool enableDebugLogger(); + static void debugMessageHandler(const QOpenGLDebugMessage &debugMessage); + static void setupDebugLogging(QOpenGLContext* context); + Context(); Context(QWindow* window); void release(); @@ -59,14 +62,14 @@ namespace gl { static void makeCurrent(QOpenGLContext* context, QSurface* surface); void swapBuffers(); void doneCurrent(); - virtual void create(); + virtual void create(QOpenGLContext* shareContext = nullptr); QOpenGLContext* qglContext(); void moveToThread(QThread* thread); static size_t evalSurfaceMemoryUsage(uint32_t width, uint32_t height, uint32_t pixelSize); static size_t getSwapchainMemoryUsage(); static void updateSwapchainMemoryUsage(size_t prevSize, size_t newSize); - + private: static std::atomic _totalSwapchainMemoryUsage; @@ -81,7 +84,7 @@ namespace gl { QWindow* _window { nullptr }; public: virtual ~OffscreenContext(); - void create() override; + void create(QOpenGLContext* shareContext = nullptr) override; }; } diff --git a/libraries/gl/src/gl/ContextQt.cpp b/libraries/gl/src/gl/ContextQt.cpp index dd65c3076c..f554877b2a 100644 --- a/libraries/gl/src/gl/ContextQt.cpp +++ b/libraries/gl/src/gl/ContextQt.cpp @@ -17,6 +17,8 @@ #include #endif +#include + #include "GLHelpers.h" using namespace gl; @@ -47,6 +49,32 @@ void Context::moveToThread(QThread* thread) { qglContext()->moveToThread(thread); } +void Context::debugMessageHandler(const QOpenGLDebugMessage& debugMessage) { + auto severity = debugMessage.severity(); + switch (severity) { + case QOpenGLDebugMessage::NotificationSeverity: + case QOpenGLDebugMessage::LowSeverity: + return; + default: + break; + } + qDebug(glLogging) << debugMessage; + return; +} + +void Context::setupDebugLogging(QOpenGLContext *context) { + QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(context); + QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, nullptr, [](const QOpenGLDebugMessage& message){ + Context::debugMessageHandler(message); + }); + if (logger->initialize()) { + logger->enableMessages(); + logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); + } else { + qCWarning(glLogging) << "OpenGL context does not support debugging"; + } +} + #ifndef GL_CUSTOM_CONTEXT bool Context::makeCurrent() { updateSwapchainMemoryCounter(); @@ -65,21 +93,29 @@ void Context::doneCurrent() { } } +Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context(); const QSurfaceFormat& getDefaultOpenGLSurfaceFormat(); - -void Context::create() { +void Context::create(QOpenGLContext* shareContext) { _context = new QOpenGLContext(); - if (PRIMARY) { - _context->setShareContext(PRIMARY->qglContext()); - } else { - PRIMARY = this; + _context->setFormat(_window->format()); + if (!shareContext) { + shareContext = qt_gl_global_share_context(); } - _context->setFormat(getDefaultOpenGLSurfaceFormat()); - _context->create(); + _context->setShareContext(shareContext); + _context->create(); _swapchainPixelSize = evalGLFormatSwapchainPixelSize(_context->format()); updateSwapchainMemoryCounter(); + + if (!makeCurrent()) { + throw std::runtime_error("Could not make context current"); + } + if (enableDebugLogger()) { + setupDebugLogging(_context); + } + doneCurrent(); + } #endif diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 7ebba4f8d8..c22bd0dde5 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -38,10 +38,15 @@ void gl::getTargetVersion(int& major, int& minor) { #if defined(USE_GLES) major = 3; minor = 2; +#else +#if defined(Q_OS_MAC) + major = 4; + minor = 1; #else major = 4; minor = disableGl45() ? 1 : 5; #endif +#endif } const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { @@ -57,6 +62,7 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { #else format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); #endif + format.setOption(QSurfaceFormat::DebugContext); // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); @@ -64,7 +70,6 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { ::gl::getTargetVersion(major, minor); format.setMajorVersion(major); format.setMinorVersion(minor); - QSurfaceFormat::setDefaultFormat(format); }); return format; } diff --git a/libraries/gl/src/gl/GLWidget.cpp b/libraries/gl/src/gl/GLWidget.cpp index 1c0ad1a85e..94702a9906 100644 --- a/libraries/gl/src/gl/GLWidget.cpp +++ b/libraries/gl/src/gl/GLWidget.cpp @@ -63,10 +63,10 @@ int GLWidget::getDeviceHeight() const { return height() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f); } -void GLWidget::createContext() { +void GLWidget::createContext(QOpenGLContext* shareContext) { _context = new gl::Context(); _context->setWindow(windowHandle()); - _context->create(); + _context->create(shareContext); _context->makeCurrent(); _context->clear(); _context->doneCurrent(); diff --git a/libraries/gl/src/gl/GLWidget.h b/libraries/gl/src/gl/GLWidget.h index a0bf8ea0b0..777d43e8af 100644 --- a/libraries/gl/src/gl/GLWidget.h +++ b/libraries/gl/src/gl/GLWidget.h @@ -29,7 +29,7 @@ public: int getDeviceHeight() const; QSize getDeviceSize() const { return QSize(getDeviceWidth(), getDeviceHeight()); } QPaintEngine* paintEngine() const override; - void createContext(); + void createContext(QOpenGLContext* shareContext = nullptr); bool makeCurrent(); void doneCurrent(); void swapBuffers(); diff --git a/libraries/gl/src/gl/GLWindow.cpp b/libraries/gl/src/gl/GLWindow.cpp index e1e6279b1c..7930e050de 100644 --- a/libraries/gl/src/gl/GLWindow.cpp +++ b/libraries/gl/src/gl/GLWindow.cpp @@ -22,7 +22,7 @@ void GLWindow::createContext(QOpenGLContext* shareContext) { void GLWindow::createContext(const QSurfaceFormat& format, QOpenGLContext* shareContext) { _context = new gl::Context(); _context->setWindow(this); - _context->create(); + _context->create(shareContext); _context->makeCurrent(); _context->clear(); } diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 6598a26c99..1256e316ff 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -75,32 +75,9 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { } #endif - if (gl::Context::enableDebugLogger()) { - _context->makeCurrent(_offscreenSurface); - QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this); - connect(logger, &QOpenGLDebugLogger::messageLogged, this, &OffscreenGLCanvas::onMessageLogged); - logger->initialize(); - logger->enableMessages(); - logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); - _context->doneCurrent(); - } - return true; } -void OffscreenGLCanvas::onMessageLogged(const QOpenGLDebugMessage& debugMessage) { - auto severity = debugMessage.severity(); - switch (severity) { - case QOpenGLDebugMessage::NotificationSeverity: - case QOpenGLDebugMessage::LowSeverity: - return; - default: - break; - } - qDebug(glLogging) << debugMessage; - return; -} - bool OffscreenGLCanvas::makeCurrent() { bool result = _context->makeCurrent(_offscreenSurface); if (glGetString) { diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h index a4960ae234..374720a910 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -35,9 +35,6 @@ public: void setThreadContext(); static bool restoreThreadContext(); -private slots: - void onMessageLogged(const QOpenGLDebugMessage &debugMessage); - protected: void clearThreadContext(); diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp index 0b153a5ae8..fbebb1128d 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp @@ -13,6 +13,10 @@ #include +#ifdef Q_OS_WIN +#include +#endif + uint32_t QOpenGLContextWrapper::currentContextVersion() { QOpenGLContext* context = QOpenGLContext::currentContext(); if (!context) { @@ -45,6 +49,19 @@ void QOpenGLContextWrapper::setFormat(const QSurfaceFormat& format) { _context->setFormat(format); } +#ifdef Q_OS_WIN +void* QOpenGLContextWrapper::nativeContext(QOpenGLContext* context) { + HGLRC result = 0; + if (context != nullptr) { + auto nativeHandle = context->nativeHandle(); + if (nativeHandle.canConvert()) { + result = nativeHandle.value().context(); + } + } + return result; +} +#endif + bool QOpenGLContextWrapper::create() { return _context->create(); } diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.h b/libraries/gl/src/gl/QOpenGLContextWrapper.h index b09ad1a4c3..32ba7f22e8 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.h +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.h @@ -13,6 +13,7 @@ #define hifi_QOpenGLContextWrapper_h #include +#include class QOpenGLContext; class QSurface; @@ -21,6 +22,10 @@ class QThread; class QOpenGLContextWrapper { public: +#ifdef Q_OS_WIN + static void* nativeContext(QOpenGLContext* context); +#endif + QOpenGLContextWrapper(); QOpenGLContextWrapper(QOpenGLContext* context); virtual ~QOpenGLContextWrapper(); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 1203e65685..c1ce05c18b 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -708,37 +708,37 @@ void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { void GLBackend::releaseBuffer(GLuint id, Size size) const { Lock lock(_trashMutex); - _buffersTrash.push_back({ id, size }); + _currentFrameTrash.buffersTrash.push_back({ id, size }); } void GLBackend::releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const { Lock lock(_trashMutex); - _externalTexturesTrash.push_back({ id, recycler }); + _currentFrameTrash.externalTexturesTrash.push_back({ id, recycler }); } void GLBackend::releaseTexture(GLuint id, Size size) const { Lock lock(_trashMutex); - _texturesTrash.push_back({ id, size }); + _currentFrameTrash.texturesTrash.push_back({ id, size }); } void GLBackend::releaseFramebuffer(GLuint id) const { Lock lock(_trashMutex); - _framebuffersTrash.push_back(id); + _currentFrameTrash.framebuffersTrash.push_back(id); } void GLBackend::releaseShader(GLuint id) const { Lock lock(_trashMutex); - _shadersTrash.push_back(id); + _currentFrameTrash.shadersTrash.push_back(id); } void GLBackend::releaseProgram(GLuint id) const { Lock lock(_trashMutex); - _programsTrash.push_back(id); + _currentFrameTrash.programsTrash.push_back(id); } void GLBackend::releaseQuery(GLuint id) const { Lock lock(_trashMutex); - _queriesTrash.push_back(id); + _currentFrameTrash.queriesTrash.push_back(id); } void GLBackend::queueLambda(const std::function lambda) const { @@ -746,6 +746,81 @@ void GLBackend::queueLambda(const std::function lambda) const { _lambdaQueue.push_back(lambda); } +void GLBackend::FrameTrash::cleanup() { + glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(fence); + + { + std::vector ids; + ids.reserve(buffersTrash.size()); + for (auto pair : buffersTrash) { + ids.push_back(pair.first); + } + if (!ids.empty()) { + glDeleteBuffers((GLsizei)ids.size(), ids.data()); + } + } + + { + std::vector ids; + ids.reserve(framebuffersTrash.size()); + for (auto id : framebuffersTrash) { + ids.push_back(id); + } + if (!ids.empty()) { + glDeleteFramebuffers((GLsizei)ids.size(), ids.data()); + } + } + + { + std::vector ids; + ids.reserve(texturesTrash.size()); + for (auto pair : texturesTrash) { + ids.push_back(pair.first); + } + if (!ids.empty()) { + glDeleteTextures((GLsizei)ids.size(), ids.data()); + } + } + + { + if (!externalTexturesTrash.empty()) { + std::vector fences; + fences.resize(externalTexturesTrash.size()); + for (size_t i = 0; i < externalTexturesTrash.size(); ++i) { + fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + // External texture fences will be read in another thread/context, so we need a flush + glFlush(); + size_t index = 0; + for (auto pair : externalTexturesTrash) { + auto fence = fences[index++]; + pair.second(pair.first, fence); + } + } + } + + for (auto id : programsTrash) { + glDeleteProgram(id); + } + + for (auto id : shadersTrash) { + glDeleteShader(id); + } + + { + std::vector ids; + ids.reserve(queriesTrash.size()); + for (auto id : queriesTrash) { + ids.push_back(id); + } + if (!ids.empty()) { + glDeleteQueries((GLsizei)ids.size(), ids.data()); + } + } + +} + void GLBackend::recycle() const { PROFILE_RANGE(render_gpu_gl, __FUNCTION__) { @@ -759,112 +834,16 @@ void GLBackend::recycle() const { } } - { - std::vector ids; - std::list> buffersTrash; - { - Lock lock(_trashMutex); - std::swap(_buffersTrash, buffersTrash); - } - ids.reserve(buffersTrash.size()); - for (auto pair : buffersTrash) { - ids.push_back(pair.first); - } - if (!ids.empty()) { - glDeleteBuffers((GLsizei)ids.size(), ids.data()); - } + while (!_previousFrameTrashes.empty()) { + _previousFrameTrashes.front().cleanup(); + _previousFrameTrashes.pop_front(); } + _previousFrameTrashes.emplace_back(); { - std::vector ids; - std::list framebuffersTrash; - { - Lock lock(_trashMutex); - std::swap(_framebuffersTrash, framebuffersTrash); - } - ids.reserve(framebuffersTrash.size()); - for (auto id : framebuffersTrash) { - ids.push_back(id); - } - if (!ids.empty()) { - glDeleteFramebuffers((GLsizei)ids.size(), ids.data()); - } - } - - { - std::vector ids; - std::list> texturesTrash; - { - Lock lock(_trashMutex); - std::swap(_texturesTrash, texturesTrash); - } - ids.reserve(texturesTrash.size()); - for (auto pair : texturesTrash) { - ids.push_back(pair.first); - } - if (!ids.empty()) { - glDeleteTextures((GLsizei)ids.size(), ids.data()); - } - } - - { - std::list> externalTexturesTrash; - { - Lock lock(_trashMutex); - std::swap(_externalTexturesTrash, externalTexturesTrash); - } - if (!externalTexturesTrash.empty()) { - std::vector fences; - fences.resize(externalTexturesTrash.size()); - for (size_t i = 0; i < externalTexturesTrash.size(); ++i) { - fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - } - // External texture fences will be read in another thread/context, so we need a flush - glFlush(); - size_t index = 0; - for (auto pair : externalTexturesTrash) { - auto fence = fences[index++]; - pair.second(pair.first, fence); - } - } - } - - { - std::list programsTrash; - { - Lock lock(_trashMutex); - std::swap(_programsTrash, programsTrash); - } - for (auto id : programsTrash) { - glDeleteProgram(id); - } - } - - { - std::list shadersTrash; - { - Lock lock(_trashMutex); - std::swap(_shadersTrash, shadersTrash); - } - for (auto id : shadersTrash) { - glDeleteShader(id); - } - } - - { - std::vector ids; - std::list queriesTrash; - { - Lock lock(_trashMutex); - std::swap(_queriesTrash, queriesTrash); - } - ids.reserve(queriesTrash.size()); - for (auto id : queriesTrash) { - ids.push_back(id); - } - if (!ids.empty()) { - glDeleteQueries((GLsizei)ids.size(), ids.data()); - } + Lock lock(_trashMutex); + _previousFrameTrashes.back().swap(_currentFrameTrash); + _previousFrameTrashes.back().fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); } _textureManagement._transferEngine->manageMemory(); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index c309bcb864..37dde5b08e 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -419,16 +419,34 @@ protected: static const size_t INVALID_OFFSET = (size_t)-1; bool _inRenderTransferPass{ false }; int _currentDraw{ -1 }; - - std::list profileRanges; + + struct FrameTrash { + GLsync fence = nullptr; + std::list> buffersTrash; + std::list> texturesTrash; + std::list> externalTexturesTrash; + std::list framebuffersTrash; + std::list shadersTrash; + std::list programsTrash; + std::list queriesTrash; + + void swap(FrameTrash& other) { + buffersTrash.swap(other.buffersTrash); + texturesTrash.swap(other.texturesTrash); + externalTexturesTrash.swap(other.externalTexturesTrash); + framebuffersTrash.swap(other.framebuffersTrash); + shadersTrash.swap(other.shadersTrash); + programsTrash.swap(other.programsTrash); + queriesTrash.swap(other.queriesTrash); + } + + void cleanup(); + }; + mutable Mutex _trashMutex; - mutable std::list> _buffersTrash; - mutable std::list> _texturesTrash; - mutable std::list> _externalTexturesTrash; - mutable std::list _framebuffersTrash; - mutable std::list _shadersTrash; - mutable std::list _programsTrash; - mutable std::list _queriesTrash; + mutable FrameTrash _currentFrameTrash; + mutable std::list _previousFrameTrashes; + std::list profileRanges; mutable std::list> _lambdaQueue; void renderPassTransfer(const Batch& batch); diff --git a/libraries/qml/src/qml/impl/RenderEventHandler.cpp b/libraries/qml/src/qml/impl/RenderEventHandler.cpp index 39f3123d40..8a56929e6b 100644 --- a/libraries/qml/src/qml/impl/RenderEventHandler.cpp +++ b/libraries/qml/src/qml/impl/RenderEventHandler.cpp @@ -139,9 +139,9 @@ void RenderEventHandler::onRender() { glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + // Fence will be used in another thread / context, so a flush is required glFlush(); _shared->updateTextureAndFence({ texture, fence }); - // Fence will be used in another thread / context, so a flush is required _shared->_quickWindow->resetOpenGLState(); } } @@ -167,4 +167,5 @@ void RenderEventHandler::onQuit() { moveToThread(qApp->thread()); QThread::currentThread()->quit(); } -#endif \ No newline at end of file + +#endif diff --git a/libraries/qml/src/qml/impl/TextureCache.cpp b/libraries/qml/src/qml/impl/TextureCache.cpp index 7af8fa1ac9..7b4fb3adaf 100644 --- a/libraries/qml/src/qml/impl/TextureCache.cpp +++ b/libraries/qml/src/qml/impl/TextureCache.cpp @@ -51,8 +51,10 @@ uint32_t TextureCache::acquireTexture(const QSize& size) { if (!textureSet.returnedTextures.empty()) { auto textureAndFence = textureSet.returnedTextures.front(); textureSet.returnedTextures.pop_front(); - glWaitSync((GLsync)textureAndFence.second, 0, GL_TIMEOUT_IGNORED); - glDeleteSync((GLsync)textureAndFence.second); + if (textureAndFence.second) { + glWaitSync((GLsync)textureAndFence.second, 0, GL_TIMEOUT_IGNORED); + glDeleteSync((GLsync)textureAndFence.second); + } return textureAndFence.first; } return createTexture(size); @@ -101,9 +103,11 @@ void TextureCache::destroyTexture(uint32_t texture) { void TextureCache::destroy(const Value& textureAndFence) { const auto& fence = textureAndFence.second; - // FIXME prevents crash on shutdown, but we should migrate to a global functions object owned by the shared context. - glWaitSync((GLsync)fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync((GLsync)fence); + if (fence) { + // FIXME prevents crash on shutdown, but we should migrate to a global functions object owned by the shared context. + glWaitSync((GLsync)fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync((GLsync)fence); + } destroyTexture(textureAndFence.first); } diff --git a/tests-manual/qml/qml/MacQml.qml b/tests-manual/qml/qml/MacQml.qml new file mode 100644 index 0000000000..bb7e3a0dff --- /dev/null +++ b/tests-manual/qml/qml/MacQml.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Controls 1.2 +import QtWebEngine 1.5 + +Item { + width: 640 + height: 480 + + Rectangle { + width: 5 + height: 5 + color: "red" + ColorAnimation on color { loops: Animation.Infinite; from: "red"; to: "yellow"; duration: 1000 } + } + + + WebEngineView { + id: root + url: "https://google.com/" + x: 6; y: 6; + width: parent.width * 0.8 + height: parent.height * 0.8 + + } +} diff --git a/tests-manual/qml/src/MacQml.cpp b/tests-manual/qml/src/MacQml.cpp new file mode 100644 index 0000000000..7f7854ce87 --- /dev/null +++ b/tests-manual/qml/src/MacQml.cpp @@ -0,0 +1,78 @@ +#include "MacQml.h" + +#include + +#include + +#include + +using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence; +// +//void MacQml::destroySurface(QmlInfo& qmlInfo) { +// auto& surface = qmlInfo.surface; +// auto& currentTexture = qmlInfo.texture; +// if (currentTexture) { +// auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +// glFlush(); +// _discardLamdba(currentTexture, readFence); +// } +// auto webView = surface->getRootItem(); +// if (webView) { +// // stop loading +// QMetaObject::invokeMethod(webView, "stop"); +// webView->setProperty(URL_PROPERTY, "about:blank"); +// } +// surface->pause(); +// surface.reset(); +//} + +void MacQml::update() { + auto rootItem =_surface->getRootItem(); + float now = sinf(secTimestampNow()); + rootItem->setProperty("level", abs(now)); + rootItem->setProperty("muted", now > 0.0f); + rootItem->setProperty("statsValue", rand()); + + // Fetch any new textures + TextureAndFence newTextureAndFence; + if (_surface->fetchTexture(newTextureAndFence)) { + if (_texture != 0) { + auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + _discardLamdba(_texture, readFence); + } + _texture = newTextureAndFence.first; + _glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED); + } +} + +void MacQml::init() { + Parent::init(); + _glf.glGenFramebuffers(1, &_fbo); + _surface.reset(new hifi::qml::OffscreenSurface()); + //QUrl url =getTestResource("qml/main.qml"); + QUrl url = getTestResource("qml/MacQml.qml"); + hifi::qml::QmlContextObjectCallback callback =[](QQmlContext* context, QQuickItem* item) { + }; + _surface->load(url, callback); + _surface->resize(_window->size()); + _surface->resume(); + +} + +void MacQml::draw() { + auto size = _window->geometry().size(); + if (_texture) { + _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo); + _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); + _glf.glBlitFramebuffer( + // src coordinates + 0, 0, size.width(), size.height(), + // dst coordinates + 0, 0, size.width(), size.height(), + // blit mask and filter + GL_COLOR_BUFFER_BIT, GL_NEAREST); + _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + } +} diff --git a/tests-manual/qml/src/MacQml.h b/tests-manual/qml/src/MacQml.h new file mode 100644 index 0000000000..50f71cb72e --- /dev/null +++ b/tests-manual/qml/src/MacQml.h @@ -0,0 +1,16 @@ +#include "TestCase.h" + +#include + +class MacQml : public TestCase { + using Parent = TestCase; +public: + GLuint _texture{ 0 }; + QmlPtr _surface; + GLuint _fbo{ 0 }; + + MacQml(const QWindow* window) : Parent(window) {} + void update() override; + void init() override; + void draw() override; +}; diff --git a/tests-manual/qml/src/StressWeb.cpp b/tests-manual/qml/src/StressWeb.cpp new file mode 100644 index 0000000000..71293feb9a --- /dev/null +++ b/tests-manual/qml/src/StressWeb.cpp @@ -0,0 +1,131 @@ +#include "StressWeb.h" + +#include + +#include + +using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence; + +static const int DEFAULT_MAX_FPS = 10; +static const QString CONTROL_URL{ "/qml/controls/WebEntityView.qml" }; +static const char* URL_PROPERTY{ "url" }; + +QString StressWeb::getSourceUrl(bool video) { + static const std::vector SOURCE_URLS{ + "https://www.reddit.com/wiki/random", + "https://en.wikipedia.org/wiki/Wikipedia:Random", + "https://slashdot.org/", + }; + + static const std::vector VIDEO_SOURCE_URLS{ + "https://www.youtube.com/watch?v=gDXwhHm4GhM", + "https://www.youtube.com/watch?v=Ch_hoYPPeGc", + }; + + const auto& sourceUrls = video ? VIDEO_SOURCE_URLS : SOURCE_URLS; + auto index = rand() % sourceUrls.size(); + return sourceUrls[index]; +} + + + +void StressWeb::buildSurface(QmlInfo& qmlInfo, bool video) { + ++_surfaceCount; + auto lifetimeSecs = (uint32_t)(5.0f + (randFloat() * 10.0f)); + auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs); + qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow(); + qmlInfo.texture = 0; + qmlInfo.surface.reset(new hifi::qml::OffscreenSurface()); + qmlInfo.surface->load(getTestResource(CONTROL_URL), [video](QQmlContext* context, QQuickItem* item) { + item->setProperty(URL_PROPERTY, getSourceUrl(video)); + }); + qmlInfo.surface->setMaxFps(DEFAULT_MAX_FPS); + qmlInfo.surface->resize(_qmlSize); + qmlInfo.surface->resume(); +} + +void StressWeb::destroySurface(QmlInfo& qmlInfo) { + auto& surface = qmlInfo.surface; + auto& currentTexture = qmlInfo.texture; + if (currentTexture) { + auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + _discardLamdba(currentTexture, readFence); + } + auto webView = surface->getRootItem(); + if (webView) { + // stop loading + QMetaObject::invokeMethod(webView, "stop"); + webView->setProperty(URL_PROPERTY, "about:blank"); + } + surface->pause(); + surface.reset(); +} + +void StressWeb::update() { + auto now = usecTimestampNow(); + // Fetch any new textures + for (size_t x = 0; x < DIVISIONS_X; ++x) { + for (size_t y = 0; y < DIVISIONS_Y; ++y) { + auto& qmlInfo = _surfaces[x][y]; + if (!qmlInfo.surface) { + if (now < _createStopTime && randFloat() > 0.99f) { + buildSurface(qmlInfo, x == 0 && y == 0); + } else { + continue; + } + } + + if (now > qmlInfo.lifetime) { + destroySurface(qmlInfo); + continue; + } + + auto& surface = qmlInfo.surface; + auto& currentTexture = qmlInfo.texture; + + TextureAndFence newTextureAndFence; + if (surface->fetchTexture(newTextureAndFence)) { + if (currentTexture != 0) { + auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + _discardLamdba(currentTexture, readFence); + } + currentTexture = newTextureAndFence.first; + _glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED); + } + } + } +} + +void StressWeb::init() { + Parent::init(); + _createStopTime = usecTimestampNow() + (3000u * USECS_PER_SECOND); + _glf.glGenFramebuffers(1, &_fbo); +} + +void StressWeb::draw() { + auto size = _window->geometry().size(); + auto incrementX = size.width() / DIVISIONS_X; + auto incrementY = size.height() / DIVISIONS_Y; + + for (uint32_t x = 0; x < DIVISIONS_X; ++x) { + for (uint32_t y = 0; y < DIVISIONS_Y; ++y) { + auto& qmlInfo = _surfaces[x][y]; + if (!qmlInfo.surface || !qmlInfo.texture) { + continue; + } + _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo); + _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, qmlInfo.texture, 0); + _glf.glBlitFramebuffer( + // src coordinates + 0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1, + // dst coordinates + incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1), + // blit mask and filter + GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + } + _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); +} diff --git a/tests-manual/qml/src/StressWeb.h b/tests-manual/qml/src/StressWeb.h new file mode 100644 index 0000000000..a68e34d0c1 --- /dev/null +++ b/tests-manual/qml/src/StressWeb.h @@ -0,0 +1,34 @@ +#include "TestCase.h" + +#include + +#include + +#define DIVISIONS_X 5 +#define DIVISIONS_Y 5 + +class StressWeb : public TestCase { + using Parent = TestCase; +public: + using QmlPtr = QSharedPointer; + + struct QmlInfo { + QmlPtr surface; + GLuint texture{ 0 }; + uint64_t lifetime{ 0 }; + }; + + size_t _surfaceCount{ 0 }; + uint64_t _createStopTime{ 0 }; + const QSize _qmlSize{ 640, 480 }; + std::array, DIVISIONS_X> _surfaces; + GLuint _fbo{ 0 }; + + StressWeb(const QWindow* window) : Parent(window) {} + static QString getSourceUrl(bool video); + void buildSurface(QmlInfo& qmlInfo, bool video); + void destroySurface(QmlInfo& qmlInfo); + void update() override; + void init() override; + void draw() override; +}; diff --git a/tests-manual/qml/src/TestCase.cpp b/tests-manual/qml/src/TestCase.cpp new file mode 100644 index 0000000000..534de71e51 --- /dev/null +++ b/tests-manual/qml/src/TestCase.cpp @@ -0,0 +1,25 @@ +#include "TestCase.h" + +#include +#include + +void TestCase::destroy() { +} +void TestCase::update() { +} + +void TestCase::init() { + _glf.initializeOpenGLFunctions(); + _discardLamdba = hifi::qml::OffscreenSurface::getDiscardLambda(); +} + +QUrl TestCase::getTestResource(const QString& relativePath) { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../")) + "/"; + qDebug() << "Resources Path: " << dir; + } + return QUrl::fromLocalFile(dir + relativePath); +} diff --git a/tests-manual/qml/src/TestCase.h b/tests-manual/qml/src/TestCase.h new file mode 100644 index 0000000000..191eecb408 --- /dev/null +++ b/tests-manual/qml/src/TestCase.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +class TestCase { +public: + using QmlPtr = QSharedPointer; + using Builder = std::function; + TestCase(const QWindow* window) : _window(window) {} + virtual void init(); + virtual void destroy(); + virtual void update(); + virtual void draw() = 0; + static QUrl getTestResource(const QString& relativePath); + +protected: + QOpenGLFunctions_4_1_Core _glf; + const QWindow* _window; + std::function _discardLamdba; +}; diff --git a/tests-manual/qml/src/main.cpp b/tests-manual/qml/src/main.cpp index d70bb52dde..1d98ebf8c8 100644 --- a/tests-manual/qml/src/main.cpp +++ b/tests-manual/qml/src/main.cpp @@ -43,6 +43,11 @@ #include #include #include +#include +#include + +#include "TestCase.h" +#include "MacQml.h" namespace gl { extern void initModuleGl(); @@ -67,53 +72,37 @@ QUrl getTestResource(const QString& relativePath) { return QUrl::fromLocalFile(dir + relativePath); } -#define DIVISIONS_X 5 -#define DIVISIONS_Y 5 - using QmlPtr = QSharedPointer; using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence; -struct QmlInfo { - QmlPtr surface; - GLuint texture{ 0 }; - uint64_t lifetime{ 0 }; -}; - class TestWindow : public QWindow { public: - TestWindow(); + TestWindow(const TestCase::Builder& caseBuilder); private: QOpenGLContext _glContext; OffscreenGLCanvas _sharedContext; - std::array, DIVISIONS_X> _surfaces; + TestCase* _testCase{ nullptr }; QOpenGLFunctions_4_1_Core _glf; - std::function _discardLamdba; QSize _size; - size_t _surfaceCount{ 0 }; - GLuint _fbo{ 0 }; - const QSize _qmlSize{ 640, 480 }; bool _aboutToQuit{ false }; - uint64_t _createStopTime; void initGl(); - void updateSurfaces(); - void buildSurface(QmlInfo& qmlInfo, bool allowVideo); - void destroySurface(QmlInfo& qmlInfo); void resizeWindow(const QSize& size); void draw(); void resizeEvent(QResizeEvent* ev) override; }; -TestWindow::TestWindow() { +TestWindow::TestWindow(const TestCase::Builder& builder) { Setting::init(); + + _testCase = builder(this); setSurfaceType(QSurface::OpenGLSurface); qmlRegisterType("Hifi", 1, 0, "TestItem"); show(); - _createStopTime = usecTimestampNow() + (3000u * USECS_PER_SECOND); resize(QSize(800, 600)); @@ -129,162 +118,84 @@ TestWindow::TestWindow() { }); } +Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); +Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context(); +OffscreenGLCanvas* _chromiumShareContext{ nullptr}; void TestWindow::initGl() { _glContext.setFormat(format()); + + auto globalShareContext = qt_gl_global_share_context(); + if (globalShareContext) { + _glContext.setShareContext(globalShareContext); + globalShareContext->makeCurrent(this); + gl::Context::setupDebugLogging(globalShareContext); + globalShareContext->doneCurrent(); + } + if (!_glContext.create() || !_glContext.makeCurrent(this)) { qFatal("Unable to intialize Window GL context"); } + gl::Context::setupDebugLogging(&_glContext); gl::initModuleGl(); _glf.initializeOpenGLFunctions(); - _glf.glGenFramebuffers(1, &_fbo); if (!_sharedContext.create(&_glContext) || !_sharedContext.makeCurrent()) { qFatal("Unable to intialize Shared GL context"); } hifi::qml::OffscreenSurface::setSharedContext(_sharedContext.getContext()); - _discardLamdba = hifi::qml::OffscreenSurface::getDiscardLambda(); + + if (!globalShareContext) { + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); + _chromiumShareContext->create(&_glContext); + if (!_chromiumShareContext->makeCurrent()) { + qFatal("Unable to make chromium shared context current"); + } + + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + _chromiumShareContext->doneCurrent(); + } + + // Restore the GL widget context + if (!_glContext.makeCurrent(this)) { + qFatal("Unable to make window context current"); + } + + _testCase->init(); } void TestWindow::resizeWindow(const QSize& size) { _size = size; } -static const int DEFAULT_MAX_FPS = 10; -static const QString CONTROL_URL{ "/qml/controls/WebEntityView.qml" }; -static const char* URL_PROPERTY{ "url" }; - -QString getSourceUrl(bool video) { - static const std::vector SOURCE_URLS{ - "https://www.reddit.com/wiki/random", - "https://en.wikipedia.org/wiki/Wikipedia:Random", - "https://slashdot.org/", - }; - - static const std::vector VIDEO_SOURCE_URLS{ - "https://www.youtube.com/watch?v=gDXwhHm4GhM", - "https://www.youtube.com/watch?v=Ch_hoYPPeGc", - }; - - const auto& sourceUrls = video ? VIDEO_SOURCE_URLS : SOURCE_URLS; - auto index = rand() % sourceUrls.size(); - return sourceUrls[index]; -} - -void TestWindow::buildSurface(QmlInfo& qmlInfo, bool video) { - ++_surfaceCount; - auto lifetimeSecs = (uint32_t)(5.0f + (randFloat() * 10.0f)); - auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs); - qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow(); - qmlInfo.texture = 0; - qmlInfo.surface.reset(new hifi::qml::OffscreenSurface()); - qmlInfo.surface->load(getTestResource(CONTROL_URL), [video](QQmlContext* context, QQuickItem* item) { - item->setProperty(URL_PROPERTY, getSourceUrl(video)); - }); - qmlInfo.surface->setMaxFps(DEFAULT_MAX_FPS); - qmlInfo.surface->resize(_qmlSize); - qmlInfo.surface->resume(); -} - -void TestWindow::destroySurface(QmlInfo& qmlInfo) { - auto& surface = qmlInfo.surface; - auto& currentTexture = qmlInfo.texture; - if (currentTexture) { - auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glFlush(); - _discardLamdba(currentTexture, readFence); - } - auto webView = surface->getRootItem(); - if (webView) { - // stop loading - QMetaObject::invokeMethod(webView, "stop"); - webView->setProperty(URL_PROPERTY, "about:blank"); - } - surface->pause(); - surface.reset(); -} - -void TestWindow::updateSurfaces() { - auto now = usecTimestampNow(); - // Fetch any new textures - for (size_t x = 0; x < DIVISIONS_X; ++x) { - for (size_t y = 0; y < DIVISIONS_Y; ++y) { - auto& qmlInfo = _surfaces[x][y]; - if (!qmlInfo.surface) { - if (now < _createStopTime && randFloat() > 0.99f) { - buildSurface(qmlInfo, x == 0 && y == 0); - } else { - continue; - } - } - - if (now > qmlInfo.lifetime) { - destroySurface(qmlInfo); - continue; - } - - auto& surface = qmlInfo.surface; - auto& currentTexture = qmlInfo.texture; - - TextureAndFence newTextureAndFence; - if (surface->fetchTexture(newTextureAndFence)) { - if (currentTexture != 0) { - auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glFlush(); - _discardLamdba(currentTexture, readFence); - } - currentTexture = newTextureAndFence.first; - _glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED); - } - } - } -} - void TestWindow::draw() { if (_aboutToQuit) { return; } - + // Attempting to draw before we're visible and have a valid size will // produce GL errors. if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) { return; } - + static std::once_flag once; std::call_once(once, [&] { initGl(); }); - + if (!_glContext.makeCurrent(this)) { return; } - - updateSurfaces(); - - auto size = this->geometry().size(); - auto incrementX = size.width() / DIVISIONS_X; - auto incrementY = size.height() / DIVISIONS_Y; + + _testCase->update(); + + auto size = geometry().size(); _glf.glViewport(0, 0, size.width(), size.height()); _glf.glClearColor(1, 0, 0, 1); _glf.glClear(GL_COLOR_BUFFER_BIT); - for (uint32_t x = 0; x < DIVISIONS_X; ++x) { - for (uint32_t y = 0; y < DIVISIONS_Y; ++y) { - auto& qmlInfo = _surfaces[x][y]; - if (!qmlInfo.surface || !qmlInfo.texture) { - continue; - } - _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo); - _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, qmlInfo.texture, 0); - _glf.glBlitFramebuffer( - // src coordinates - 0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1, - // dst coordinates - incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1), - // blit mask and filter - GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - } - _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + _testCase->draw(); + _glContext.swapBuffers(this); } @@ -292,19 +203,15 @@ void TestWindow::resizeEvent(QResizeEvent* ev) { resizeWindow(ev->size()); } -int main(int argc, char** argv) { - QSurfaceFormat format; - format.setDepthBufferSize(24); - format.setStencilBufferSize(8); +int main(int argc, char** argv) { + auto format = getDefaultOpenGLSurfaceFormat(); format.setVersion(4, 1); - format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); - format.setOption(QSurfaceFormat::DebugContext); QSurfaceFormat::setDefaultFormat(format); - // setFormat(format); QGuiApplication app(argc, argv); - TestWindow window; + TestCase::Builder builder = [](const QWindow* window)->TestCase*{ return new MacQml(window); }; + TestWindow window(builder); return app.exec(); } From 28b45ce1d9932fbb93041be2b7542ab4a6538564 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Mon, 1 Oct 2018 15:13:11 -0700 Subject: [PATCH 03/10] Resolve compositing glitches --- interface/src/Application.cpp | 52 +++++++++++-------- .../display-plugins/OpenGLDisplayPlugin.cpp | 2 + libraries/gl/src/gl/GLHelpers.cpp | 35 +++++++++++++ libraries/gl/src/gl/GLHelpers.h | 3 ++ libraries/gl/src/gl/OffscreenGLCanvas.cpp | 6 ++- libraries/gl/src/gl/OffscreenGLCanvas.h | 2 + .../qml/src/qml/impl/RenderEventHandler.cpp | 3 ++ 7 files changed, 80 insertions(+), 23 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 700561325f..6f65a124dd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2736,7 +2736,36 @@ void Application::initializeGL() { // We have to account for this possibility by checking here for an existing // global share context auto globalShareContext = qt_gl_global_share_context(); - _glWidget->createContext(globalShareContext); + +#if !defined(DISABLE_QML) + // Build a shared canvas / context for the Chromium processes + if (!globalShareContext) { + // Chromium rendering uses some GL functions that prevent nSight from capturing + // frames, so we only create the shared context if nsight is NOT active. + if (!nsightActive()) { + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); + auto format =QSurfaceFormat::defaultFormat(); +#ifdef Q_OS_MAC + // On mac, the primary shared OpenGL context must be a 3.2 core context, + // or chromium flips out and spews error spam (but renders fine) + format.setMajorVersion(3); + format.setMinorVersion(2); +#endif + _chromiumShareContext->setFormat(format); + _chromiumShareContext->create(); + if (!_chromiumShareContext->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make chromium shared context current"); + } + globalShareContext = _chromiumShareContext->getContext(); + qt_gl_set_global_share_context(globalShareContext); + _chromiumShareContext->doneCurrent(); + } + } +#endif + + + _glWidget->createContext(globalShareContext); if (!_glWidget->makeCurrent()) { qCWarning(interfaceapp, "Unable to make window context current"); @@ -2749,27 +2778,6 @@ void Application::initializeGL() { if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) { qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text")); } - - // Build a shared canvas / context for the Chromium processes - if (!globalShareContext) { - // Chromium rendering uses some GL functions that prevent nSight from capturing - // frames, so we only create the shared context if nsight is NOT active. - if (!nsightActive()) { - _chromiumShareContext = new OffscreenGLCanvas(); - _chromiumShareContext->setObjectName("ChromiumShareContext"); - _chromiumShareContext->create(_glWidget->qglContext()); - if (!_chromiumShareContext->makeCurrent()) { - qCWarning(interfaceapp, "Unable to make chromium shared context current"); - } - globalShareContext = _chromiumShareContext->getContext(); - qt_gl_set_global_share_context(globalShareContext); - _chromiumShareContext->doneCurrent(); - // Restore the GL widget context - if (!_glWidget->makeCurrent()) { - qCWarning(interfaceapp, "Unable to make window context current"); - } - } - } #endif if (!globalShareContext) { diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 6448c6d3a1..6525672aee 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -179,7 +179,9 @@ public: _context->makeCurrent(); { PROFILE_RANGE(render, "PluginPresent") + gl::globalLock(); currentPlugin->present(); + gl::globalRelease(false); CHECK_GL_ERROR(); } _context->doneCurrent(); diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index c22bd0dde5..ba27a13f58 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -34,6 +34,41 @@ bool gl::disableGl45() { #endif } +#ifdef Q_OS_MAC +#define SERIALIZE_GL_RENDERING +#endif + +#ifdef SERIALIZE_GL_RENDERING + +// This terrible terrible hack brought to you by the complete lack of reasonable +// OpenGL debugging tools on OSX. Without this serialization code, the UI textures +// frequently become 'glitchy' and get composited onto the main scene in what looks +// like a partially rendered state. +// This looks very much like either state bleeding across the contexts, or bad +// synchronization for the shared OpenGL textures. However, previous attempts to resolve +// it, even with gratuitous use of glFinish hasn't improved the situation + +static std::mutex _globalOpenGLLock; + +void gl::globalLock() { + _globalOpenGLLock.lock(); +} + +void gl::globalRelease(bool finish) { + if (finish) { + glFinish(); + } + _globalOpenGLLock.unlock(); +} + +#else + +void gl::globalLock() {} +void gl::globalRelease(bool finish) {} + +#endif + + void gl::getTargetVersion(int& major, int& minor) { #if defined(USE_GLES) major = 3; diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index 6252eba2f0..1865442ef6 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -35,6 +35,9 @@ int glVersionToInteger(QString glVersion); bool isRenderThread(); namespace gl { + void globalLock(); + void globalRelease(bool finish = true); + void withSavedContext(const std::function& f); bool checkGLError(const char* name); diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 1256e316ff..37289745a4 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -33,6 +33,7 @@ OffscreenGLCanvas::OffscreenGLCanvas() : _context(new QOpenGLContext), _offscreenSurface(new QOffscreenSurface) { + setFormat(getDefaultOpenGLSurfaceFormat()); } OffscreenGLCanvas::~OffscreenGLCanvas() { @@ -49,12 +50,15 @@ OffscreenGLCanvas::~OffscreenGLCanvas() { } +void OffscreenGLCanvas::setFormat(const QSurfaceFormat& format) { + _context->setFormat(format); +} + bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { if (nullptr != sharedContext) { sharedContext->doneCurrent(); _context->setShareContext(sharedContext); } - _context->setFormat(getDefaultOpenGLSurfaceFormat()); if (!_context->create()) { qFatal("Failed to create OffscreenGLCanvas context"); } diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h index 374720a910..3946f760cf 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -18,11 +18,13 @@ class QOpenGLContext; class QOffscreenSurface; class QOpenGLDebugMessage; +class QSurfaceFormat; class OffscreenGLCanvas : public QObject { public: OffscreenGLCanvas(); ~OffscreenGLCanvas(); + void setFormat(const QSurfaceFormat& format); bool create(QOpenGLContext* sharedContext = nullptr); bool makeCurrent(); void doneCurrent(); diff --git a/libraries/qml/src/qml/impl/RenderEventHandler.cpp b/libraries/qml/src/qml/impl/RenderEventHandler.cpp index 8a56929e6b..46fc3490a7 100644 --- a/libraries/qml/src/qml/impl/RenderEventHandler.cpp +++ b/libraries/qml/src/qml/impl/RenderEventHandler.cpp @@ -12,6 +12,7 @@ #include #include +#include #include @@ -114,6 +115,7 @@ void RenderEventHandler::onRender() { PROFILE_RANGE(render_qml_gl, __FUNCTION__); + gl::globalLock(); if (!_shared->preRender()) { return; } @@ -144,6 +146,7 @@ void RenderEventHandler::onRender() { _shared->updateTextureAndFence({ texture, fence }); _shared->_quickWindow->resetOpenGLState(); } + gl::globalRelease(); } void RenderEventHandler::onQuit() { From 405ec228b8cae4aac0d340f6f132ddf825c6fb23 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 1 Oct 2018 16:07:02 -0700 Subject: [PATCH 04/10] Fix mac warning --- tests-manual/qml/src/MacQml.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-manual/qml/src/MacQml.cpp b/tests-manual/qml/src/MacQml.cpp index 7f7854ce87..13d8642822 100644 --- a/tests-manual/qml/src/MacQml.cpp +++ b/tests-manual/qml/src/MacQml.cpp @@ -29,7 +29,7 @@ using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence; void MacQml::update() { auto rootItem =_surface->getRootItem(); float now = sinf(secTimestampNow()); - rootItem->setProperty("level", abs(now)); + rootItem->setProperty("level", fabs(now)); rootItem->setProperty("muted", now > 0.0f); rootItem->setProperty("statsValue", rand()); From 9a30073f27b0786186a8214e94cd20c27db8ea2b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 2 Oct 2018 09:20:41 -0700 Subject: [PATCH 05/10] Only enable debug GL context on demand --- libraries/gl/src/gl/Context.cpp | 7 +++++-- libraries/gl/src/gl/GLHelpers.cpp | 6 +++++- tests-manual/qml/src/MacQml.cpp | 18 ------------------ 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index cd2b19beec..a3e01018b5 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -31,6 +31,10 @@ using namespace gl; bool Context::enableDebugLogger() { +#if defined(Q_OS_MAC) + // OSX does not support GL_KHR_debug or GL_ARB_debug_output + return false; +#else #if defined(DEBUG) || defined(USE_GLES) static bool enableDebugLogger = true; #else @@ -45,6 +49,7 @@ bool Context::enableDebugLogger() { } }); return enableDebugLogger; +#endif } @@ -233,7 +238,6 @@ GLAPI PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context(); - void Context::create(QOpenGLContext* shareContext) { assert(0 != _hwnd); assert(0 == _hdc); @@ -351,7 +355,6 @@ void Context::create(QOpenGLContext* shareContext) { #endif - OffscreenContext::~OffscreenContext() { _window->deleteLater(); } diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index ba27a13f58..15a41c3dc1 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -13,6 +13,8 @@ #include #include +#include "Context.h" + size_t evalGLFormatSwapchainPixelSize(const QSurfaceFormat& format) { size_t pixelSize = format.redBufferSize() + format.greenBufferSize() + format.blueBufferSize() + format.alphaBufferSize(); // We don't apply the length of the swap chain into this pixelSize since it is not vsible for the Process (on windows). @@ -97,7 +99,9 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { #else format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); #endif - format.setOption(QSurfaceFormat::DebugContext); + if (gl::Context::enableDebugLogger()) { + format.setOption(QSurfaceFormat::DebugContext); + } // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); diff --git a/tests-manual/qml/src/MacQml.cpp b/tests-manual/qml/src/MacQml.cpp index 13d8642822..9c5f91041e 100644 --- a/tests-manual/qml/src/MacQml.cpp +++ b/tests-manual/qml/src/MacQml.cpp @@ -7,24 +7,6 @@ #include using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence; -// -//void MacQml::destroySurface(QmlInfo& qmlInfo) { -// auto& surface = qmlInfo.surface; -// auto& currentTexture = qmlInfo.texture; -// if (currentTexture) { -// auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); -// glFlush(); -// _discardLamdba(currentTexture, readFence); -// } -// auto webView = surface->getRootItem(); -// if (webView) { -// // stop loading -// QMetaObject::invokeMethod(webView, "stop"); -// webView->setProperty(URL_PROPERTY, "about:blank"); -// } -// surface->pause(); -// surface.reset(); -//} void MacQml::update() { auto rootItem =_surface->getRootItem(); From 7ca8b540399586683b075b4ab787bbf7a046e16f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 25 Oct 2018 14:34:23 -0700 Subject: [PATCH 06/10] Fix crash on startup on windows / android --- libraries/gl/src/gl/Context.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index a3e01018b5..d4ecbaa5ba 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -41,13 +41,6 @@ bool Context::enableDebugLogger() { static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL"); static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); #endif - static std::once_flag once; - std::call_once(once, [&] { - // If the previous run crashed, force GL debug logging on - if (qApp->property(hifi::properties::CRASHED).toBool()) { - enableDebugLogger = true; - } - }); return enableDebugLogger; #endif } From 36edb939c3dbe7f72563fe590f41046d7863e345 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 25 Oct 2018 16:28:30 -0700 Subject: [PATCH 07/10] Try to fix android crash --- interface/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 12a02ffd32..5af0a9371d 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -41,8 +41,8 @@ extern "C" { #endif int main(int argc, const char* argv[]) { - auto format = getDefaultOpenGLSurfaceFormat(); #ifdef Q_OS_MAC + auto format = getDefaultOpenGLSurfaceFormat(); // Deal with some weirdness in the chromium context sharing on Mac. // The primary share context needs to be 3.2, so that the Chromium will // succeed in it's creation of it's command stub contexts. @@ -51,8 +51,8 @@ int main(int argc, const char* argv[]) { // idea why. qputenv("QT_ENABLE_GLYPH_CACHE_WORKAROUND", "true"); // https://i.kym-cdn.com/entries/icons/original/000/008/342/ihave.jpg -#endif QSurfaceFormat::setDefaultFormat(format); +#endif setupHifiApplication(BuildInfo::INTERFACE_NAME); QStringList arguments; From a3a952265a7e8d6cf42c43676a7be33717162740 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 26 Oct 2018 11:23:04 -0700 Subject: [PATCH 08/10] Fix android crash --- .../display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp | 1 + libraries/gl/src/gl/OffscreenGLCanvas.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 6525672aee..190d4d4104 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -88,6 +88,7 @@ public: // Move the OpenGL context to the present thread // Extra code because of the widget 'wrapper' context _context = context; + _context->doneCurrent(); _context->moveToThread(this); } diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 37289745a4..f05acb50e9 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -73,6 +73,7 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { if (!_context->makeCurrent(_offscreenSurface)) { qFatal("Unable to make offscreen surface current"); } + _context->doneCurrent(); #else if (!_offscreenSurface->isValid()) { qFatal("Offscreen surface is invalid"); From c4aabfda44abdf49db49097c464d4408fef19f8c Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 29 Oct 2018 08:47:09 -0700 Subject: [PATCH 09/10] adding prototype functions --- interface/src/Application.cpp | 7 +++++++ interface/src/Application.h | 4 ++-- interface/src/ui/LoginDialog.cpp | 19 +++++++++++++------ interface/src/ui/LoginDialog.h | 3 +++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9b52357099..414dc92e0b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5181,6 +5181,12 @@ void Application::init() { _entitySimulation->setWorkloadSpace(getEntities()->getWorkloadSpace()); } +void Application::pauseUntilLoginDetermined() { +} + +void Application::resumeAfterLoginDialogActionTaken() { +} + void Application::loadAvatarScripts(const QVector& urls) { auto scriptEngines = DependencyManager::get(); auto runningScripts = scriptEngines->getRunningScripts(); @@ -8398,6 +8404,7 @@ void Application::onDismissedLoginDialog() { qDebug() << "dismissed login dialog"; _loginDialogPoppedUp = false; loginDialogPoppedUp.set(false); + resumeAfterLoginDialogActionTaken(); } void Application::startHMDStandBySession() { diff --git a/interface/src/Application.h b/interface/src/Application.h index d35848754a..e78bf3e84a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -320,8 +320,6 @@ public: void setOtherAvatarsReplicaCount(int count) { DependencyManager::get()->setReplicaCount(count); } bool getLoginDialogPoppedUp() const { return _loginDialogPoppedUp; } - void pauseUntilLoginDetermined(); - void resumeAfterLoginDialogActionTaken(); #if defined(Q_OS_ANDROID) void beforeEnterBackground(); @@ -514,6 +512,8 @@ private slots: private: void init(); + void pauseUntilLoginDetermined(); + void resumeAfterLoginDialogActionTaken(); bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event); bool handleFileOpenEvent(QFileOpenEvent* event); void cleanupBeforeQuit(); diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 1ec098ddb2..085ddf5b5a 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -26,6 +26,8 @@ #include "Menu.h" #include "Application.h" +#include "overlays/Overlays.h" +#include "overlays/Web3DOverlay.h" #include "scripting/HMDScriptingInterface.h" #include "Constants.h" @@ -63,12 +65,13 @@ void LoginDialog::showWithSelection() { } } } else { - //if (qApp->getLoginDialogPoppedUp()) { - // // pop up those overlay things. - // return; - //} else { - // tablet->initialScreen(url); - //} + if (qApp->getLoginDialogPoppedUp()) { + // pop up those overlay things. + createLoginScreenDialog(); + return; + } else { + tablet->initialScreen(url); + } } if (!hmd->getShouldShowTablet()) { @@ -252,6 +255,10 @@ bool LoginDialog::getLoginDialogPoppedUp() const { return qApp->getLoginDialogPoppedUp(); } +void LoginDialog::createLoginScreenDialog() { + +} + QString errorStringFromAPIObject(const QJsonValue& apiObject) { if (apiObject.isArray()) { return apiObject.toArray()[0].toString(); diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h index df7bf69172..c2b0af54c3 100644 --- a/interface/src/ui/LoginDialog.h +++ b/interface/src/ui/LoginDialog.h @@ -76,8 +76,11 @@ protected slots: Q_INVOKABLE bool getLoginDialogPoppedUp() const; private: + static void createLoginScreenDialog(); + bool getIsLogIn() const { return _isLogIn; } void setIsLogIn(const bool isLogIn) { _isLogIn = isLogIn; } + bool _isLogIn { false }; }; From ed847203ea8a5e5270f388c9b38a1175cfc144c7 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 29 Oct 2018 11:33:28 -0700 Subject: [PATCH 10/10] wip for getting hmd overlay --- interface/src/Application.cpp | 218 ++++++++++-------- interface/src/Application.h | 6 +- interface/src/ConnectionMonitor.cpp | 1 - libraries/networking/src/DomainHandler.cpp | 31 +-- libraries/networking/src/DomainHandler.h | 9 - libraries/script-engine/src/ScriptEngines.cpp | 5 + libraries/script-engine/src/ScriptEngines.h | 1 + 7 files changed, 133 insertions(+), 138 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 620d73d2cd..f94d21ae9f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1025,6 +1025,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); setProperty(hifi::properties::CRASHED, _previousSessionCrashed); + { const QStringList args = arguments(); @@ -1223,7 +1224,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &Application::domainConnectionRefused); nodeList->getDomainHandler().setErrorDomainURL(QUrl(REDIRECT_HIFI_ADDRESS)); - nodeList->getDomainHandler().setLoginScreenDomainURL(QUrl(LOGIN_SCREEN_HIFI_ADDRESS)); // We could clear ATP assets only when changing domains, but it's possible that the domain you are connected // to has gone down and switched to a new content set, so when you reconnect the cached ATP assets will no longer be valid. @@ -1745,27 +1745,30 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo userInputMapper->registerDevice(_touchscreenVirtualPadDevice->getInputDevice()); } - { - auto scriptEngines = DependencyManager::get().data(); - // this will force the model the look at the correct directory (weird order of operations issue) - scriptEngines->reloadLocalFiles(); + QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH); + _defaultScriptsLocation = getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str()); - // do this as late as possible so that all required subsystems are initialized - // If we've overridden the default scripts location, just load default scripts - // otherwise, load 'em all + //{ + // auto scriptEngines = DependencyManager::get().data(); + // // this will force the model the look at the correct directory (weird order of operations issue) + // scriptEngines->reloadLocalFiles(); - // we just want to see if --scripts was set, we've already parsed it and done - // the change in PathUtils. Rather than pass that in the constructor, lets just - // look (this could be debated) - QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH); - QDir defaultScriptsLocation(getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str())); - if (!defaultScriptsLocation.exists()) { - scriptEngines->loadDefaultScripts(); - scriptEngines->defaultScriptsLocationOverridden(true); - } else { - scriptEngines->loadScripts(); - } - } + // // do this as late as possible so that all required subsystems are initialized + // // If we've overridden the default scripts location, just load default scripts + // // otherwise, load 'em all + + // // we just want to see if --scripts was set, we've already parsed it and done + // // the change in PathUtils. Rather than pass that in the constructor, lets just + // // look (this could be debated) + // QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH); + // QDir defaultScriptsLocation(getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str())); + // if (!defaultScriptsLocation.exists()) { + // scriptEngines->loadDefaultScripts(); + // scriptEngines->defaultScriptsLocationOverridden(true); + // } else { + // scriptEngines->loadScripts(); + // } + //} // Make sure we don't time out during slow operations at startup updateHeartbeat(); @@ -2247,28 +2250,28 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _snapshotSound = DependencyManager::get()->getSound(PathUtils::resourcesUrl("sounds/snapshot/snap.wav")); - QVariant testProperty = property(hifi::properties::TEST); - qDebug() << testProperty; - if (testProperty.isValid()) { - const auto testScript = property(hifi::properties::TEST).toUrl(); + //QVariant testProperty = property(hifi::properties::TEST); + //qDebug() << testProperty; + //if (testProperty.isValid()) { + // const auto testScript = property(hifi::properties::TEST).toUrl(); - // Set last parameter to exit interface when the test script finishes, if so requested - DependencyManager::get()->loadScript(testScript, false, false, false, false, quitWhenFinished); + // // Set last parameter to exit interface when the test script finishes, if so requested + // DependencyManager::get()->loadScript(testScript, false, false, false, false, quitWhenFinished); - // This is done so we don't get a "connection time-out" message when we haven't passed in a URL. - if (arguments().contains("--url")) { - auto reply = SandboxUtils::getStatus(); - connect(reply, &QNetworkReply::finished, this, [this, reply] { - handleSandboxStatus(reply); - }); - } - } else { - PROFILE_RANGE(render, "GetSandboxStatus"); - auto reply = SandboxUtils::getStatus(); - connect(reply, &QNetworkReply::finished, this, [this, reply] { - handleSandboxStatus(reply); - }); - } + // // This is done so we don't get a "connection time-out" message when we haven't passed in a URL. + // if (arguments().contains("--url")) { + // auto reply = SandboxUtils::getStatus(); + // connect(reply, &QNetworkReply::finished, this, [this, reply] { + // handleSandboxStatus(reply); + // }); + // } + //} else { + // PROFILE_RANGE(render, "GetSandboxStatus"); + // auto reply = SandboxUtils::getStatus(); + // connect(reply, &QNetworkReply::finished, this, [this, reply] { + // handleSandboxStatus(reply); + // }); + //} // Monitor model assets (e.g., from Clara.io) added to the world that may need resizing. static const int ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS = 1000; @@ -2288,7 +2291,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(this, &QCoreApplication::aboutToQuit, this, &Application::addAssetToWorldMessageClose); connect(&domainHandler, &DomainHandler::domainURLChanged, this, &Application::addAssetToWorldMessageClose); connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &Application::addAssetToWorldMessageClose); - connect(&domainHandler, &DomainHandler::redirectToLoginScreenDomainURL, this, &Application::addAssetToWorldMessageClose); updateSystemTabletMode(); @@ -2344,6 +2346,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground); AndroidHelper::instance().notifyLoadComplete(); #endif + pauseUntilLoginDetermined(); } void Application::updateVerboseLogging() { @@ -2914,18 +2917,14 @@ void Application::showLoginScreen() { auto accountManager = DependencyManager::get(); auto dialogsManager = DependencyManager::get(); if (!accountManager->isLoggedIn()) { -// dialogsManager->showLoginScreenDialog(); _loginDialogPoppedUp = true; dialogsManager->showLoginDialog(); QJsonObject loginData = {}; loginData["action"] = "login dialog shown"; UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); - if (qApp->isHMDMode()) { - // create web overlay. - auto nodeList = DependencyManager::get(); - auto loginScreenDomainURL = nodeList->getDomainHandler().getLoginScreenDomainURL(); - goToLoginScreenDomainURL(loginScreenDomainURL); - } + _window->setWindowTitle("High Fidelity Interface"); + } else { + resumeAfterLoginDialogActionTaken(); } _loginDialogPoppedUp = !accountManager->isLoggedIn(); loginDialogPoppedUp.set(_loginDialogPoppedUp); @@ -3066,14 +3065,12 @@ void Application::initializeUi() { offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); flushMenuUpdates(); - // Now that the menu is instantiated, ensure the display plugin menu is properly updated { auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); // first sort the plugins into groupings: standard, advanced, developer std::stable_sort(displayPlugins.begin(), displayPlugins.end(), - [](const DisplayPluginPointer& a, const DisplayPluginPointer& b)->bool { return a->getGrouping() < b->getGrouping(); }); - + [](const DisplayPluginPointer& a, const DisplayPluginPointer& b) -> bool { return a->getGrouping() < b->getGrouping(); }); int dpIndex = 1; // concatenate the groupings into a single list in the order: standard, advanced, developer for(const auto& displayPlugin : displayPlugins) { @@ -3086,6 +3083,7 @@ void Application::initializeUi() { parent->addSeparator(); } + // The display plugins are created before the menu now, so we need to do this here to hide the menu bar // now that it exists if (_window && _window->isFullScreen()) { @@ -3661,24 +3659,6 @@ void Application::loadErrorDomain(QUrl domainURL) { _fullSceneReceivedCounter++; } -void Application::loadLoginScreenDomain(QUrl domainURL) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadLoginScreenDomain", Q_ARG(QUrl, domainURL)); - return; - } - - if (domainURL.isEmpty()) { - return; - } - - auto namedPaths = prepareServerlessDomainContents(domainURL); - auto nodeList = DependencyManager::get(); - - nodeList->getDomainHandler().loadedLoginScreenDomain(namedPaths); - - _fullSceneReceivedCounter++; -} - bool Application::importImage(const QString& urlString) { qCDebug(interfaceapp) << "An image file has been dropped in"; QString filepath(urlString); @@ -3913,7 +3893,7 @@ void Application::keyPressEvent(QKeyEvent* event) { return; } - if (hasFocus()) { + if (hasFocus() && !_loginDialogPoppedUp) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->keyPressEvent(event); } @@ -5205,9 +5185,83 @@ void Application::init() { } void Application::pauseUntilLoginDetermined() { + if (QThread::currentThread() != qApp->thread()) { + QMetaObject::invokeMethod(this, "pauseUntilLoginDetermined"); + return; + } + + auto myAvatar = qApp->getMyAvatar(); + myAvatar->setEnableMeshVisible(false); + + const auto& nodeList = DependencyManager::get(); + // disconnect domain handler. + nodeList->getDomainHandler().disconnect(); + Menu::getInstance()->setVisible(false); + + { + auto scriptEngines = DependencyManager::get().data(); + scriptEngines->reloadLocalFiles(); + scriptEngines->loadControllerScripts(); + } } void Application::resumeAfterLoginDialogActionTaken() { + if (QThread::currentThread() != qApp->thread()) { + QMetaObject::invokeMethod(this, "resumeAfterLoginDialogActionTaken"); + return; + } + + auto myAvatar = qApp->getMyAvatar(); + myAvatar->setEnableMeshVisible(true); + + { + auto scriptEngines = DependencyManager::get().data(); + // this will force the model the look at the correct directory (weird order of operations issue) + scriptEngines->reloadLocalFiles(); + + // do this as late as possible so that all required subsystems are initialized + // If we've overridden the default scripts location, just load default scripts + // otherwise, load 'em all + + // we just want to see if --scripts was set, we've already parsed it and done + // the change in PathUtils. Rather than pass that in the constructor, lets just + // look (this could be debated) + if (!_defaultScriptsLocation.exists()) { + scriptEngines->loadDefaultScripts(); + scriptEngines->defaultScriptsLocationOverridden(true); + } else { + scriptEngines->loadScripts(); + } + } + + QVariant testProperty = property(hifi::properties::TEST); + qDebug() << testProperty; + if (testProperty.isValid()) { + const auto testScript = property(hifi::properties::TEST).toUrl(); + + // Set last parameter to exit interface when the test script finishes, if so requested + DependencyManager::get()->loadScript(testScript, false, false, false, false, quitWhenFinished); + + // This is done so we don't get a "connection time-out" message when we haven't passed in a URL. + if (arguments().contains("--url")) { + auto reply = SandboxUtils::getStatus(); + connect(reply, &QNetworkReply::finished, this, [this, reply] { + handleSandboxStatus(reply); + }); + } + } else { + PROFILE_RANGE(render, "GetSandboxStatus"); + auto reply = SandboxUtils::getStatus(); + connect(reply, &QNetworkReply::finished, this, [this, reply] { + handleSandboxStatus(reply); + }); + } + + const auto& nodeList = DependencyManager::get(); + // disconnect domain handler. + nodeList->getDomainHandler().resetting(); + + Menu::getInstance()->setVisible(true); } void Application::loadAvatarScripts(const QVector& urls) { @@ -6545,7 +6599,6 @@ void Application::updateWindowTitle() const { auto nodeList = DependencyManager::get(); auto accountManager = DependencyManager::get(); auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); - auto isInLoginScreenState = nodeList->getDomainHandler().isInLoginScreenState(); QString buildVersion = " - " + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) @@ -6555,8 +6608,6 @@ void Application::updateWindowTitle() const { QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" : nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)"; - // check for login state - login state needs empty connection status - connectionStatus = isInLoginScreenState ? "" : connectionStatus; QString username = accountManager->getAccountInfo().getUsername(); setCrashAnnotation("username", username.toStdString()); @@ -6565,8 +6616,6 @@ void Application::updateWindowTitle() const { if (isServerlessMode()) { if (isInErrorState) { currentPlaceName = "serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString(); - } else if (isInLoginScreenState) { - currentPlaceName = "High Fidelity Interface"; } else { currentPlaceName = "serverless: " + DependencyManager::get()->getDomainURL().toString(); } @@ -6645,23 +6694,6 @@ void Application::goToErrorDomainURL(QUrl errorDomainURL) { updateWindowTitle(); } -void Application::goToLoginScreenDomainURL(QUrl loginScreenDomainURL) { - // disable physics until we have enough information about our new location to not cause craziness. - resetPhysicsReadyInformation(); - setIsServerlessMode(loginScreenDomainURL.scheme() != URL_SCHEME_HIFI); - - // show avatar as a mesh and show hand controllers. - qApp->getMyAvatar()->setEnableMeshVisible(false); - DependencyManager::get()->requestShowHandControllers(); - // set into login screen state. - emit loginScreenStateChanged(true); - - if (isServerlessMode()) { - loadLoginScreenDomain(loginScreenDomainURL); - } - updateWindowTitle(); -} - void Application::resettingDomain() { _notifiedPacketVersionMismatchThisDomain = false; @@ -8459,8 +8491,6 @@ void Application::setShowBulletConstraintLimits(bool value) { } void Application::onDismissedLoginDialog() { - // TODO something with login dialog. - qDebug() << "dismissed login dialog"; _loginDialogPoppedUp = false; loginDialogPoppedUp.set(false); resumeAfterLoginDialogActionTaken(); diff --git a/interface/src/Application.h b/interface/src/Application.h index e0955c3456..3f3b6370a8 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -339,8 +339,6 @@ signals: void interstitialModeChanged(bool isInInterstitialMode); - void loginScreenStateChanged(bool isInLoginScreenState); - public slots: QVector pasteEntities(float x, float y, float z); bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); @@ -349,7 +347,6 @@ public slots: void updateThreadPoolCount() const; void updateSystemTabletMode(); void goToErrorDomainURL(QUrl errorDomainURL); - void goToLoginScreenDomainURL(QUrl loginScreenDomainURL); Q_INVOKABLE void loadDialog(); Q_INVOKABLE void loadScriptURLDialog() const; @@ -444,7 +441,6 @@ public slots: void loadServerlessDomain(QUrl domainURL); void loadErrorDomain(QUrl domainURL); - void loadLoginScreenDomain(QUrl domainURL); void setIsInterstitialMode(bool interstitialMode); void updateVerboseLogging(); @@ -664,6 +660,8 @@ private: QPointer _logDialog; QPointer _entityScriptServerLogDialog; + QDir _defaultScriptsLocation{""}; + FileLogger* _logger; TouchEvent _lastTouchEvent; diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp index ff89d9a0d6..e86061b090 100644 --- a/interface/src/ConnectionMonitor.cpp +++ b/interface/src/ConnectionMonitor.cpp @@ -33,7 +33,6 @@ void ConnectionMonitor::init() { connect(&domainHandler, &DomainHandler::connectedToDomain, this, &ConnectionMonitor::stopTimer); connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ConnectionMonitor::stopTimer); connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &ConnectionMonitor::stopTimer); - connect(&domainHandler, &DomainHandler::redirectToLoginScreenDomainURL, this, &ConnectionMonitor::stopTimer); connect(this, &ConnectionMonitor::setRedirectErrorState, &domainHandler, &DomainHandler::setRedirectErrorState); _timer.setSingleShot(true); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index a2293c485d..182a79ec4b 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -62,16 +62,6 @@ DomainHandler::DomainHandler(QObject* parent) : // stop the refresh timer if redirected to the error domain connect(this, &DomainHandler::redirectToErrorDomainURL, &_apiRefreshTimer, &QTimer::stop); - - // stop the refresh timer if redirected to the login screen domain - connect(this, &DomainHandler::redirectToLoginScreenDomainURL, &_apiRefreshTimer, &QTimer::stop); - - - // stop the refresh timer if redirected to the login screen domain - connect(this, &DomainHandler::redirectToLoginScreenDomainURL, [this]() { - _isInLoginScreenState = true; - qCDebug(networking) << "Redirecting user to " << _loginScreenDomainURL; - }); } void DomainHandler::disconnect() { @@ -123,7 +113,7 @@ void DomainHandler::softReset() { QMetaObject::invokeMethod(&_settingsTimer, "stop"); // restart the API refresh timer in case we fail to connect and need to refresh information - if (!_isInErrorState || !_isInLoginScreenState) { + if (!_isInErrorState) { QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); } } @@ -135,9 +125,6 @@ void DomainHandler::hardReset() { _isInErrorState = false; emit redirectErrorStateChanged(_isInErrorState); - _isInLoginScreenState = false; - emit loginScreenStateChanged(_isInLoginScreenState); - qCDebug(networking) << "Hard reset in NodeList DomainHandler."; _pendingDomainID = QUuid(); _iceServerSockAddr = HifiSockAddr(); @@ -176,11 +163,6 @@ void DomainHandler::setErrorDomainURL(const QUrl& url) { return; } -void DomainHandler::setLoginScreenDomainURL(const QUrl& url) { - _loginScreenDomainURL = url; - return; -} - void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname) { if (_sockAddr != sockAddr) { // we should reset on a sockAddr change @@ -383,17 +365,6 @@ void DomainHandler::loadedErrorDomain(std::map namedPaths) { DependencyManager::get()->goToViewpointForPath(viewpoint, QString()); } -void DomainHandler::loadedLoginScreenDomain(std::map namedPaths) { - auto lookup = namedPaths.find("/"); - QString viewpoint; - if (lookup != namedPaths.end()) { - viewpoint = lookup->second; - } else { - viewpoint = DOMAIN_SPAWNING_POINT; - } - DependencyManager::get()->goToViewpointForPath(viewpoint, QString()); -} - void DomainHandler::setRedirectErrorState(QUrl errorUrl, QString reasonMessage, int reasonCode, const QString& extraInfo) { _lastDomainConnectionError = reasonCode; if (getInterstitialModeEnabled() && isHardRefusal(reasonCode)) { diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 9bfbe26db0..e99875a50c 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -58,9 +58,6 @@ public: int getLastDomainConnectionError() { return _lastDomainConnectionError; } - QUrl getLoginScreenDomainURL(){ return _loginScreenDomainURL; } - void setLoginScreenDomainURL(const QUrl& url); - const QHostAddress& getIP() const { return _sockAddr.getAddress(); } void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); } @@ -96,8 +93,6 @@ public: void loadedErrorDomain(std::map namedPaths); - void loadedLoginScreenDomain(std::map namedPaths); - QString getViewPointFromNamedPath(QString namedPath); bool hasSettings() const { return !_settingsObject.isEmpty(); } @@ -211,9 +206,6 @@ signals: void redirectToErrorDomainURL(QUrl errorDomainURL); void redirectErrorStateChanged(bool isInErrorState); - void redirectToLoginScreenDomainURL(); - void loginScreenStateChanged(bool isInLoginScreenState); - void limitOfSilentDomainCheckInsReached(); private: @@ -227,7 +219,6 @@ private: Node::LocalID _localID; QUrl _domainURL; QUrl _errorDomainURL; - QUrl _loginScreenDomainURL; HifiSockAddr _sockAddr; QUuid _assignmentUUID; QUuid _connectionToken; diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 6393ff33ea..06ed0b8cfa 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -28,6 +28,7 @@ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStanda static const bool HIFI_SCRIPT_DEBUGGABLES { true }; static const QString SETTINGS_KEY { "RunningScripts" }; static const QUrl DEFAULT_SCRIPTS_LOCATION { "file:///~//defaultScripts.js" }; +static const QUrl CONTROLLER_SCRIPTS_LOCATION { "file:///~//system//controllers//controllerScripts.js" }; // Using a QVariantList so this is human-readable in the settings file static Setting::Handle runningScriptsHandle(SETTINGS_KEY, { QVariant(DEFAULT_SCRIPTS_LOCATION) }); @@ -287,6 +288,10 @@ void ScriptEngines::loadDefaultScripts() { loadScript(DEFAULT_SCRIPTS_LOCATION); } +void ScriptEngines::loadControllerScripts() { + loadScript(CONTROLLER_SCRIPTS_LOCATION); +} + void ScriptEngines::loadOneScript(const QString& scriptFilename) { loadScript(scriptFilename); } diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 4d5964e462..1c904498c4 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -65,6 +65,7 @@ public: void setDebugScriptUrl(const QString& url) { _debugScriptUrl = url; }; void loadDefaultScripts(); + void loadControllerScripts(); void reloadLocalFiles(); QStringList getRunningScripts();