3
0
Fork 0
mirror of https://github.com/JulianGro/overte.git synced 2025-04-18 09:58:55 +02:00

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

This commit is contained in:
howard-stearns 2016-08-16 11:14:36 -07:00
commit 2b75240771
189 changed files with 6047 additions and 3392 deletions
cmake/externals/openvr
interface
libraries

View file

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

Binary file not shown.

Before

(image error) Size: 110 KiB

After

(image error) Size: 106 KiB

Binary file not shown.

After

(image error) Size: 5.7 KiB

View file

@ -10,296 +10,70 @@
import Hifi 1.0
import QtQuick 2.4
import "controls"
import "styles"
import "controls-uit"
import "styles-uit"
import "windows"
ScrollingWindow {
import "LoginDialog"
ModalWindow {
id: root
HifiConstants { id: hifi }
objectName: "LoginDialog"
height: loginDialog.implicitHeight
width: loginDialog.implicitWidth
// FIXME make movable
anchors.centerIn: parent
destroyOnHidden: false
hideBackground: true
shown: false
implicitWidth: 520
implicitHeight: 320
destroyOnCloseButton: true
destroyOnHidden: true
visible: true
property string iconText: ""
property int iconSize: 50
property string title: ""
property int titleWidth: 0
LoginDialog {
id: loginDialog
implicitWidth: backgroundRectangle.width
implicitHeight: backgroundRectangle.height
readonly property int inputWidth: 500
readonly property int inputHeight: 60
readonly property int borderWidth: 30
readonly property int closeMargin: 16
readonly property real tan30: 0.577 // tan(30°)
readonly property int inputSpacing: 16
Rectangle {
id: backgroundRectangle
width: loginDialog.inputWidth + loginDialog.borderWidth * 2
height: loginDialog.inputHeight * 6 + loginDialog.closeMargin * 2
radius: loginDialog.closeMargin * 2
color: "#2c86b1"
opacity: 0.85
}
Column {
id: mainContent
width: loginDialog.inputWidth
spacing: loginDialog.inputSpacing
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
Item {
// Offset content down a little
width: loginDialog.inputWidth
height: loginDialog.closeMargin
}
Rectangle {
width: loginDialog.inputWidth
height: loginDialog.inputHeight
radius: height / 2
color: "#ebebeb"
Image {
source: "../images/login-username.svg"
width: loginDialog.inputHeight * 0.65
height: width
sourceSize: Qt.size(width, height);
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: loginDialog.inputHeight / 4
}
}
TextInput {
id: username
anchors {
fill: parent
leftMargin: loginDialog.inputHeight
rightMargin: loginDialog.inputHeight / 2
}
helperText: "username or email"
color: hifi.colors.text
KeyNavigation.tab: password
KeyNavigation.backtab: password
}
}
Rectangle {
width: loginDialog.inputWidth
height: loginDialog.inputHeight
radius: height / 2
color: "#ebebeb"
Image {
source: "../images/login-password.svg"
width: loginDialog.inputHeight * 0.65
height: width
sourceSize: Qt.size(width, height);
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: loginDialog.inputHeight / 4
}
}
TextInput {
id: password
anchors {
fill: parent
leftMargin: loginDialog.inputHeight
rightMargin: loginDialog.inputHeight / 2
}
helperText: "password"
echoMode: TextInput.Password
color: hifi.colors.text
KeyNavigation.tab: username
KeyNavigation.backtab: username
onFocusChanged: {
if (password.focus) {
password.selectAll()
}
}
}
}
Item {
width: loginDialog.inputWidth
height: loginDialog.inputHeight / 2
Text {
id: messageText
visible: loginDialog.statusText != "" && loginDialog.statusText != "Logging in..."
width: loginDialog.inputWidth
height: loginDialog.inputHeight / 2
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: loginDialog.statusText
color: "white"
}
Row {
id: messageSpinner
visible: loginDialog.statusText == "Logging in..."
onVisibleChanged: visible ? messageSpinnerAnimation.restart() : messageSpinnerAnimation.stop()
spacing: 24
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
Rectangle {
id: spinner1
width: 10
height: 10
color: "#ebebeb"
opacity: 0.05
}
Rectangle {
id: spinner2
width: 10
height: 10
color: "#ebebeb"
opacity: 0.05
}
Rectangle {
id: spinner3
width: 10
height: 10
color: "#ebebeb"
opacity: 0.05
}
SequentialAnimation {
id: messageSpinnerAnimation
running: messageSpinner.visible
loops: Animation.Infinite
NumberAnimation { target: spinner1; property: "opacity"; to: 1.0; duration: 1000 }
NumberAnimation { target: spinner2; property: "opacity"; to: 1.0; duration: 1000 }
NumberAnimation { target: spinner3; property: "opacity"; to: 1.0; duration: 1000 }
NumberAnimation { target: spinner1; property: "opacity"; to: 0.05; duration: 0 }
NumberAnimation { target: spinner2; property: "opacity"; to: 0.05; duration: 0 }
NumberAnimation { target: spinner3; property: "opacity"; to: 0.05; duration: 0 }
}
}
}
Rectangle {
width: loginDialog.inputWidth
height: loginDialog.inputHeight
radius: height / 2
color: "#353535"
TextInput {
anchors.fill: parent
text: "Login"
color: "white"
horizontalAlignment: Text.AlignHCenter
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
loginDialog.login(username.text, password.text)
}
}
}
Item {
anchors { left: parent.left; right: parent.right; }
height: loginDialog.inputHeight
Image {
id: hifiIcon
source: "../images/hifi-logo-blackish.svg"
width: loginDialog.inputHeight
height: width
sourceSize: Qt.size(width, height);
anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter }
}
Text {
anchors { verticalCenter: parent.verticalCenter; right: hifiIcon.left; margins: loginDialog.inputSpacing }
text: "Password?"
scale: 0.8
font.underline: true
color: "#e0e0e0"
MouseArea {
anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 }
cursorShape: Qt.PointingHandCursor
onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new")
}
}
Text {
anchors { verticalCenter: parent.verticalCenter; left: hifiIcon.right; margins: loginDialog.inputSpacing }
text: "Register"
scale: 0.8
font.underline: true
color: "#e0e0e0"
MouseArea {
anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 }
cursorShape: Qt.PointingHandCursor
onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/signup")
}
}
}
}
}
onShownChanged: {
if (!shown) {
username.text = ""
password.text = ""
loginDialog.statusText = ""
} else {
username.forceActiveFocus()
Loader {
id: bodyLoader
anchors.fill: parent
source: loginDialog.isSteamRunning() ? "LoginDialog/SignInBody.qml" : "LoginDialog/LinkAccountBody.qml"
}
}
Keys.onPressed: {
switch (event.key) {
if (!visible) {
return
}
if (event.modifiers === Qt.ControlModifier)
switch (event.key) {
case Qt.Key_A:
event.accepted = true
detailedText.selectAll()
break
case Qt.Key_C:
event.accepted = true
detailedText.copy()
break
case Qt.Key_Period:
if (Qt.platform.os === "osx") {
event.accepted = true
content.reject()
}
break
} else switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
root.shown = false;
event.accepted = true;
break;
event.accepted = true
destroy()
break
case Qt.Key_Enter:
case Qt.Key_Return:
if (username.activeFocus) {
event.accepted = true
password.forceActiveFocus()
} else if (password.activeFocus) {
event.accepted = true
if (username.text == "") {
username.forceActiveFocus()
} else {
loginDialog.login(username.text, password.text)
}
}
event.accepted = true
break
}
}

View file

@ -0,0 +1,125 @@
//
// CompleteProfileBody.qml
//
// Created by Clement on 7/18/16
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: completeProfileBody
clip: true
width: pane.width
height: pane.height
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, additionalTextContainer.contentWidth)
var targetHeight = 4 * hifi.dimensions.contentSpacing.y + buttons.height + additionalTextContainer.height
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
}
}
Row {
id: buttons
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
margins: 0
topMargin: 3 * hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
anchors.verticalCenter: parent.verticalCenter
width: 200
text: qsTr("Create your profile")
color: hifi.buttons.blue
onClicked: loginDialog.createAccountFromStream()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.destroy()
}
}
ShortcutText {
id: additionalTextContainer
anchors {
top: buttons.bottom
horizontalCenter: parent.horizontalCenter
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
text: "<a href='https://fake.link'>Already have a High Fidelity profile? Link to an existing profile here.</a>"
wrapMode: Text.WordWrap
lineHeight: 2
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
onLinkActivated: {
bodyLoader.source = "LinkAccountBody.qml"
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
Component.onCompleted: {
root.title = qsTr("Complete Your Profile")
root.iconText = "<"
d.resize();
}
Connections {
target: loginDialog
onHandleCreateCompleted: {
console.log("Create Succeeded")
loginDialog.loginThroughSteam()
}
onHandleCreateFailed: {
console.log("Create Failed: " + error)
bodyLoader.source = "UsernameCollisionBody.qml"
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLoginCompleted: {
console.log("Login Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLoginFailed: {
console.log("Login Failed")
}
}
}

View file

@ -0,0 +1,215 @@
//
// LinkAccountBody.qml
//
// Created by Clement on 7/18/16
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: linkAccountBody
clip: true
width: root.pane.width
height: root.pane.height
function login() {
mainTextContainer.visible = false
loginDialog.login(usernameField.text, passwordField.text)
}
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, form.contentWidth)
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
4 * hifi.dimensions.contentSpacing.y + form.height +
4 * hifi.dimensions.contentSpacing.y + buttons.height
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
}
}
ShortcutText {
id: mainTextContainer
anchors {
top: parent.top
left: parent.left
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
visible: false
text: qsTr("Username or password incorrect.")
wrapMode: Text.WordWrap
color: hifi.colors.redAccent
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
}
Column {
id: form
anchors {
top: mainTextContainer.bottom
left: parent.left
margins: 0
topMargin: 2 * hifi.dimensions.contentSpacing.y
}
spacing: 2 * hifi.dimensions.contentSpacing.y
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: usernameField
anchors {
verticalCenter: parent.verticalCenter
}
width: 350
label: "User Name or Email"
}
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Username?</a>"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
onLinkActivated: loginDialog.openUrl(link)
}
}
Row {
spacing: hifi.dimensions.contentSpacing.x
TextField {
id: passwordField
anchors {
verticalCenter: parent.verticalCenter
}
width: 350
label: "Password"
echoMode: TextInput.Password
}
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Password?</a>"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
onLinkActivated: loginDialog.openUrl(link)
}
}
}
Row {
id: buttons
anchors {
top: form.bottom
right: parent.right
margins: 0
topMargin: 3 * hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
width: 200
text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login")
color: hifi.buttons.blue
onClicked: linkAccountBody.login()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.destroy()
}
}
Component.onCompleted: {
root.title = qsTr("Sign Into High Fidelity")
root.iconText = "<"
d.resize();
usernameField.forceActiveFocus()
}
Connections {
target: loginDialog
onHandleLoginCompleted: {
console.log("Login Succeeded, linking steam account")
if (loginDialog.isSteamRunning()) {
loginDialog.linkSteam()
} else {
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
onHandleLoginFailed: {
console.log("Login Failed")
mainTextContainer.visible = true
}
onHandleLinkCompleted: {
console.log("Link Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLinkFailed: {
console.log("Link Failed")
}
}
Keys.onPressed: {
if (!visible) {
return
}
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
linkAccountBody.login()
break
}
}
}

View file

@ -0,0 +1,128 @@
//
// SignInBody.qml
//
// Created by Clement on 7/18/16
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: signInBody
clip: true
width: pane.width
height: pane.height
property bool required: false
function login() {
console.log("Trying to log in")
loginDialog.loginThroughSteam()
}
function cancel() {
root.destroy()
}
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
}
}
MenuItem {
id: mainTextContainer
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
text: required ? qsTr("This domain's owner requires that you sign in:")
: qsTr("Sign in to access your user account:")
wrapMode: Text.WordWrap
color: hifi.colors.baseGrayHighlight
lineHeight: 2
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
}
Row {
id: buttons
anchors {
top: mainTextContainer.bottom
horizontalCenter: parent.horizontalCenter
margins: 0
topMargin: 2 * hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
anchors.verticalCenter: parent.verticalCenter
width: undefined // invalidate so that the image's size sets the width
height: undefined // invalidate so that the image's size sets the height
focus: true
style: OriginalStyles.ButtonStyle {
background: Image {
id: buttonImage
source: "../../images/steam-sign-in.png"
}
}
onClicked: signInBody.login()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel");
onClicked: signInBody.cancel()
}
}
Component.onCompleted: {
root.title = required ? qsTr("Sign In Required")
: qsTr("Sign In")
root.iconText = ""
d.resize();
}
Connections {
target: loginDialog
onHandleLoginCompleted: {
console.log("Login Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLoginFailed: {
console.log("Login Failed")
bodyLoader.source = "CompleteProfileBody.qml"
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
}

View file

@ -0,0 +1,173 @@
//
// UsernameCollisionBody.qml
//
// Created by Clement on 7/18/16
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: usernameCollisionBody
clip: true
width: root.pane.width
height: root.pane.height
function create() {
mainTextContainer.visible = false
loginDialog.createAccountFromStream(textField.text)
}
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, Math.max(mainTextContainer.contentWidth,
termsContainer.contentWidth))
var targetHeight = mainTextContainer.height +
2 * hifi.dimensions.contentSpacing.y + textField.height +
5 * hifi.dimensions.contentSpacing.y + termsContainer.height +
1 * hifi.dimensions.contentSpacing.y + buttons.height
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
}
}
ShortcutText {
id: mainTextContainer
anchors {
top: parent.top
left: parent.left
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
text: qsTr("Your Steam username is not available.")
wrapMode: Text.WordWrap
color: hifi.colors.redAccent
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
}
TextField {
id: textField
anchors {
top: mainTextContainer.bottom
left: parent.left
margins: 0
topMargin: 2 * hifi.dimensions.contentSpacing.y
}
width: 250
placeholderText: "Choose your own"
}
MenuItem {
id: termsContainer
anchors {
top: textField.bottom
left: parent.left
margins: 0
topMargin: 3 * hifi.dimensions.contentSpacing.y
}
text: qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
wrapMode: Text.WordWrap
color: hifi.colors.baseGrayHighlight
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
onLinkActivated: loginDialog.openUrl(link)
}
Row {
id: buttons
anchors {
top: termsContainer.bottom
right: parent.right
margins: 0
topMargin: 1 * hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
anchors.verticalCenter: parent.verticalCenter
width: 200
text: qsTr("Create your profile")
color: hifi.buttons.blue
onClicked: usernameCollisionBody.create()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.destroy()
}
}
Component.onCompleted: {
root.title = qsTr("Complete Your Profile")
root.iconText = "<"
d.resize();
}
Connections {
target: loginDialog
onHandleCreateCompleted: {
console.log("Create Succeeded")
loginDialog.loginThroughSteam()
}
onHandleCreateFailed: {
console.log("Create Failed: " + error)
mainTextContainer.visible = true
mainTextContainer.text = "\"" + textField.text + qsTr("\" is invalid or already taken.")
}
onHandleLoginCompleted: {
console.log("Login Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLoginFailed: {
console.log("Login Failed")
}
}
Keys.onPressed: {
if (!visible) {
return
}
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
usernameCollisionBody.create()
break
}
}
}

View file

@ -0,0 +1,90 @@
//
// WelcomeBody.qml
//
// Created by Clement on 7/18/16
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import "../controls-uit"
import "../styles-uit"
Item {
id: welcomeBody
clip: true
width: pane.width
height: pane.height
property bool welcomeBack: false
function setTitle() {
root.title = (welcomeBack ? qsTr("Welcome back <b>") : qsTr("Welcome <b>")) + Account.username + qsTr("</b>!")
root.iconText = ""
d.resize();
}
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
}
}
MenuItem {
id: mainTextContainer
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
text: qsTr("You are now signed into High Fidelity")
wrapMode: Text.WordWrap
color: hifi.colors.baseGrayHighlight
lineHeight: 2
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
}
Row {
id: buttons
anchors {
top: mainTextContainer.bottom
horizontalCenter: parent.horizontalCenter
margins: 0
topMargin: 2 * hifi.dimensions.contentSpacing.y
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Close");
onClicked: root.destroy()
}
}
Component.onCompleted: welcomeBody.setTitle()
Connections {
target: Account
onUsernameChanged: welcomeBody.setTitle()
}
}

View file

@ -0,0 +1,20 @@
//
// HorizontalRule.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: hifi.colors.lightGray
}

View file

@ -0,0 +1,21 @@
//
// HorizontalSpacer.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import "../styles-uit"
Item {
id: root
property alias size: root.width
width: hifi.dimensions.controlInterlineHeight
height: 1 // Must be non-zero
}

View file

@ -13,6 +13,9 @@ import QtQuick 2.5
import "../styles-uit"
Item {
id: root
property alias size: root.height
width: 1 // Must be non-zero
height: hifi.dimensions.controlInterlineHeight
}

View file

@ -21,8 +21,6 @@ import "messageDialog"
ModalWindow {
id: root
HifiConstants { id: hifi }
implicitWidth: 640
implicitHeight: 320
destroyOnCloseButton: true
destroyOnHidden: true
visible: true
@ -70,7 +68,7 @@ ModalWindow {
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWdith: 1280
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
@ -80,7 +78,7 @@ ModalWindow {
+ (informativeTextContainer.text != "" ? informativeTextContainer.contentHeight + 3 * hifi.dimensions.contentSpacing.y : 0)
+ buttons.height
+ (content.state === "expanded" ? details.implicitHeight + hifi.dimensions.contentSpacing.y : 0)
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth)
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWidth) ? d.maxWidth : targetWidth)
root.height = (targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)
}
}

View file

@ -0,0 +1,18 @@
//
// ButtonLabel.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayBold {
font.pixelSize: hifi.fontSizes.buttonLabel
}

View file

@ -0,0 +1,20 @@
//
// IconButton.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayRegular {
font.pixelSize: hifi.fontSizes.iconButton
font.capitalization: Font.AllUppercase
font.letterSpacing: 1.5
}

View file

@ -0,0 +1,18 @@
//
// InputLabel.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewaySemiBold {
font.pixelSize: hifi.fontSizes.inputLabel
}

View file

@ -0,0 +1,18 @@
//
// ListItem.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayRegular {
font.pixelSize: hifi.fontSizes.listItem
}

View file

@ -0,0 +1,18 @@
//
// Logs.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
AnonymousProRegular {
font.pixelSize: hifi.fontSizes.logs
}

View file

@ -0,0 +1,19 @@
//
// MenuItem.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewaySemiBold {
lineHeight: 2
font.pixelSize: hifi.fontSizes.menuItem
}

View file

@ -0,0 +1,18 @@
//
// OverlayTitle.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayRegular {
font.pixelSize: hifi.fontSizes.overlayTitle
}

View file

@ -0,0 +1,19 @@
//
// SectionName.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayRegular {
font.pixelSize: hifi.fontSizes.sectionName
font.capitalization: Font.AllUppercase
}

View file

@ -0,0 +1,18 @@
//
// ShortcutText.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayLight {
font.pixelSize: hifi.fontSizes.shortcutText
}

View file

@ -0,0 +1,19 @@
//
// TabName.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayRegular {
font.pixelSize: hifi.fontSizes.tabName
font.capitalization: Font.AllUppercase
}

View file

@ -0,0 +1,18 @@
//
// TextFieldInput.qml
//
// Created by Clement on 7/18/16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
FiraSansSemiBold {
font.pixelSize: hifi.fontSizes.textFieldInput
}

View file

@ -1,35 +0,0 @@
//
// Created by Bradley Austin Davis on 2016/07/11
// Copyright 2013-2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#version 410 core
uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0);
layout(location = 0) in vec3 inLineDistance;
out vec4 FragColor;
void main() {
vec2 d = inLineDistance.xy;
d.y = abs(d.y);
d.x = abs(d.x);
if (d.x > 1.0) {
d.x = (d.x - 1.0) / 0.02;
} else {
d.x = 0.0;
}
float alpha = 1.0 - length(d);
if (alpha <= 0.0) {
discard;
}
alpha = pow(alpha, 10.0);
if (alpha < 0.05) {
discard;
}
FragColor = vec4(color.rgb, alpha);
}

View file

@ -1,70 +0,0 @@
//
// Created by Bradley Austin Davis on 2016/07/11
// Copyright 2013-2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#version 410 core
#extension GL_EXT_geometry_shader4 : enable
layout(location = 0) out vec3 outLineDistance;
layout(lines) in;
layout(triangle_strip, max_vertices = 24) out;
vec3[2] getOrthogonals(in vec3 n, float scale) {
float yDot = abs(dot(n, vec3(0, 1, 0)));
vec3 result[2];
if (yDot < 0.9) {
result[0] = normalize(cross(n, vec3(0, 1, 0)));
} else {
result[0] = normalize(cross(n, vec3(1, 0, 0)));
}
// The cross of result[0] and n is orthogonal to both, which are orthogonal to each other
result[1] = cross(result[0], n);
result[0] *= scale;
result[1] *= scale;
return result;
}
vec2 orthogonal(vec2 v) {
vec2 result = v.yx;
result.y *= -1.0;
return result;
}
void main() {
vec2 endpoints[2];
for (int i = 0; i < 2; ++i) {
endpoints[i] = gl_PositionIn[i].xy / gl_PositionIn[i].w;
}
vec2 lineNormal = normalize(endpoints[1] - endpoints[0]);
vec2 lineOrthogonal = orthogonal(lineNormal);
lineNormal *= 0.02;
lineOrthogonal *= 0.02;
gl_Position = gl_PositionIn[0];
gl_Position.xy -= lineOrthogonal;
outLineDistance = vec3(-1.02, -1, gl_Position.z);
EmitVertex();
gl_Position = gl_PositionIn[0];
gl_Position.xy += lineOrthogonal;
outLineDistance = vec3(-1.02, 1, gl_Position.z);
EmitVertex();
gl_Position = gl_PositionIn[1];
gl_Position.xy -= lineOrthogonal;
outLineDistance = vec3(1.02, -1, gl_Position.z);
EmitVertex();
gl_Position = gl_PositionIn[1];
gl_Position.xy += lineOrthogonal;
outLineDistance = vec3(1.02, 1, gl_Position.z);
EmitVertex();
EndPrimitive();
}

View file

@ -1,15 +0,0 @@
//
// Created by Bradley Austin Davis on 2016/07/11
// Copyright 2013-2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#version 410 core
uniform mat4 mvp = mat4(1);
in vec3 Position;
void main() {
gl_Position = mvp * vec4(Position, 1);
}

View file

@ -6,18 +6,27 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#version 410 core
uniform sampler2D sampler;
uniform float alpha = 1.0;
uniform vec4 glowPoints = vec4(-1);
uniform vec4 glowColors[2];
uniform vec2 resolution = vec2(3960.0, 1188.0);
uniform float radius = 0.005;
struct OverlayData {
mat4 mvp;
vec4 glowPoints;
vec4 glowColors[2];
vec4 resolutionRadiusAlpha;
};
layout(std140) uniform overlayBuffer {
OverlayData overlay;
};
vec2 resolution = overlay.resolutionRadiusAlpha.xy;
float radius = overlay.resolutionRadiusAlpha.z;
float alpha = overlay.resolutionRadiusAlpha.w;
vec4 glowPoints = overlay.glowPoints;
vec4 glowColors[2] = overlay.glowColors;
in vec3 vPosition;
in vec2 vTexCoord;
in vec4 vGlowPoints;
out vec4 FragColor;
@ -31,9 +40,10 @@ float easeInOutCubic(float f) {
}
void main() {
FragColor = texture(sampler, vTexCoord);
vec2 aspect = resolution;
aspect /= resolution.x;
FragColor = texture(sampler, vTexCoord);
float glowIntensity = 0.0;
float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect);

View file

@ -6,12 +6,21 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#version 410 core
struct OverlayData {
mat4 mvp;
vec4 glowPoints;
vec4 glowColors[2];
vec4 resolutionRadiusAlpha;
};
uniform mat4 mvp = mat4(1);
layout(std140) uniform overlayBuffer {
OverlayData overlay;
};
in vec3 Position;
in vec2 TexCoord;
mat4 mvp = overlay.mvp;
layout(location = 0) in vec3 Position;
layout(location = 3) in vec2 TexCoord;
out vec3 vPosition;
out vec2 vTexCoord;

View file

@ -86,15 +86,16 @@
#include <PhysicsHelpers.h>
#include <plugins/PluginManager.h>
#include <plugins/CodecPlugin.h>
#include <RecordingScriptingInterface.h>
#include <RenderableWebEntityItem.h>
#include <RenderShadowTask.h>
#include <RenderDeferredTask.h>
#include <ResourceCache.h>
#include <SceneScriptingInterface.h>
#include <RecordingScriptingInterface.h>
#include <ScriptEngines.h>
#include <ScriptCache.h>
#include <SoundCache.h>
#include <ScriptEngines.h>
#include <steamworks-wrapper/SteamClient.h>
#include <Tooltip.h>
#include <udt/PacketHeaders.h>
#include <UserActivityLogger.h>
@ -256,7 +257,10 @@ public:
void run() override {
while (!_quit) {
QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS);
// Don't do heartbeat detection under nsight
if (nsightActive()) {
continue;
}
uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us
uint64_t now = usecTimestampNow();
auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0;
@ -304,8 +308,6 @@ public:
// Don't actually crash in debug builds, in case this apparent deadlock is simply from
// the developer actively debugging code
#ifdef NDEBUG
deadlockDetectionCrash();
#endif
}
@ -778,16 +780,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_glWidget->makeCurrent();
_glWidget->initializeGL();
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->create(_glWidget->context()->contextHandle());
_chromiumShareContext->makeCurrent();
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
_offscreenContext = new OffscreenGLCanvas();
_offscreenContext->create(_glWidget->context()->contextHandle());
_offscreenContext->makeCurrent();
initializeGL();
_offscreenContext->makeCurrent();
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
@ -1497,11 +1490,18 @@ void Application::initializeGL() {
_isGLInitialized = true;
}
_glWidget->makeCurrent();
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->create(_glWidget->context()->contextHandle());
_chromiumShareContext->makeCurrent();
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
_glWidget->makeCurrent();
gpu::Context::init<gpu::gl::GLBackend>();
_gpuContext = std::make_shared<gpu::Context>();
// The gpu context can make child contexts for transfers, so
// we need to restore primary rendering context
_offscreenContext->makeCurrent();
_glWidget->makeCurrent();
initDisplay();
qCDebug(interfaceapp, "Initialized Display.");
@ -1520,7 +1520,8 @@ void Application::initializeGL() {
// Needs to happen AFTER the render engine initialization to access its configuration
initializeUi();
qCDebug(interfaceapp, "Initialized Offscreen UI.");
_offscreenContext->makeCurrent();
_glWidget->makeCurrent();
// call Menu getInstance static method to set up the menu
// Needs to happen AFTER the QML UI initialization
@ -1536,8 +1537,13 @@ void Application::initializeGL() {
_idleLoopStdev.reset();
_offscreenContext = new OffscreenGLCanvas();
_offscreenContext->create(_glWidget->context()->contextHandle());
_offscreenContext->makeCurrent();
// update before the first render
update(0);
}
FrameTimingsScriptingInterface _frameTimingsScriptingInterface;
@ -1554,7 +1560,7 @@ void Application::initializeUi() {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_offscreenContext->getContext());
offscreenUi->create(_glWidget->context()->contextHandle());
auto rootContext = offscreenUi->getRootContext();
@ -1631,6 +1637,8 @@ void Application::initializeUi() {
rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
rootContext->setContextProperty("Steam", new SteamScriptingInterface(engine));
_glWidget->installEventFilter(offscreenUi.data());
@ -1723,17 +1731,7 @@ void Application::paintGL() {
PerformanceWarning warn(showWarnings, "Application::paintGL()");
resizeGL();
// Before anything else, let's sync up the gpuContext with the true glcontext used in case anything happened
{
PerformanceTimer perfTimer("syncCache");
renderArgs._context->syncCache();
}
auto framebufferCache = DependencyManager::get<FramebufferCache>();
// Final framebuffer that will be handled to the display-plugin
auto finalFramebuffer = framebufferCache->getFramebuffer();
_gpuContext->beginFrame(finalFramebuffer, getHMDSensorPose());
_gpuContext->beginFrame(getHMDSensorPose());
// Reset the gpu::Context Stages
// Back to the default framebuffer;
gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) {
@ -1863,7 +1861,10 @@ void Application::paintGL() {
getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform());
// Primary rendering pass
auto framebufferCache = DependencyManager::get<FramebufferCache>();
const QSize size = framebufferCache->getFrameBufferSize();
// Final framebuffer that will be handled to the display-plugin
auto finalFramebuffer = framebufferCache->getFramebuffer();
{
PROFILE_RANGE(__FUNCTION__ "/mainRender");
@ -1888,7 +1889,6 @@ void Application::paintGL() {
auto baseProjection = renderArgs.getViewFrustum().getProjection();
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
float IPDScale = hmdInterface->getIPDScale();
mat4 headPose = displayPlugin->getHeadPose();
// FIXME we probably don't need to set the projection matrix every frame,
// only when the display plugin changes (or in non-HMD modes when the user
@ -1904,13 +1904,6 @@ void Application::paintGL() {
// Apply IPD scaling
mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
eyeOffsets[eye] = eyeOffsetTransform;
// Tell the plugin what pose we're using to render. In this case we're just using the
// unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
// for rotational timewarp. If we move to support positonal timewarp, we need to
// ensure this contains the full pose composed with the eye offsets.
displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform));
eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection);
});
renderArgs._context->setStereoProjections(eyeProjections);
@ -1918,36 +1911,26 @@ void Application::paintGL() {
}
renderArgs._blitFramebuffer = finalFramebuffer;
displaySide(&renderArgs, _myCamera);
renderArgs._blitFramebuffer.reset();
renderArgs._context->enableStereo(false);
}
_gpuContext->endFrame();
gpu::TexturePointer overlayTexture = _applicationOverlay.acquireOverlay();
if (overlayTexture) {
displayPlugin->submitOverlayTexture(overlayTexture);
}
// deliver final composited scene to the display plugin
auto frame = _gpuContext->endFrame();
frame->frameIndex = _frameCount;
frame->framebuffer = finalFramebuffer;
frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
};
frame->overlay = _applicationOverlay.getOverlayTexture();
// deliver final scene rendering commands to the display plugin
{
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
PerformanceTimer perfTimer("pluginOutput");
auto finalTexture = finalFramebuffer->getRenderBuffer(0);
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
_lockedFramebufferMap[finalTexture] = finalFramebuffer;
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
{
PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
PerformanceTimer perfTimer("pluginSubmitScene");
displayPlugin->submitSceneTexture(_frameCount, finalTexture);
}
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
displayPlugin->submitFrame(frame);
}
// Reset the framebuffer and stereo state
renderArgs._blitFramebuffer.reset();
renderArgs._context->enableStereo(false);
{
Stats::getInstance()->setRenderDetails(renderArgs._details);
}
@ -2943,6 +2926,8 @@ void Application::idle(float nsecsElapsed) {
PROFILE_RANGE(__FUNCTION__);
SteamClient::runCallbacks();
float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND;
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
@ -3251,6 +3236,14 @@ void Application::init() {
addressLookupString = arguments().value(urlIndex + 1);
}
// when +connect_lobby in command line, join steam lobby
const QString STEAM_LOBBY_COMMAND_LINE_KEY = "+connect_lobby";
int lobbyIndex = arguments().indexOf(STEAM_LOBBY_COMMAND_LINE_KEY);
if (lobbyIndex != -1) {
QString lobbyId = arguments().value(lobbyIndex + 1);
SteamClient::joinLobby(lobbyId);
}
Setting::Handle<bool> firstRun { Settings::firstRun, true };
if (addressLookupString.isEmpty() && firstRun.get()) {
qDebug() << "First run and no URL passed... attempting to go to Home or Entry...";
@ -4837,6 +4830,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine));
}
bool Application::canAcceptURL(const QString& urlString) const {
@ -4875,7 +4870,7 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
}
if (defaultUpload) {
toggleAssetServerWidget(urlString);
showAssetServerWidget(urlString);
}
return defaultUpload;
}
@ -5063,7 +5058,7 @@ void Application::toggleRunningScriptsWidget() const {
//}
}
void Application::toggleAssetServerWidget(QString filePath) {
void Application::showAssetServerWidget(QString filePath) {
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
return;
}
@ -5395,6 +5390,7 @@ void Application::updateDisplayMode() {
DisplayPluginList advanced;
DisplayPluginList developer;
foreach(auto displayPlugin, displayPlugins) {
displayPlugin->setContext(_gpuContext);
auto grouping = displayPlugin->getGrouping();
switch (grouping) {
case Plugin::ADVANCED:
@ -5464,9 +5460,6 @@ void Application::updateDisplayMode() {
_displayPlugin->deactivate();
}
// FIXME probably excessive and useless context switching
_offscreenContext->makeCurrent();
bool active = newDisplayPlugin->activate();
if (!active) {
@ -5611,20 +5604,6 @@ bool Application::makeRenderingContextCurrent() {
return _offscreenContext->makeCurrent();
}
void Application::releaseSceneTexture(const gpu::TexturePointer& texture) {
Q_ASSERT(QThread::currentThread() == thread());
auto& framebufferMap = _lockedFramebufferMap;
Q_ASSERT(framebufferMap.contains(texture));
auto framebufferPointer = framebufferMap[texture];
framebufferMap.remove(texture);
auto framebufferCache = DependencyManager::get<FramebufferCache>();
framebufferCache->releaseFramebuffer(framebufferPointer);
}
void Application::releaseOverlayTexture(const gpu::TexturePointer& texture) {
_applicationOverlay.releaseOverlay(texture);
}
bool Application::isForeground() const {
return _isForeground && !_window->isMinimized();
}

View file

@ -112,8 +112,6 @@ public:
virtual MainWindow* getPrimaryWindow() override;
virtual QOpenGLContext* getPrimaryContext() override;
virtual bool makeRenderingContextCurrent() override;
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override;
virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override;
virtual bool isForeground() const override;
virtual DisplayPluginPointer getActiveDisplayPlugin() const override;
@ -280,7 +278,7 @@ public slots:
Q_INVOKABLE void loadScriptURLDialog() const;
void toggleLogDialog();
void toggleRunningScriptsWidget() const;
void toggleAssetServerWidget(QString filePath = "");
Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
void handleLocalServerConnection() const;
void readArgumentsFromLocalSocket() const;
@ -435,7 +433,6 @@ private:
InputPluginList _activeInputPlugins;
bool _activatingDisplayPlugin { false };
QMap<gpu::TexturePointer, gpu::FramebufferPointer> _lockedFramebufferMap;
QUndoStack _undoStack;
UndoStackScriptingInterface _undoStackScriptingInterface;

View file

@ -15,6 +15,7 @@
#include <AddressManager.h>
#include <DomainHandler.h>
#include <NodeList.h>
#include <steamworks-wrapper/SteamClient.h>
#include <UserActivityLogger.h>
#include <UUID.h>
@ -36,11 +37,11 @@ const QString SESSION_ID_KEY = "session_id";
void DiscoverabilityManager::updateLocation() {
auto accountManager = DependencyManager::get<AccountManager>();
if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
auto addressManager = DependencyManager::get<AddressManager>();
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
auto addressManager = DependencyManager::get<AddressManager>();
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
// construct a QJsonObject given the user's current address information
QJsonObject rootObject;
@ -48,8 +49,6 @@ void DiscoverabilityManager::updateLocation() {
QString pathString = addressManager->currentPath();
const QString LOCATION_KEY_IN_ROOT = "location";
const QString PATH_KEY_IN_LOCATION = "path";
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
@ -90,6 +89,7 @@ void DiscoverabilityManager::updateLocation() {
// we have a changed location, send it now
_lastLocationObject = locationObject;
const QString LOCATION_KEY_IN_ROOT = "location";
rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
apiPath = API_USER_LOCATION_PATH;
@ -109,6 +109,9 @@ void DiscoverabilityManager::updateLocation() {
accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional,
QNetworkAccessManager::PutOperation, callbackParameters);
}
// Update Steam
SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentFacingAddress());
}
void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) {

View file

@ -134,7 +134,7 @@ Menu::Menu() {
// Edit > My Asset Server
auto assetServerAction = addActionToQMenuAndActionHash(editMenu, MenuOption::AssetServer,
Qt::CTRL | Qt::SHIFT | Qt::Key_A,
qApp, SLOT(toggleAssetServerWidget()));
qApp, SLOT(showAssetServerWidget()));
auto nodeList = DependencyManager::get<NodeList>();
QObject::connect(nodeList.data(), &NodeList::canWriteAssetsChanged, assetServerAction, &QAction::setEnabled);
assetServerAction->setEnabled(nodeList->getThisNodeCanWriteAssets());

View file

@ -142,7 +142,7 @@ void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, -1000, 1000);
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch.resetViewTransform();
geometryCache->renderQuad(batch, x, y, w, h, backgroundColor, _audioScopeBackground);
renderLineStrip(batch, _inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);

View file

@ -122,11 +122,13 @@ MyAvatar::MyAvatar(RigPointer rig) :
_driveKeys[i] = 0.0f;
}
// Necessary to select the correct slot
using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool);
// connect to AddressManager signal for location jumps
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
[=](const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation){
goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation);
});
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
this, static_cast<SlotType>(&MyAvatar::goToLocation));
_characterController.setEnabled(true);
@ -1859,7 +1861,7 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
glm::quat quatOrientation = cancelOutRollAndPitch(newOrientation);
if (shouldFaceLocation) {
quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
quatOrientation = newOrientation * glm::angleAxis(PI, Vectors::UP);
// move the user a couple units away
const float DISTANCE_TO_USER = 2.0f;

View file

@ -8,6 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <thread>
#include <QCommandLineParser>
#include <QDebug>
#include <QDir>
@ -20,12 +22,13 @@
#include <gl/OpenGLVersionChecker.h>
#include <SharedUtil.h>
#include <steamworks-wrapper/SteamClient.h>
#include "AddressManager.h"
#include "Application.h"
#include "InterfaceLogging.h"
#include "UserActivityLogger.h"
#include "MainWindow.h"
#include <thread>
#ifdef HAS_BUGSPLAT
#include <BuildInfo.h>
@ -137,6 +140,8 @@ int main(int argc, const char* argv[]) {
// or in the main window ctor, before GL startup.
Application::initPlugins(arguments);
SteamClient::init();
int exitCode;
{
QSettings::setDefaultFormat(QSettings::IniFormat);
@ -202,6 +207,8 @@ int main(int argc, const char* argv[]) {
Application::shutdownPlugins();
SteamClient::shutdown();
qCDebug(interfaceapp, "Normal exit.");
#if !defined(DEBUG) && !defined(Q_OS_LINUX)
// HACK: exit immediately (don't handle shutdown callbacks) for Release build

View file

@ -15,6 +15,9 @@
AccountScriptingInterface* AccountScriptingInterface::getInstance() {
static AccountScriptingInterface sharedInstance;
auto accountManager = DependencyManager::get<AccountManager>();
QObject::connect(accountManager.data(), &AccountManager::profileChanged,
&sharedInstance, &AccountScriptingInterface::usernameChanged);
return &sharedInstance;
}

View file

@ -17,6 +17,11 @@
class AccountScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
signals:
void usernameChanged();
public slots:
static AccountScriptingInterface* getInstance();
QString getUsername();

View file

@ -179,6 +179,10 @@ QScriptValue WindowScriptingInterface::save(const QString& title, const QString&
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
}
void WindowScriptingInterface::showAssetServer(const QString& upload) {
QMetaObject::invokeMethod(qApp, "showAssetServerWidget", Qt::QueuedConnection, Q_ARG(QString, upload));
}
int WindowScriptingInterface::getInnerWidth() {
return qApp->getWindow()->geometry().width();
}

View file

@ -53,6 +53,7 @@ public slots:
CustomPromptResult customPrompt(const QVariant& config);
QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
void showAssetServer(const QString& upload = "");
void copyToClipboard(const QString& text);
void takeSnapshot(bool notify);
void shareSnapshot(const QString& path);

View file

@ -67,7 +67,9 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
// Execute the batch into our framebuffer
doInBatch(renderArgs->_context, [&](gpu::Batch& batch) {
PROFILE_RANGE_BATCH(batch, "ApplicationOverlayRender");
renderArgs->_batch = &batch;
batch.enableStereo(false);
int width = _overlayFramebuffer->getWidth();
int height = _overlayFramebuffer->getHeight();
@ -101,7 +103,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
geometryCache->useSimpleDrawPipeline(batch);
batch.setProjectionTransform(mat4());
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch.resetViewTransform();
batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _uiTexture);
geometryCache->renderUnitQuad(batch, glm::vec4(1));
@ -121,7 +123,7 @@ void ApplicationOverlay::renderAudioScope(RenderArgs* renderArgs) {
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch.resetViewTransform();
// Render the audio scope
DependencyManager::get<AudioScope>()->render(renderArgs, width, height);
@ -140,7 +142,7 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch.resetViewTransform();
// Render all of the Script based "HUD" aka 2D overlays.
// note: we call them HUD, as opposed to 2D, only because there are some cases of 3D HUD overlays, like the
@ -166,7 +168,7 @@ void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch.resetViewTransform();
float screenRatio = ((float)qApp->getDevicePixelRatio());
float renderRatio = ((float)qApp->getRenderResolutionScale());
@ -228,7 +230,7 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr
geometryCache->useSimpleDrawPipeline(batch);
batch.setProjectionTransform(mat4());
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch.resetViewTransform();
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
// FIXME: THe line width of CONNECTION_STATUS_BORDER_LINE_WIDTH is not supported anymore, we ll need a workaround
@ -246,10 +248,6 @@ static const auto COLOR_FORMAT = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)
static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
std::mutex _textureGuard;
using Lock = std::unique_lock<std::mutex>;
std::queue<gpu::TexturePointer> _availableTextures;
void ApplicationOverlay::buildFramebufferObject() {
PROFILE_RANGE(__FUNCTION__);
@ -265,22 +263,6 @@ void ApplicationOverlay::buildFramebufferObject() {
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
}
if (!_overlayFramebuffer->getRenderBuffer(0)) {
gpu::TexturePointer newColorAttachment;
{
Lock lock(_textureGuard);
if (!_availableTextures.empty()) {
newColorAttachment = _availableTextures.front();
_availableTextures.pop();
}
}
if (newColorAttachment) {
newColorAttachment->resize2D(width, height, newColorAttachment->getNumSamples());
_overlayFramebuffer->setRenderBuffer(0, newColorAttachment);
}
}
// If the overlay framebuffer still has no color attachment, no textures were available for rendering, so build a new one
if (!_overlayFramebuffer->getRenderBuffer(0)) {
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER));
@ -288,20 +270,9 @@ void ApplicationOverlay::buildFramebufferObject() {
}
}
gpu::TexturePointer ApplicationOverlay::acquireOverlay() {
gpu::TexturePointer ApplicationOverlay::getOverlayTexture() {
if (!_overlayFramebuffer) {
return gpu::TexturePointer();
}
auto result = _overlayFramebuffer->getRenderBuffer(0);
_overlayFramebuffer->setRenderBuffer(0, gpu::TexturePointer());
return result;
}
void ApplicationOverlay::releaseOverlay(gpu::TexturePointer texture) {
if (texture) {
Lock lock(_textureGuard);
_availableTextures.push(texture);
} else {
qWarning() << "Attempted to release null texture";
}
}
return _overlayFramebuffer->getRenderBuffer(0);
}

View file

@ -26,8 +26,7 @@ public:
void renderOverlay(RenderArgs* renderArgs);
gpu::TexturePointer acquireOverlay();
void releaseOverlay(gpu::TexturePointer pointer);
gpu::TexturePointer getOverlayTexture();
private:
void renderStatsAndLogs(RenderArgs* renderArgs);

View file

@ -12,8 +12,11 @@
#include "LoginDialog.h"
#include <QDesktopServices>
#include <QJsonDocument>
#include <QNetworkReply>
#include <NetworkingConstants.h>
#include <steamworks-wrapper/SteamClient.h>
#include "AccountManager.h"
#include "DependencyManager.h"
@ -21,9 +24,7 @@
HIFI_QML_DEF(LoginDialog)
LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent),
_rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString())
{
LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) {
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::loginComplete,
this, &LoginDialog::handleLoginCompleted);
@ -53,36 +54,102 @@ void LoginDialog::toggleAction() {
}
}
void LoginDialog::handleLoginCompleted(const QUrl&) {
hide();
bool LoginDialog::isSteamRunning() const {
return SteamClient::isRunning();
}
void LoginDialog::handleLoginFailed() {
setStatusText("Invalid username or password");
}
void LoginDialog::setStatusText(const QString& statusText) {
if (statusText != _statusText) {
_statusText = statusText;
emit statusTextChanged();
}
}
QString LoginDialog::statusText() const {
return _statusText;
}
QString LoginDialog::rootUrl() const {
return _rootUrl;
}
void LoginDialog::login(const QString& username, const QString& password) {
void LoginDialog::login(const QString& username, const QString& password) const {
qDebug() << "Attempting to login " << username;
setStatusText("Logging in...");
DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
}
void LoginDialog::openUrl(const QString& url) {
qDebug() << url;
QDesktopServices::openUrl(url);
void LoginDialog::loginThroughSteam() {
qDebug() << "Attempting to login through Steam";
SteamClient::requestTicket([this](Ticket ticket) {
if (ticket.isNull()) {
emit handleLoginFailed();
return;
}
DependencyManager::get<AccountManager>()->requestAccessTokenWithSteam(ticket);
});
}
void LoginDialog::linkSteam() {
qDebug() << "Attempting to link Steam account";
SteamClient::requestTicket([this](Ticket ticket) {
if (ticket.isNull()) {
emit handleLoginFailed();
return;
}
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.jsonCallbackMethod = "linkCompleted";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "linkFailed";
const QString LINK_STEAM_PATH = "api/v1/user/steam/link";
QJsonObject payload;
payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket)));
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->sendRequest(LINK_STEAM_PATH, AccountManagerAuth::Required,
QNetworkAccessManager::PostOperation, callbackParams,
QJsonDocument(payload).toJson());
});
}
void LoginDialog::createAccountFromStream(QString username) {
qDebug() << "Attempting to create account from Steam info";
SteamClient::requestTicket([this, username](Ticket ticket) {
if (ticket.isNull()) {
emit handleLoginFailed();
return;
}
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.jsonCallbackMethod = "createCompleted";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "createFailed";
const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "api/v1/user/steam/create";
QJsonObject payload;
payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket)));
if (!username.isEmpty()) {
payload.insert("username", QJsonValue::fromVariant(QVariant(username)));
}
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->sendRequest(CREATE_ACCOUNT_FROM_STEAM_PATH, AccountManagerAuth::None,
QNetworkAccessManager::PostOperation, callbackParams,
QJsonDocument(payload).toJson());
});
}
void LoginDialog::openUrl(const QString& url) const {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto browser = offscreenUi->load("Browser.qml");
browser->setProperty("url", url);
}
void LoginDialog::linkCompleted(QNetworkReply& reply) {
emit handleLinkCompleted();
}
void LoginDialog::linkFailed(QNetworkReply& reply) {
emit handleLinkFailed(reply.errorString());
}
void LoginDialog::createCompleted(QNetworkReply& reply) {
emit handleCreateCompleted();
}
void LoginDialog::createFailed(QNetworkReply& reply) {
emit handleCreateFailed(reply.errorString());
}

View file

@ -16,35 +16,44 @@
#include <OffscreenQmlDialog.h>
class QNetworkReply;
class LoginDialog : public OffscreenQmlDialog {
Q_OBJECT
HIFI_QML_DECL
Q_PROPERTY(QString statusText READ statusText WRITE setStatusText NOTIFY statusTextChanged)
Q_PROPERTY(QString rootUrl READ rootUrl)
public:
static void toggleAction();
LoginDialog(QQuickItem* parent = nullptr);
void setStatusText(const QString& statusText);
QString statusText() const;
QString rootUrl() const;
signals:
void statusTextChanged();
protected:
void handleLoginCompleted(const QUrl& authURL);
void handleLoginCompleted();
void handleLoginFailed();
Q_INVOKABLE void login(const QString& username, const QString& password);
Q_INVOKABLE void openUrl(const QString& url);
private:
QString _statusText;
const QString _rootUrl;
void handleLinkCompleted();
void handleLinkFailed(QString error);
void handleCreateCompleted();
void handleCreateFailed(QString error);
public slots:
void linkCompleted(QNetworkReply& reply);
void linkFailed(QNetworkReply& reply);
void createCompleted(QNetworkReply& reply);
void createFailed(QNetworkReply& reply);
protected slots:
Q_INVOKABLE bool isSteamRunning() const;
Q_INVOKABLE void login(const QString& username, const QString& password) const;
Q_INVOKABLE void loginThroughSteam();
Q_INVOKABLE void linkSteam();
Q_INVOKABLE void createAccountFromStream(QString username = QString());
Q_INVOKABLE void openUrl(const QString& url) const;
};
#endif // hifi_LoginDialog_h

View file

@ -121,7 +121,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
batch.setResourceTexture(0, textureCache->getWhiteTexture()); // FIXME - do we really need to do this??
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch.resetViewTransform();
thisOverlay->render(renderArgs);
}

View file

@ -1,6 +1,7 @@
set(TARGET_NAME display-plugins)
AUTOSCRIBE_SHADER_LIB(gpu display-plugins)
setup_hifi_library(OpenGL)
link_hifi_libraries(shared plugins ui-plugins gl gpu-gl ui)
link_hifi_libraries(shared plugins ui-plugins gl gpu-gl ui render-utils)
target_opengl()

View file

@ -33,18 +33,6 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() {
return Parent::internalActivate();
}
void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
_wantVsync = true; // always
Parent::submitSceneTexture(frameIndex, sceneTexture);
}
void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {
if (_wantVsync != isVsyncEnabled()) {
enableVsync(_wantVsync);
}
Parent::internalPresent();
}
static const uint32_t MIN_THROTTLE_CHECK_FRAMES = 60;
bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const {

View file

@ -24,10 +24,6 @@ public:
virtual bool internalActivate() override;
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
virtual void internalPresent() override;
virtual bool isThrottled() const override;
protected:
@ -40,5 +36,4 @@ private:
QAction* _vsyncAction { nullptr };
uint32_t _framerateTarget { 0 };
int _fullscreenTarget{ -1 };
bool _wantVsync { true };
};

View file

@ -12,6 +12,7 @@
#include "NullDisplayPlugin.h"
#include "stereo/SideBySideStereoDisplayPlugin.h"
#include "stereo/InterleavedStereoDisplayPlugin.h"
#include "hmd/DebugHmdDisplayPlugin.h"
#include "Basic2DWindowOpenGLDisplayPlugin.h"
const QString& DisplayPlugin::MENU_PATH() {
@ -23,6 +24,7 @@ const QString& DisplayPlugin::MENU_PATH() {
DisplayPluginList getDisplayPlugins() {
DisplayPlugin* PLUGIN_POOL[] = {
new Basic2DWindowOpenGLDisplayPlugin(),
new DebugHmdDisplayPlugin(),
#ifdef DEBUG
new NullDisplayPlugin(),
#endif

View file

@ -11,6 +11,9 @@
#include <QtGui/QImage>
#include <ui-plugins/PluginContainer.h>
#include <FramebufferCache.h>
#include <gpu/Frame.h>
#include <gpu/Context.h>
const QString NullDisplayPlugin::NAME("NullDisplayPlugin");
@ -22,12 +25,10 @@ bool NullDisplayPlugin::hasFocus() const {
return false;
}
void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
_container->releaseSceneTexture(sceneTexture);
}
void NullDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) {
_container->releaseOverlayTexture(overlayTexture);
void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) {
if (frame) {
_gpuContext->consumeFrameUpdates(frame);
}
}
QImage NullDisplayPlugin::getScreenshot() const {

View file

@ -11,16 +11,14 @@
class NullDisplayPlugin : public DisplayPlugin {
public:
~NullDisplayPlugin() final {}
const QString& getName() const override { return NAME; }
grouping getGrouping() const override { return DEVELOPER; }
virtual ~NullDisplayPlugin() final {}
virtual const QString& getName() const override { return NAME; }
virtual grouping getGrouping() const override { return DEVELOPER; }
virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual bool hasFocus() const override;
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
virtual QImage getScreenshot() const override;
glm::uvec2 getRecommendedRenderSize() const override;
bool hasFocus() const override;
void submitFrame(const gpu::FramePointer& newFrame) override;
QImage getScreenshot() const override;
private:
static const QString NAME;
};

View file

@ -8,6 +8,7 @@
#include "OpenGLDisplayPlugin.h"
#include <condition_variable>
#include <queue>
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
@ -19,26 +20,56 @@
#if defined(Q_OS_MAC)
#include <OpenGL/CGLCurrent.h>
#endif
#include <gl/QOpenGLContextWrapper.h>
#include <gpu/Texture.h>
#include <gl/GLWidget.h>
#include <NumericalConstants.h>
#include <DependencyManager.h>
#include <shared/NsightHelpers.h>
#include <ui-plugins/PluginContainer.h>
#include <GLMHelpers.h>
#include <gl/QOpenGLContextWrapper.h>
#include <gl/GLWidget.h>
#include <gl/Config.h>
#include <gl/GLEscrow.h>
#include <GLMHelpers.h>
#include <CursorManager.h>
#include "CompositorHelper.h"
#include <gpu/Texture.h>
#include <gpu/StandardShaderLib.h>
#include <gpu/gl/GLShared.h>
#include <gpu/gl/GLBackend.h>
#include <GeometryCache.h>
#include <FramebufferCache.h>
#include <shared/NsightHelpers.h>
#include <ui-plugins/PluginContainer.h>
#include <ui/Menu.h>
#include <CursorManager.h>
#include "CompositorHelper.h"
#if THREADED_PRESENT
const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE(
// FIXME, for display plugins that don't block on something like vsync, just
// cap the present rate at 200
// const static unsigned int MAX_PRESENT_RATE = 200;
uniform sampler2D colorMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
float sRGBFloatToLinear(float value) {
const float SRGB_ELBOW = 0.04045;
return (value <= SRGB_ELBOW) ? value / 12.92 : pow((value + 0.055) / 1.055, 2.4);
}
vec3 colorToLinearRGB(vec3 srgb) {
return vec3(sRGBFloatToLinear(srgb.r), sRGBFloatToLinear(srgb.g), sRGBFloatToLinear(srgb.b));
}
void main(void) {
outFragColor.a = 1.0;
outFragColor.rgb = colorToLinearRGB(texture(colorMap, varTexCoord0).rgb);
}
)SCRIBE";
extern QThread* RENDER_THREAD;
class PresentThread : public QThread, public Dependency {
using Mutex = std::mutex;
@ -86,13 +117,22 @@ public:
virtual void run() override {
// FIXME determine the best priority balance between this and the main thread...
// It may be dependent on the display plugin being used, since VR plugins should
// have higher priority on rendering (although we could say that the Oculus plugin
// doesn't need that since it has async timewarp).
// A higher priority here
setPriority(QThread::HighPriority);
OpenGLDisplayPlugin* currentPlugin{ nullptr };
thread()->setPriority(QThread::HighestPriority);
Q_ASSERT(_context);
_context->makeCurrent();
Q_ASSERT(isCurrentContext(_context->contextHandle()));
while (!_shutdown) {
if (_pendingMainThreadOperation) {
PROFILE_RANGE("MainThreadOp")
{
Lock lock(_mutex);
_context->doneCurrent();
// Move the context to the main thread
_context->moveToThread(qApp->thread());
_pendingMainThreadOperation = false;
@ -111,33 +151,46 @@ public:
// Check for a new display plugin
{
Lock lock(_mutex);
_context->makeCurrent();
// Check if we have a new plugin to activate
while (!_newPluginQueue.empty()) {
auto newPlugin = _newPluginQueue.front();
if (newPlugin != currentPlugin) {
// Deactivate the old plugin
if (currentPlugin != nullptr) {
try {
currentPlugin->uncustomizeContext();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in uncustomizeContext: " << error.what();
}
_context->makeCurrent();
currentPlugin->uncustomizeContext();
CHECK_GL_ERROR();
_context->doneCurrent();
}
if (newPlugin) {
try {
newPlugin->customizeContext();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in customizeContext: " << error.what();
}
bool hasVsync = true;
bool wantVsync = newPlugin->wantVsync();
_context->makeCurrent();
#if defined(Q_OS_WIN)
wglSwapIntervalEXT(wantVsync ? 1 : 0);
hasVsync = wglGetSwapIntervalEXT() != 0;
#elif defined(Q_OS_MAC)
GLint interval = wantVsync ? 1 : 0;
newPlugin->swapBuffers();
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
newPlugin->swapBuffers();
CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
hasVsync = interval != 0;
#else
// TODO: Fill in for linux
Q_UNUSED(wantVsync);
#endif
newPlugin->setVsyncEnabled(hasVsync);
newPlugin->customizeContext();
CHECK_GL_ERROR();
_context->doneCurrent();
}
currentPlugin = newPlugin;
_newPluginQueue.pop();
_condition.notify_one();
}
}
_context->doneCurrent();
}
// If there's no active plugin, just sleep
@ -147,18 +200,14 @@ public:
continue;
}
// take the latest texture and present it
// Execute the frame and present it to the display device.
_context->makeCurrent();
if (isCurrentContext(_context->contextHandle())) {
try {
currentPlugin->present();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in presentation: " << error.what();
}
_context->doneCurrent();
} else {
qWarning() << "Makecurrent failed";
{
PROFILE_RANGE("PluginPresent")
currentPlugin->present();
CHECK_GL_ERROR();
}
_context->doneCurrent();
}
Lock lock(_mutex);
@ -204,27 +253,6 @@ private:
QGLContext* _context { nullptr };
};
#endif
OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
_sceneTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture){
cleanupForSceneTexture(texture);
_container->releaseSceneTexture(texture);
});
_overlayTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture) {
_container->releaseOverlayTexture(texture);
});
}
void OpenGLDisplayPlugin::cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture) {
withRenderThreadLock([&] {
Q_ASSERT(_sceneTextureToFrameIndexMap.contains(sceneTexture));
_sceneTextureToFrameIndexMap.remove(sceneTexture);
});
}
bool OpenGLDisplayPlugin::activate() {
if (!_cursorsData.size()) {
auto& cursorManager = Cursor::Manager::instance();
@ -242,9 +270,7 @@ bool OpenGLDisplayPlugin::activate() {
if (!_container) {
return false;
}
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
#if THREADED_PRESENT
// Start the present thread if necessary
QSharedPointer<PresentThread> presentThread;
if (DependencyManager::isSet<PresentThread>()) {
@ -259,7 +285,9 @@ bool OpenGLDisplayPlugin::activate() {
presentThread->start();
}
_presentThread = presentThread.data();
#endif
if (!RENDER_THREAD) {
RENDER_THREAD = _presentThread;
}
// Child classes may override this in order to do things like initialize
// libraries, etc
@ -267,17 +295,10 @@ bool OpenGLDisplayPlugin::activate() {
return false;
}
#if THREADED_PRESENT
// This should not return until the new context has been customized
// and the old context (if any) has been uncustomized
presentThread->setNewDisplayPlugin(this);
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
customizeContext();
_container->makeRenderingContextCurrent();
#endif
auto compositorHelper = DependencyManager::get<CompositorHelper>();
connect(compositorHelper.data(), &CompositorHelper::alphaChanged, [this] {
@ -296,20 +317,12 @@ bool OpenGLDisplayPlugin::activate() {
}
void OpenGLDisplayPlugin::deactivate() {
auto compositorHelper = DependencyManager::get<CompositorHelper>();
disconnect(compositorHelper.data());
#if THREADED_PRESENT
auto presentThread = DependencyManager::get<PresentThread>();
// Does not return until the GL transition has completeed
presentThread->setNewDisplayPlugin(nullptr);
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
uncustomizeContext();
_container->makeRenderingContextCurrent();
#endif
internalDeactivate();
_container->showDisplayPluginsTools(false);
@ -323,58 +336,99 @@ void OpenGLDisplayPlugin::deactivate() {
Parent::deactivate();
}
void OpenGLDisplayPlugin::customizeContext() {
#if THREADED_PRESENT
auto presentThread = DependencyManager::get<PresentThread>();
Q_ASSERT(thread() == presentThread->thread());
#endif
enableVsync();
getGLBackend()->setCameraCorrection(mat4());
for (auto& cursorValue : _cursorsData) {
auto& cursorData = cursorValue.second;
if (!cursorData.texture) {
const auto& image = cursorData.image;
glGenTextures(1, &cursorData.texture);
glBindTexture(GL_TEXTURE_2D, cursorData.texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
auto image = cursorData.image;
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
if ((image.width() > 0) && (image.height() > 0)) {
cursorData.texture.reset(
gpu::Texture::create2D(
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
image.width(), image.height(),
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
cursorData.texture->setUsage(usage.build());
cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits());
cursorData.texture->autoGenerateMips(-1);
}
}
glBindTexture(GL_TEXTURE_2D, 0);
}
using namespace oglplus;
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
Context::Disable(Capability::Blend);
Context::Disable(Capability::DepthTest);
Context::Disable(Capability::CullFace);
_program = loadDefaultShader();
auto uniforms = _program->ActiveUniforms();
while (!uniforms.Empty()) {
auto uniform = uniforms.Front();
if (uniform.Name() == "mvp") {
_mvpUniform = uniform.Index();
if (!_presentPipeline) {
{
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
state->setScissorEnable(true);
_simplePipeline = gpu::Pipeline::create(program, state);
}
if (uniform.Name() == "alpha") {
_alphaUniform = uniform.Index();
{
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_FRAG));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
state->setScissorEnable(true);
_presentPipeline = gpu::Pipeline::create(program, state);
}
{
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
_overlayPipeline = gpu::Pipeline::create(program, state);
}
{
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
_cursorPipeline = gpu::Pipeline::create(program, state);
}
uniforms.Next();
}
_plane = loadPlane(_program);
_compositeFramebuffer = std::make_shared<BasicFramebufferWrapper>();
_compositeFramebuffer->Init(getRecommendedRenderSize());
auto renderSize = getRecommendedRenderSize();
_compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y));
}
void OpenGLDisplayPlugin::uncustomizeContext() {
_presentPipeline.reset();
_cursorPipeline.reset();
_overlayPipeline.reset();
_compositeFramebuffer.reset();
_program.reset();
_plane.reset();
withPresentThreadLock([&] {
_currentFrame.reset();
while (!_newFrameQueue.empty()) {
_gpuContext->consumeFrameUpdates(_newFrameQueue.front());
_newFrameQueue.pop();
}
});
}
@ -420,172 +474,161 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
return false;
}
void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
if (_lockCurrentTexture) {
_container->releaseSceneTexture(sceneTexture);
return;
}
withRenderThreadLock([&] {
_sceneTextureToFrameIndexMap[sceneTexture] = frameIndex;
withNonPresentThreadLock([&] {
_newFrameQueue.push(newFrame);
});
// Submit it to the presentation thread via escrow
_sceneTextureEscrow.submit(sceneTexture);
#if THREADED_PRESENT
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
present();
_container->makeRenderingContextCurrent();
#endif
}
void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) {
// Submit it to the presentation thread via escrow
_overlayTextureEscrow.submit(overlayTexture);
}
void OpenGLDisplayPlugin::updateTextures() {
// FIXME intrduce a GPU wait instead of a CPU/GPU sync point?
#if THREADED_PRESENT
if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) {
#else
if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) {
#endif
updateFrameData();
_newFrameRate.increment();
}
_overlayTextureEscrow.fetchSignaledAndRelease(_currentOverlayTexture);
}
void OpenGLDisplayPlugin::updateFrameData() {
withPresentThreadLock([&] {
auto previousFrameIndex = _currentPresentFrameIndex;
_currentPresentFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture];
auto skippedCount = (_currentPresentFrameIndex - previousFrameIndex) - 1;
gpu::FramePointer oldFrame = _currentFrame;
uint32_t skippedCount = 0;
if (!_newFrameQueue.empty()) {
// We're changing frames, so we can cleanup any GL resources that might have been used by the old frame
_gpuContext->recycle();
}
while (!_newFrameQueue.empty()) {
_currentFrame = _newFrameQueue.front();
_newFrameQueue.pop();
_gpuContext->consumeFrameUpdates(_currentFrame);
if (_currentFrame && oldFrame) {
skippedCount += (_currentFrame->frameIndex - oldFrame->frameIndex) - 1;
}
}
_droppedFrameRate.increment(skippedCount);
});
}
void OpenGLDisplayPlugin::compositeOverlay() {
using namespace oglplus;
auto compositorHelper = DependencyManager::get<CompositorHelper>();
useProgram(_program);
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
// check the alpha
// Overlay draw
if (isStereo()) {
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
});
} else {
// Overlay draw
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
drawUnitQuad();
}
// restore the alpha
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void OpenGLDisplayPlugin::compositePointer() {
using namespace oglplus;
auto compositorHelper = DependencyManager::get<CompositorHelper>();
useProgram(_program);
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
Uniform<glm::mat4>(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
if (isStereo()) {
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
});
} else {
drawUnitQuad();
}
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
// restore the alpha
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void OpenGLDisplayPlugin::compositeScene() {
using namespace oglplus;
useProgram(_program);
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
drawUnitQuad();
}
void OpenGLDisplayPlugin::compositeLayers() {
using namespace oglplus;
auto targetRenderSize = getRecommendedRenderSize();
if (!_compositeFramebuffer || _compositeFramebuffer->size != targetRenderSize) {
_compositeFramebuffer = std::make_shared<BasicFramebufferWrapper>();
_compositeFramebuffer->Init(targetRenderSize);
}
_compositeFramebuffer->Bound(Framebuffer::Target::Draw, [&] {
Context::Viewport(targetRenderSize.x, targetRenderSize.y);
auto sceneTextureId = getSceneTextureId();
auto overlayTextureId = getOverlayTextureId();
glBindTexture(GL_TEXTURE_2D, sceneTextureId);
compositeScene();
if (overlayTextureId) {
glBindTexture(GL_TEXTURE_2D, overlayTextureId);
Context::Enable(Capability::Blend);
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
compositeOverlay();
auto compositorHelper = DependencyManager::get<CompositorHelper>();
if (compositorHelper->getReticleVisible()) {
auto& cursorManager = Cursor::Manager::instance();
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
glBindTexture(GL_TEXTURE_2D, cursorData.texture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, overlayTextureId);
compositePointer();
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
}
glBindTexture(GL_TEXTURE_2D, 0);
Context::Disable(Capability::Blend);
render([&](gpu::Batch& batch){
batch.enableStereo(false);
batch.setFramebuffer(_compositeFramebuffer);
batch.setPipeline(_overlayPipeline);
batch.setResourceTexture(0, _currentFrame->overlay);
if (isStereo()) {
for_each_eye([&](Eye eye) {
batch.setViewportTransform(eyeViewport(eye));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
compositeExtra();
});
}
void OpenGLDisplayPlugin::compositePointer() {
auto& cursorManager = Cursor::Manager::instance();
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
auto cursorTransform = DependencyManager::get<CompositorHelper>()->getReticleTransform(glm::mat4());
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setProjectionTransform(mat4());
batch.setFramebuffer(_compositeFramebuffer);
batch.setPipeline(_cursorPipeline);
batch.setResourceTexture(0, cursorData.texture);
batch.resetViewTransform();
batch.setModelTransform(cursorTransform);
if (isStereo()) {
for_each_eye([&](Eye eye) {
batch.setViewportTransform(eyeViewport(eye));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
});
}
void OpenGLDisplayPlugin::compositeScene() {
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setFramebuffer(_compositeFramebuffer);
batch.setViewportTransform(ivec4(uvec2(), _compositeFramebuffer->getSize()));
batch.setStateScissorRect(ivec4(uvec2(), _compositeFramebuffer->getSize()));
batch.resetViewTransform();
batch.setProjectionTransform(mat4());
batch.setPipeline(_simplePipeline);
batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
}
void OpenGLDisplayPlugin::compositeLayers() {
auto renderSize = getRecommendedRenderSize();
if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) {
_compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y));
}
{
PROFILE_RANGE_EX("compositeScene", 0xff0077ff, (uint64_t)presentCount())
compositeScene();
}
{
PROFILE_RANGE_EX("compositeOverlay", 0xff0077ff, (uint64_t)presentCount())
compositeOverlay();
}
auto compositorHelper = DependencyManager::get<CompositorHelper>();
if (compositorHelper->getReticleVisible()) {
PROFILE_RANGE_EX("compositePointer", 0xff0077ff, (uint64_t)presentCount())
compositePointer();
}
{
PROFILE_RANGE_EX("compositeExtra", 0xff0077ff, (uint64_t)presentCount())
compositeExtra();
}
}
void OpenGLDisplayPlugin::internalPresent() {
using namespace oglplus;
const uvec2& srcSize = _compositeFramebuffer->size;
uvec2 dstSize = getSurfacePixels();
_compositeFramebuffer->Bound(FramebufferTarget::Read, [&] {
Context::BlitFramebuffer(
0, 0, srcSize.x, srcSize.y,
0, 0, dstSize.x, dstSize.y,
BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(gpu::FramebufferPointer());
batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0));
batch.setPipeline(_presentPipeline);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
swapBuffers();
}
void OpenGLDisplayPlugin::present() {
PROFILE_RANGE_EX(__FUNCTION__, 0xffffff00, (uint64_t)presentCount())
updateFrameData();
incrementPresentCount();
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
{
PROFILE_RANGE_EX("recycle", 0xff00ff00, (uint64_t)presentCount())
_gpuContext->recycle();
}
if (_currentFrame) {
{
// Execute the frame rendering commands
PROFILE_RANGE_EX("execute", 0xff00ff00, (uint64_t)presentCount())
_gpuContext->executeFrame(_currentFrame);
}
updateTextures();
if (_currentSceneTexture) {
// Write all layers to a local framebuffer
compositeLayers();
{
PROFILE_RANGE_EX("composite", 0xff00ffff, (uint64_t)presentCount())
compositeLayers();
}
// Take the composite framebuffer and send it to the output device
internalPresent();
{
PROFILE_RANGE_EX("internalPresent", 0xff00ffff, (uint64_t)presentCount())
internalPresent();
}
_presentRate.increment();
_activeProgram.reset();
}
}
@ -595,7 +638,7 @@ float OpenGLDisplayPlugin::newFramePresentRate() const {
float OpenGLDisplayPlugin::droppedFrameRate() const {
float result;
withRenderThreadLock([&] {
withNonPresentThreadLock([&] {
result = _droppedFrameRate.rate();
});
return result;
@ -605,102 +648,27 @@ float OpenGLDisplayPlugin::presentRate() const {
return _presentRate.rate();
}
void OpenGLDisplayPlugin::drawUnitQuad() {
useProgram(_program);
_plane->Use();
_plane->Draw();
}
void OpenGLDisplayPlugin::enableVsync(bool enable) {
if (!_vsyncSupported) {
return;
}
#if defined(Q_OS_WIN)
wglSwapIntervalEXT(enable ? 1 : 0);
#elif defined(Q_OS_MAC)
GLint interval = enable ? 1 : 0;
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
#else
// TODO: Fill in for linux
return;
#endif
}
bool OpenGLDisplayPlugin::isVsyncEnabled() {
if (!_vsyncSupported) {
return true;
}
#if defined(Q_OS_WIN)
return wglGetSwapIntervalEXT() != 0;
#elif defined(Q_OS_MAC)
GLint interval;
CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
return interval != 0;
#else
// TODO: Fill in for linux
return true;
#endif
}
void OpenGLDisplayPlugin::swapBuffers() {
static auto widget = _container->getPrimaryWidget();
widget->swapBuffers();
}
void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const {
#if THREADED_PRESENT
static auto presentThread = DependencyManager::get<PresentThread>();
presentThread->withMainThreadContext(f);
_container->makeRenderingContextCurrent();
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
f();
_container->makeRenderingContextCurrent();
#endif
}
QImage OpenGLDisplayPlugin::getScreenshot() const {
using namespace oglplus;
auto width = _compositeFramebuffer->size.x;
if (isHmd()) {
width /= 2;
}
QImage screenshot(width, _compositeFramebuffer->size.y, QImage::Format_RGBA8888);
auto size = _compositeFramebuffer->getSize();
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
QImage screenshot(size.x, size.y, QImage::Format_ARGB32);
withMainThreadContext([&] {
Framebuffer::Bind(Framebuffer::Target::Read, _compositeFramebuffer->fbo);
Context::ReadPixels(0, 0, width, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits());
glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(uvec2(0), size), screenshot);
});
return screenshot.mirrored(false, true);
}
uint32_t OpenGLDisplayPlugin::getSceneTextureId() const {
if (!_currentSceneTexture) {
return 0;
}
return _currentSceneTexture->getHardwareId();
}
uint32_t OpenGLDisplayPlugin::getOverlayTextureId() const {
if (!_currentOverlayTexture) {
return 0;
}
return _currentOverlayTexture->getHardwareId();
}
void OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
using namespace oglplus;
uvec2 vpSize = _compositeFramebuffer->size;
vpSize.x /= 2;
uvec2 vpPos;
if (eye == Eye::Right) {
vpPos.x = vpSize.x;
}
Context::Viewport(vpPos.x, vpPos.y, vpSize.x, vpSize.y);
}
glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const {
uvec2 result;
auto window = _container->getPrimaryWidget();
@ -724,14 +692,7 @@ bool OpenGLDisplayPlugin::hasFocus() const {
return window ? window->hasFocus() : false;
}
void OpenGLDisplayPlugin::useProgram(const ProgramPtr& program) {
if (_activeProgram != program) {
program->Bind();
_activeProgram = program;
}
}
void OpenGLDisplayPlugin::assertIsRenderThread() const {
void OpenGLDisplayPlugin::assertNotPresentThread() const {
Q_ASSERT(QThread::currentThread() != _presentThread);
}
@ -740,8 +701,39 @@ void OpenGLDisplayPlugin::assertIsPresentThread() const {
}
bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
withRenderThreadLock([&] {
withNonPresentThreadLock([&] {
_compositeOverlayAlpha = _overlayAlpha;
});
return Parent::beginFrameRender(frameIndex);
}
ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
uvec2 vpSize = _currentFrame->framebuffer->getSize();
vpSize.x /= 2;
uvec2 vpPos;
if (eye == Eye::Right) {
vpPos.x = vpSize.x;
}
return ivec4(vpPos, vpSize);
}
gpu::gl::GLBackend* OpenGLDisplayPlugin::getGLBackend() {
if (!_gpuContext || !_gpuContext->getBackend()) {
return nullptr;
}
auto backend = _gpuContext->getBackend().get();
#if defined(Q_OS_MAC)
// Should be dynamic_cast, but that doesn't work in plugins on OSX
auto glbackend = static_cast<gpu::gl::GLBackend*>(backend);
#else
auto glbackend = dynamic_cast<gpu::gl::GLBackend*>(backend);
#endif
return glbackend;
}
void OpenGLDisplayPlugin::render(std::function<void(gpu::Batch& batch)> f) {
gpu::Batch batch;
f(batch);
_gpuContext->executeBatch(batch);
}

View file

@ -11,17 +11,21 @@
#include <condition_variable>
#include <memory>
#include <queue>
#include <QtCore/QTimer>
#include <QtGui/QImage>
#include <GLMHelpers.h>
#include <SimpleMovingAverage.h>
#include <gl/OglplusHelpers.h>
#include <gl/GLEscrow.h>
#include <shared/RateCounter.h>
#define THREADED_PRESENT 1
namespace gpu {
namespace gl {
class GLBackend;
}
}
class OpenGLDisplayPlugin : public DisplayPlugin {
Q_OBJECT
@ -33,19 +37,14 @@ protected:
using Condition = std::condition_variable;
using TextureEscrow = GLEscrow<gpu::TexturePointer>;
public:
OpenGLDisplayPlugin();
// These must be final to ensure proper ordering of operations
// between the main thread and the presentation thread
bool activate() override final;
void deactivate() override final;
bool eventFilter(QObject* receiver, QEvent* event) override;
bool isDisplayVisible() const override { return true; }
void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
void submitFrame(const gpu::FramePointer& newFrame) override;
glm::uvec2 getRecommendedRenderSize() const override {
return getSurfacePixels();
@ -64,17 +63,18 @@ public:
float droppedFrameRate() const override;
bool beginFrameRender(uint32_t frameIndex) override;
virtual bool wantVsync() const { return true; }
void setVsyncEnabled(bool vsyncEnabled) { _vsyncEnabled = vsyncEnabled; }
bool isVsyncEnabled() const { return _vsyncEnabled; }
protected:
#if THREADED_PRESENT
friend class PresentThread;
#endif
uint32_t getSceneTextureId() const;
uint32_t getOverlayTextureId() const;
glm::uvec2 getSurfaceSize() const;
glm::uvec2 getSurfacePixels() const;
void compositeLayers();
virtual void compositeLayers();
virtual void compositeScene();
virtual void compositeOverlay();
virtual void compositePointer();
@ -82,10 +82,6 @@ protected:
virtual bool hasFocus() const override;
// FIXME make thread safe?
virtual bool isVsyncEnabled();
virtual void enableVsync(bool enable = true);
// These functions must only be called on the presentation thread
virtual void customizeContext();
virtual void uncustomizeContext();
@ -93,54 +89,46 @@ protected:
// Returns true on successful activation
virtual bool internalActivate() { return true; }
virtual void internalDeactivate() {}
virtual void cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture);
// Plugin specific functionality to send the composed scene to the output window or device
virtual void internalPresent();
void withMainThreadContext(std::function<void()> f) const;
void useProgram(const ProgramPtr& program);
void present();
void updateTextures();
void drawUnitQuad();
void swapBuffers();
void eyeViewport(Eye eye) const;
virtual void updateFrameData();
QThread* _presentThread{ nullptr };
ProgramPtr _program;
int32_t _mvpUniform { -1 };
int32_t _alphaUniform { -1 };
ShapeWrapperPtr _plane;
void withMainThreadContext(std::function<void()> f) const;
void present();
virtual void swapBuffers();
ivec4 eyeViewport(Eye eye) const;
void render(std::function<void(gpu::Batch& batch)> f);
bool _vsyncEnabled { true };
QThread* _presentThread{ nullptr };
std::queue<gpu::FramePointer> _newFrameQueue;
RateCounter<> _droppedFrameRate;
RateCounter<> _newFrameRate;
RateCounter<> _presentRate;
QMap<gpu::TexturePointer, uint32_t> _sceneTextureToFrameIndexMap;
uint32_t _currentPresentFrameIndex { 0 };
float _compositeOverlayAlpha{ 1.0f };
gpu::TexturePointer _currentSceneTexture;
gpu::TexturePointer _currentOverlayTexture;
TextureEscrow _sceneTextureEscrow;
TextureEscrow _overlayTextureEscrow;
bool _vsyncSupported { false };
gpu::FramePointer _currentFrame;
gpu::FramebufferPointer _compositeFramebuffer;
gpu::PipelinePointer _overlayPipeline;
gpu::PipelinePointer _simplePipeline;
gpu::PipelinePointer _presentPipeline;
gpu::PipelinePointer _cursorPipeline;
float _compositeOverlayAlpha { 1.0f };
struct CursorData {
QImage image;
vec2 hotSpot;
uvec2 size;
uint32_t texture { 0 };
gpu::TexturePointer texture;
};
std::map<uint16_t, CursorData> _cursorsData;
BasicFramebufferWrapperPtr _compositeFramebuffer;
bool _lockCurrentTexture { false };
void assertIsRenderThread() const;
void assertNotPresentThread() const;
void assertIsPresentThread() const;
template<typename F>
@ -151,17 +139,17 @@ protected:
}
template<typename F>
void withRenderThreadLock(F f) const {
assertIsRenderThread();
void withNonPresentThreadLock(F f) const {
assertNotPresentThread();
Lock lock(_presentMutex);
f();
}
private:
gpu::gl::GLBackend* getGLBackend();
// Any resource shared by the main thread and the presentation thread must
// be serialized through this mutex
mutable Mutex _presentMutex;
ProgramPtr _activeProgram;
float _overlayAlpha{ 1.0f };
};

View file

@ -0,0 +1,90 @@
//
// Created by Bradley Austin Davis on 2016/07/31
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DebugHmdDisplayPlugin.h"
#include <QtCore/QProcessEnvironment>
#include <ViewFrustum.h>
#include <controllers/Pose.h>
#include <gpu/Frame.h>
const QString DebugHmdDisplayPlugin::NAME("HMD Simulator");
static const QString DEBUG_FLAG("HIFI_DEBUG_HMD");
static bool enableDebugHmd = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
bool DebugHmdDisplayPlugin::isSupported() const {
return enableDebugHmd;
}
void DebugHmdDisplayPlugin::resetSensors() {
_currentRenderFrameInfo.renderPose = glm::mat4(); // identity
}
bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
_currentRenderFrameInfo = FrameInfo();
_currentRenderFrameInfo.sensorSampleTime = secTimestampNow();
_currentRenderFrameInfo.predictedDisplayTime = _currentRenderFrameInfo.sensorSampleTime;
// FIXME simulate head movement
//_currentRenderFrameInfo.renderPose = ;
//_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose;
withNonPresentThreadLock([&] {
_uiModelTransform = DependencyManager::get<CompositorHelper>()->getModelTransform();
_frameInfos[frameIndex] = _currentRenderFrameInfo;
_handPoses[0] = glm::translate(mat4(), vec3(-0.3f, 0.0f, 0.0f));
_handLasers[0].color = vec4(1, 0, 0, 1);
_handLasers[0].mode = HandLaserMode::Overlay;
_handPoses[1] = glm::translate(mat4(), vec3(0.3f, 0.0f, 0.0f));
_handLasers[1].color = vec4(0, 1, 1, 1);
_handLasers[1].mode = HandLaserMode::Overlay;
});
return Parent::beginFrameRender(frameIndex);
}
// DLL based display plugins MUST initialize GLEW inside the DLL code.
void DebugHmdDisplayPlugin::customizeContext() {
glewExperimental = true;
glewInit();
glGetError(); // clear the potential error from glewExperimental
Parent::customizeContext();
}
bool DebugHmdDisplayPlugin::internalActivate() {
_ipd = 0.0327499993f * 2.0f;
_eyeProjections[0][0] = vec4{ 0.759056330, 0.000000000, 0.000000000, 0.000000000 };
_eyeProjections[0][1] = vec4{ 0.000000000, 0.682773232, 0.000000000, 0.000000000 };
_eyeProjections[0][2] = vec4{ -0.0580431037, -0.00619550655, -1.00000489, -1.00000000 };
_eyeProjections[0][3] = vec4{ 0.000000000, 0.000000000, -0.0800003856, 0.000000000 };
_eyeProjections[1][0] = vec4{ 0.752847493, 0.000000000, 0.000000000, 0.000000000 };
_eyeProjections[1][1] = vec4{ 0.000000000, 0.678060353, 0.000000000, 0.000000000 };
_eyeProjections[1][2] = vec4{ 0.0578232110, -0.00669418881, -1.00000489, -1.000000000 };
_eyeProjections[1][3] = vec4{ 0.000000000, 0.000000000, -0.0800003856, 0.000000000 };
_eyeInverseProjections[0] = glm::inverse(_eyeProjections[0]);
_eyeInverseProjections[1] = glm::inverse(_eyeProjections[1]);
_eyeOffsets[0][3] = vec4{ -0.0327499993, 0.0, 0.0149999997, 1.0 };
_eyeOffsets[0][3] = vec4{ 0.0327499993, 0.0, 0.0149999997, 1.0 };
_renderTargetSize = { 3024, 1680 };
_cullingProjection = _eyeProjections[0];
// This must come after the initialization, so that the values calculated
// above are available during the customizeContext call (when not running
// in threaded present mode)
return Parent::internalActivate();
}
void DebugHmdDisplayPlugin::updatePresentPose() {
float yaw = sinf(secTimestampNow()) * 0.25f;
float pitch = cosf(secTimestampNow()) * 0.25f;
// Simulates head pose latency correction
_currentPresentFrameInfo.presentPose =
glm::mat4_cast(glm::angleAxis(yaw, Vectors::UP)) *
glm::mat4_cast(glm::angleAxis(pitch, Vectors::RIGHT));
}

View file

@ -0,0 +1,33 @@
//
// Created by Bradley Austin Davis on 2016/07/31
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include "HmdDisplayPlugin.h"
class DebugHmdDisplayPlugin : public HmdDisplayPlugin {
using Parent = HmdDisplayPlugin;
public:
const QString& getName() const override { return NAME; }
grouping getGrouping() const override { return DEVELOPER; }
bool isSupported() const override;
void resetSensors() override final;
bool beginFrameRender(uint32_t frameIndex) override;
float getTargetFrameRate() const override { return 90; }
protected:
void updatePresentPose() override;
void hmdPresent() override {}
bool isHmdMounted() const override { return true; }
void customizeContext() override;
bool internalActivate() override;
private:
static const QString NAME;
};

View file

@ -1,10 +1,11 @@
//
//
// Created by Bradley Austin Davis on 2016/02/15
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "HmdDisplayPlugin.h"
#include <memory>
@ -22,9 +23,10 @@
#include <CursorManager.h>
#include <gl/GLWidget.h>
#include <shared/NsightHelpers.h>
#include <gpu/DrawUnitQuadTexcoord_vert.h>
#include <gpu/DrawTexture_frag.h>
#include <GeometryCache.h>
#include <gpu/Context.h>
#include <gpu/StandardShaderLib.h>
#include <gpu/gl/GLBackend.h>
#include <PathUtils.h>
@ -32,94 +34,18 @@
#include "../CompositorHelper.h"
static const QString MONO_PREVIEW = "Mono Preview";
static const QString REPROJECTION = "Allow Reprojection";
static const QString DISABLE_PREVIEW = "Disable Preview";
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
static const QString DEVELOPER_MENU_PATH = "Developer>" + DisplayPlugin::MENU_PATH();
static const bool DEFAULT_MONO_VIEW = true;
static const int NUMBER_OF_HANDS = 2;
static const glm::mat4 IDENTITY_MATRIX;
glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
}
QRect HmdDisplayPlugin::getRecommendedOverlayRect() const {
return CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT;
}
bool HmdDisplayPlugin::internalActivate() {
_monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW);
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), MONO_PREVIEW,
[this](bool clicked) {
_monoPreview = clicked;
_container->setBoolSetting("monoPreview", _monoPreview);
}, true, _monoPreview);
_container->removeMenu(FRAMERATE);
_container->addMenu(DEVELOPER_MENU_PATH);
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, DEVELOPER_MENU_PATH, REPROJECTION,
[this](bool clicked) {
_enableReprojection = clicked;
_container->setBoolSetting("enableReprojection", _enableReprojection);
}, true, _enableReprojection);
for_each_eye([&](Eye eye) {
_eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]);
});
if (_previewTextureID == 0) {
QImage previewTexture(PathUtils::resourcesPath() + "images/preview.png");
if (!previewTexture.isNull()) {
glGenTextures(1, &_previewTextureID);
glBindTexture(GL_TEXTURE_2D, _previewTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, previewTexture.width(), previewTexture.height(), 0,
GL_BGRA, GL_UNSIGNED_BYTE, previewTexture.mirrored(false, true).bits());
using namespace oglplus;
Texture::MinFilter(TextureTarget::_2D, TextureMinFilter::Linear);
Texture::MagFilter(TextureTarget::_2D, TextureMagFilter::Linear);
glBindTexture(GL_TEXTURE_2D, 0);
_previewAspect = ((float)previewTexture.width())/((float)previewTexture.height());
_firstPreview = true;
}
}
return Parent::internalActivate();
}
void HmdDisplayPlugin::internalDeactivate() {
if (_previewTextureID != 0) {
glDeleteTextures(1, &_previewTextureID);
_previewTextureID = 0;
}
Parent::internalDeactivate();
}
void HmdDisplayPlugin::customizeContext() {
Parent::customizeContext();
// Only enable mirroring if we know vsync is disabled
// On Mac, this won't work due to how the contexts are handled, so don't try
#if !defined(Q_OS_MAC)
enableVsync(false);
static const bool DEFAULT_DISABLE_PREVIEW = false;
#endif
_enablePreview = !isVsyncEnabled();
_sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO);
using namespace oglplus;
if (!_enablePreview) {
const std::string version("#version 410 core\n");
compileProgram(_previewProgram, version + DrawUnitQuadTexcoord_vert, version + DrawTexture_frag);
_previewUniforms.previewTexture = Uniform<int>(*_previewProgram, "colorMap").Location();
}
updateReprojectionProgram();
updateOverlayProgram();
#ifdef HMD_HAND_LASER_SUPPORT
updateLaserProgram();
_laserGeometry = loadLaser(_laserProgram);
#endif
}
static const glm::mat4 IDENTITY_MATRIX;
static const size_t NUMBER_OF_HANDS = 2;
//#define LIVE_SHADER_RELOAD 1
extern glm::vec3 getPoint(float yaw, float pitch);
static QString readFile(const QString& filename) {
QFile file(filename);
@ -129,183 +55,220 @@ static QString readFile(const QString& filename) {
return result;
}
void HmdDisplayPlugin::updateReprojectionProgram() {
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.vert";
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.frag";
#if LIVE_SHADER_RELOAD
static qint64 vsBuiltAge = 0;
static qint64 fsBuiltAge = 0;
QFileInfo vsInfo(vsFile);
QFileInfo fsInfo(fsFile);
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
if (!_reprojectionProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) {
vsBuiltAge = vsAge;
fsBuiltAge = fsAge;
#else
if (!_reprojectionProgram) {
#endif
QString vsSource = readFile(vsFile);
QString fsSource = readFile(fsFile);
ProgramPtr program;
try {
compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString());
if (program) {
using namespace oglplus;
_reprojectionUniforms.reprojectionMatrix = Uniform<glm::mat3>(*program, "reprojection").Location();
_reprojectionUniforms.inverseProjectionMatrix = Uniform<glm::mat4>(*program, "inverseProjections").Location();
_reprojectionUniforms.projectionMatrix = Uniform<glm::mat4>(*program, "projections").Location();
_reprojectionProgram = program;
}
} catch (std::runtime_error& error) {
qWarning() << "Error building reprojection shader " << error.what();
}
}
glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
}
#ifdef HMD_HAND_LASER_SUPPORT
void HmdDisplayPlugin::updateLaserProgram() {
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.vert";
static const QString gsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.geom";
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.frag";
#if LIVE_SHADER_RELOAD
static qint64 vsBuiltAge = 0;
static qint64 gsBuiltAge = 0;
static qint64 fsBuiltAge = 0;
QFileInfo vsInfo(vsFile);
QFileInfo fsInfo(fsFile);
QFileInfo gsInfo(fsFile);
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
auto gsAge = gsInfo.lastModified().toMSecsSinceEpoch();
if (!_laserProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge || gsAge > gsBuiltAge) {
vsBuiltAge = vsAge;
gsBuiltAge = gsAge;
fsBuiltAge = fsAge;
#else
if (!_laserProgram) {
#endif
QString vsSource = readFile(vsFile);
QString fsSource = readFile(fsFile);
QString gsSource = readFile(gsFile);
ProgramPtr program;
try {
compileProgram(program, vsSource.toLocal8Bit().toStdString(), gsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString());
if (program) {
using namespace oglplus;
_laserUniforms.color = Uniform<glm::vec4>(*program, "color").Location();
_laserUniforms.mvp = Uniform<glm::mat4>(*program, "mvp").Location();
_laserProgram = program;
}
} catch (std::runtime_error& error) {
qWarning() << "Error building hand laser composite shader " << error.what();
}
}
QRect HmdDisplayPlugin::getRecommendedOverlayRect() const {
return CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT;
}
#endif
void HmdDisplayPlugin::updateOverlayProgram() {
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag";
#if LIVE_SHADER_RELOAD
static qint64 vsBuiltAge = 0;
static qint64 fsBuiltAge = 0;
QFileInfo vsInfo(vsFile);
QFileInfo fsInfo(fsFile);
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
if (!_overlayProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) {
vsBuiltAge = vsAge;
fsBuiltAge = fsAge;
#else
if (!_overlayProgram) {
#endif
QString vsSource = readFile(vsFile);
QString fsSource = readFile(fsFile);
ProgramPtr program;
try {
compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString());
if (program) {
using namespace oglplus;
_overlayUniforms.mvp = Uniform<glm::mat4>(*program, "mvp").Location();
_overlayUniforms.alpha = Uniform<float>(*program, "alpha").Location();
_overlayUniforms.glowColors = Uniform<glm::vec4>(*program, "glowColors").Location();
_overlayUniforms.glowPoints = Uniform<glm::vec4>(*program, "glowPoints").Location();
_overlayUniforms.resolution = Uniform<glm::vec2>(*program, "resolution").Location();
_overlayUniforms.radius = Uniform<float>(*program, "radius").Location();
_overlayProgram = program;
useProgram(_overlayProgram);
Uniform<glm::vec2>(*_overlayProgram, _overlayUniforms.resolution).Set(CompositorHelper::VIRTUAL_SCREEN_SIZE);
}
} catch (std::runtime_error& error) {
qWarning() << "Error building overlay composite shader " << error.what();
}
bool HmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
if (!_vsyncEnabled && !_disablePreviewItemAdded) {
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), DISABLE_PREVIEW,
[this](bool clicked) {
_disablePreview = clicked;
_container->setBoolSetting("disableHmdPreview", _disablePreview);
if (_disablePreview) {
_clearPreviewFlag = true;
}
}, true, _disablePreview);
_disablePreviewItemAdded = true;
}
return Parent::beginFrameRender(frameIndex);
}
bool HmdDisplayPlugin::internalActivate() {
_disablePreviewItemAdded = false;
_monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW);
_clearPreviewFlag = true;
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), MONO_PREVIEW,
[this](bool clicked) {
_monoPreview = clicked;
_container->setBoolSetting("monoPreview", _monoPreview);
}, true, _monoPreview);
#if defined(Q_OS_MAC)
_disablePreview = true;
#else
_disablePreview = _container->getBoolSetting("disableHmdPreview", DEFAULT_DISABLE_PREVIEW || _vsyncEnabled);
#endif
_container->removeMenu(FRAMERATE);
for_each_eye([&](Eye eye) {
_eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]);
});
_clearPreviewFlag = true;
return Parent::internalActivate();
}
void HmdDisplayPlugin::internalDeactivate() {
Parent::internalDeactivate();
}
void HmdDisplayPlugin::customizeContext() {
Parent::customizeContext();
_overlayRenderer.build();
}
void HmdDisplayPlugin::uncustomizeContext() {
_overlayProgram.reset();
_sphereSection.reset();
_compositeFramebuffer.reset();
_previewProgram.reset();
_reprojectionProgram.reset();
#ifdef HMD_HAND_LASER_SUPPORT
_laserProgram.reset();
_laserGeometry.reset();
#endif
// This stops the weirdness where if the preview was disabled, on switching back to 2D,
// the vsync was stuck in the disabled state. No idea why that happens though.
_disablePreview = false;
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(_compositeFramebuffer);
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
});
internalPresent();
_overlayRenderer = OverlayRenderer();
Parent::uncustomizeContext();
}
// By default assume we'll present with the same pose as the render
ivec4 HmdDisplayPlugin::getViewportForSourceSize(const uvec2& size) const {
// screen preview mirroring
auto window = _container->getPrimaryWidget();
auto devicePixelRatio = window->devicePixelRatio();
auto windowSize = toGlm(window->size());
windowSize *= devicePixelRatio;
float windowAspect = aspect(windowSize);
float sceneAspect = aspect(size);
float aspectRatio = sceneAspect / windowAspect;
uvec2 targetViewportSize = windowSize;
if (aspectRatio < 1.0f) {
targetViewportSize.x *= aspectRatio;
} else {
targetViewportSize.y /= aspectRatio;
}
uvec2 targetViewportPosition;
if (targetViewportSize.x < windowSize.x) {
targetViewportPosition.x = (windowSize.x - targetViewportSize.x) / 2;
} else if (targetViewportSize.y < windowSize.y) {
targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2;
}
return ivec4(targetViewportPosition, targetViewportSize);
}
void HmdDisplayPlugin::internalPresent() {
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
// Composite together the scene, overlay and mouse cursor
hmdPresent();
if (!_disablePreview) {
// screen preview mirroring
auto sourceSize = _renderTargetSize;
if (_monoPreview) {
sourceSize.x >>= 1;
}
auto viewport = getViewportForSourceSize(sourceSize);
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(gpu::FramebufferPointer());
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
batch.setStateScissorRect(viewport);
if (_monoPreview) {
viewport.z *= 2;
}
batch.setViewportTransform(viewport);
batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0));
batch.setPipeline(_presentPipeline);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
swapBuffers();
} else if (_clearPreviewFlag) {
auto image = QImage(PathUtils::resourcesPath() + "images/preview.png");
image = image.mirrored();
image = image.convertToFormat(QImage::Format_RGBA8888);
if (!_previewTexture) {
_previewTexture.reset(
gpu::Texture::create2D(
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
image.width(), image.height(),
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
_previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build());
_previewTexture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits());
_previewTexture->autoGenerateMips(-1);
}
if (getGLBackend()->isTextureReady(_previewTexture)) {
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(gpu::FramebufferPointer());
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
batch.setStateScissorRect(viewport);
batch.setViewportTransform(viewport);
batch.setResourceTexture(0, _previewTexture);
batch.setPipeline(_presentPipeline);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
_clearPreviewFlag = false;
swapBuffers();
}
}
postPreview();
}
// HMD specific stuff
glm::mat4 HmdDisplayPlugin::getHeadPose() const {
return _currentRenderFrameInfo.renderPose;
}
void HmdDisplayPlugin::updatePresentPose() {
// By default assume we'll present with the same pose as the render
_currentPresentFrameInfo.presentPose = _currentPresentFrameInfo.renderPose;
}
void HmdDisplayPlugin::compositeScene() {
updatePresentPose();
void HmdDisplayPlugin::updateFrameData() {
// Check if we have old frame data to discard
static const uint32_t INVALID_FRAME = (uint32_t)(~0);
uint32_t oldFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME;
if (!_enableReprojection || glm::mat3() == _currentPresentFrameInfo.presentReprojection) {
// No reprojection required
Parent::compositeScene();
return;
Parent::updateFrameData();
uint32_t newFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME;
if (oldFrameIndex != newFrameIndex) {
withPresentThreadLock([&] {
if (oldFrameIndex != INVALID_FRAME) {
auto itr = _frameInfos.find(oldFrameIndex);
if (itr != _frameInfos.end()) {
_frameInfos.erase(itr);
}
}
if (newFrameIndex != INVALID_FRAME) {
_currentPresentFrameInfo = _frameInfos[newFrameIndex];
}
});
}
#ifdef DEBUG_REPROJECTION_SHADER
_reprojectionProgram = getReprojectionProgram();
#endif
useProgram(_reprojectionProgram);
updatePresentPose();
using namespace oglplus;
Texture::MinFilter(TextureTarget::_2D, TextureMinFilter::Linear);
Texture::MagFilter(TextureTarget::_2D, TextureMagFilter::Linear);
Uniform<glm::mat3>(*_reprojectionProgram, _reprojectionUniforms.reprojectionMatrix).Set(_currentPresentFrameInfo.presentReprojection);
//Uniform<glm::mat4>(*_reprojectionProgram, PROJECTION_MATRIX_LOCATION).Set(_eyeProjections);
//Uniform<glm::mat4>(*_reprojectionProgram, INVERSE_PROJECTION_MATRIX_LOCATION).Set(_eyeInverseProjections);
// FIXME what's the right oglplus mechanism to do this? It's not that ^^^ ... better yet, switch to a uniform buffer
glUniformMatrix4fv(_reprojectionUniforms.inverseProjectionMatrix, 2, GL_FALSE, &(_eyeInverseProjections[0][0][0]));
glUniformMatrix4fv(_reprojectionUniforms.projectionMatrix, 2, GL_FALSE, &(_eyeProjections[0][0][0]));
_plane->UseInProgram(*_reprojectionProgram);
_plane->Draw();
}
void HmdDisplayPlugin::compositeOverlay() {
using namespace oglplus;
auto compositorHelper = DependencyManager::get<CompositorHelper>();
glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix();
if (_currentFrame) {
auto batchPose = _currentFrame->pose;
auto currentPose = _currentPresentFrameInfo.presentPose;
auto correction = glm::inverse(batchPose) * currentPose;
getGLBackend()->setCameraCorrection(correction);
}
withPresentThreadLock([&] {
_presentHandLasers = _handLasers;
_presentHandPoses = _handPoses;
_presentUiModelTransform = _uiModelTransform;
});
std::array<vec2, NUMBER_OF_HANDS> handGlowPoints { { vec2(-1), vec2(-1) } };
auto compositorHelper = DependencyManager::get<CompositorHelper>();
glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix();
std::array<vec2, NUMBER_OF_HANDS> handGlowPoints{ { vec2(-1), vec2(-1) } };
// compute the glow point interesections
for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
for (size_t i = 0; i < NUMBER_OF_HANDS; ++i) {
if (_presentHandPoses[i] == IDENTITY_MATRIX) {
continue;
}
@ -331,6 +294,9 @@ void HmdDisplayPlugin::compositeOverlay() {
continue;
}
_presentHandLaserPoints[i].first = vec3(_presentHandPoses[i][3]);
_presentHandLaserPoints[i].second = _presentHandLaserPoints[i].first + (castDirection * distance);
vec3 intersectionPosition = vec3(_presentHandPoses[i][3]) + (castDirection * distance) - _presentUiModelTransform.getTranslation();
intersectionPosition = glm::inverse(_presentUiModelTransform.getRotation()) * intersectionPosition;
@ -353,154 +319,174 @@ void HmdDisplayPlugin::compositeOverlay() {
handGlowPoints[i] = yawPitch;
}
updateOverlayProgram();
if (!_overlayProgram) {
return;
}
useProgram(_overlayProgram);
for_each_eye([&](Eye eye) {
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat;
_overlayRenderer.mvps[eye] = _eyeProjections[eye] * modelView;
});
// Setup the uniforms
{
if (_overlayUniforms.alpha >= 0) {
Uniform<float>(*_overlayProgram, _overlayUniforms.alpha).Set(_compositeOverlayAlpha);
}
if (_overlayUniforms.glowPoints >= 0) {
vec4 glowPoints(handGlowPoints[0], handGlowPoints[1]);
Uniform<glm::vec4>(*_overlayProgram, _overlayUniforms.glowPoints).Set(glowPoints);
}
if (_overlayUniforms.glowColors >= 0) {
std::array<glm::vec4, NUMBER_OF_HANDS> glowColors;
glowColors[0] = _presentHandLasers[0].color;
glowColors[1] = _presentHandLasers[1].color;
glProgramUniform4fv(GetName(*_overlayProgram), _overlayUniforms.glowColors, 2, &glowColors[0].r);
auto& uniforms = _overlayRenderer.uniforms;
uniforms.alpha = _compositeOverlayAlpha;
uniforms.glowPoints = vec4(handGlowPoints[0], handGlowPoints[1]);
uniforms.glowColors[0] = _presentHandLasers[0].color;
uniforms.glowColors[1] = _presentHandLasers[1].color;
}
}
void HmdDisplayPlugin::OverlayRenderer::build() {
vertices = std::make_shared<gpu::Buffer>();
indices = std::make_shared<gpu::Buffer>();
//UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
static const float fov = CompositorHelper::VIRTUAL_UI_TARGET_FOV.y;
static const float aspectRatio = CompositorHelper::VIRTUAL_UI_ASPECT_RATIO;
static const uint16_t stacks = 128;
static const uint16_t slices = 64;
Vertex vertex;
// Compute vertices positions and texture UV coordinate
// Create and write to buffer
for (int i = 0; i < stacks; i++) {
vertex.uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
// abs(theta) <= fov / 2.0f
float pitch = -fov * (vertex.uv.y - 0.5f);
for (int j = 0; j < slices; j++) {
vertex.uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
// abs(phi) <= fov * aspectRatio / 2.0f
float yaw = -fov * aspectRatio * (vertex.uv.x - 0.5f);
vertex.pos = getPoint(yaw, pitch);
vertices->append(sizeof(Vertex), (gpu::Byte*)&vertex);
}
}
_sphereSection->Use();
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat;
auto mvp = _eyeProjections[eye] * modelView;
Uniform<glm::mat4>(*_overlayProgram, _overlayUniforms.mvp).Set(mvp);
_sphereSection->Draw();
// Compute number of indices needed
static const int VERTEX_PER_TRANGLE = 3;
static const int TRIANGLE_PER_RECTANGLE = 2;
int numberOfRectangles = (slices - 1) * (stacks - 1);
indexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
// Compute indices order
std::vector<GLushort> indices;
for (int i = 0; i < stacks - 1; i++) {
for (int j = 0; j < slices - 1; j++) {
GLushort bottomLeftIndex = i * slices + j;
GLushort bottomRightIndex = bottomLeftIndex + 1;
GLushort topLeftIndex = bottomLeftIndex + slices;
GLushort topRightIndex = topLeftIndex + 1;
// FIXME make a z-order curve for better vertex cache locality
indices.push_back(topLeftIndex);
indices.push_back(bottomLeftIndex);
indices.push_back(topRightIndex);
indices.push_back(topRightIndex);
indices.push_back(bottomLeftIndex);
indices.push_back(bottomRightIndex);
}
}
this->indices->append(indices);
format = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
format->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
format->setAttribute(gpu::Stream::TEXCOORD, gpu::Stream::TEXCOORD, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
uniformBuffers[0] = std::make_shared<gpu::Buffer>(sizeof(Uniforms), nullptr);
uniformBuffers[1] = std::make_shared<gpu::Buffer>(sizeof(Uniforms), nullptr);
updatePipeline();
}
void HmdDisplayPlugin::OverlayRenderer::updatePipeline() {
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag";
#if LIVE_SHADER_RELOAD
static qint64 vsBuiltAge = 0;
static qint64 fsBuiltAge = 0;
QFileInfo vsInfo(vsFile);
QFileInfo fsInfo(fsFile);
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
if (!pipeline || vsAge > vsBuiltAge || fsAge > fsBuiltAge) {
vsBuiltAge = vsAge;
fsBuiltAge = fsAge;
#else
if (!pipeline) {
#endif
QString vsSource = readFile(vsFile);
QString fsSource = readFile(fsFile);
auto vs = gpu::Shader::createVertex(vsSource.toLocal8Bit().toStdString());
auto ps = gpu::Shader::createPixel(fsSource.toLocal8Bit().toStdString());
auto program = gpu::Shader::createProgram(vs, ps);
gpu::gl::GLBackend::makeProgram(*program, gpu::Shader::BindingSet());
this->uniformsLocation = program->getBuffers().findLocation("overlayBuffer");
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
pipeline = gpu::Pipeline::create(program, state);
}
}
void HmdDisplayPlugin::OverlayRenderer::render(HmdDisplayPlugin& plugin) {
updatePipeline();
for_each_eye([&](Eye eye){
uniforms.mvp = mvps[eye];
uniformBuffers[eye]->setSubData(0, uniforms);
});
plugin.render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setFramebuffer(plugin._compositeFramebuffer);
batch.setPipeline(pipeline);
batch.setInputFormat(format);
gpu::BufferView posView(vertices, VERTEX_OFFSET, vertices->getSize(), VERTEX_STRIDE, format->getAttributes().at(gpu::Stream::POSITION)._element);
gpu::BufferView uvView(vertices, TEXTURE_OFFSET, vertices->getSize(), VERTEX_STRIDE, format->getAttributes().at(gpu::Stream::TEXCOORD)._element);
batch.setInputBuffer(gpu::Stream::POSITION, posView);
batch.setInputBuffer(gpu::Stream::TEXCOORD, uvView);
batch.setIndexBuffer(gpu::UINT16, indices, 0);
batch.setResourceTexture(0, plugin._currentFrame->overlay);
// FIXME use stereo information input to set both MVPs in the uniforms
for_each_eye([&](Eye eye) {
batch.setUniformBuffer(uniformsLocation, uniformBuffers[eye]);
batch.setViewportTransform(plugin.eyeViewport(eye));
batch.drawIndexed(gpu::TRIANGLES, indexCount);
});
});
}
void HmdDisplayPlugin::compositePointer() {
using namespace oglplus;
auto& cursorManager = Cursor::Manager::instance();
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
auto compositorHelper = DependencyManager::get<CompositorHelper>();
useProgram(_program);
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
// Mouse pointer
_plane->Use();
// Reconstruct the headpose from the eye poses
auto headPosition = vec3(_currentPresentFrameInfo.presentPose[3]);
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition);
auto mvp = _eyeProjections[eye] * reticleTransform;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_plane->Draw();
});
// restore the alpha
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void HmdDisplayPlugin::internalPresent() {
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
// Composite together the scene, overlay and mouse cursor
hmdPresent();
// screen preview mirroring
auto window = _container->getPrimaryWidget();
auto devicePixelRatio = window->devicePixelRatio();
auto windowSize = toGlm(window->size());
windowSize *= devicePixelRatio;
float windowAspect = aspect(windowSize);
float sceneAspect = _enablePreview ? aspect(_renderTargetSize) : _previewAspect;
if (_enablePreview && _monoPreview) {
sceneAspect /= 2.0f;
}
float aspectRatio = sceneAspect / windowAspect;
uvec2 targetViewportSize = windowSize;
if (aspectRatio < 1.0f) {
targetViewportSize.x *= aspectRatio;
} else {
targetViewportSize.y /= aspectRatio;
}
uvec2 targetViewportPosition;
if (targetViewportSize.x < windowSize.x) {
targetViewportPosition.x = (windowSize.x - targetViewportSize.x) / 2;
} else if (targetViewportSize.y < windowSize.y) {
targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2;
}
if (_enablePreview) {
using namespace oglplus;
Context::Clear().ColorBuffer();
auto sourceSize = _compositeFramebuffer->size;
if (_monoPreview) {
sourceSize.x /= 2;
}
_compositeFramebuffer->Bound(Framebuffer::Target::Read, [&] {
Context::BlitFramebuffer(
0, 0, sourceSize.x, sourceSize.y,
targetViewportPosition.x, targetViewportPosition.y,
targetViewportPosition.x + targetViewportSize.x, targetViewportPosition.y + targetViewportSize.y,
BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
render([&](gpu::Batch& batch) {
// FIXME use standard gpu stereo rendering for this.
batch.enableStereo(false);
batch.setFramebuffer(_compositeFramebuffer);
batch.setPipeline(_cursorPipeline);
batch.setResourceTexture(0, cursorData.texture);
batch.resetViewTransform();
for_each_eye([&](Eye eye) {
batch.setViewportTransform(eyeViewport(eye));
batch.setProjectionTransform(_eyeProjections[eye]);
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition);
batch.setModelTransform(reticleTransform);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
swapBuffers();
} else if (_firstPreview || windowSize != _prevWindowSize || devicePixelRatio != _prevDevicePixelRatio) {
useProgram(_previewProgram);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(targetViewportPosition.x, targetViewportPosition.y, targetViewportSize.x, targetViewportSize.y);
glUniform1i(_previewUniforms.previewTexture, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _previewTextureID);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
swapBuffers();
_firstPreview = false;
_prevWindowSize = windowSize;
_prevDevicePixelRatio = devicePixelRatio;
});
}
void HmdDisplayPlugin::compositeOverlay() {
if (!_currentFrame || !_currentFrame->overlay) {
return;
}
postPreview();
}
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
}
void HmdDisplayPlugin::updateFrameData() {
// Check if we have old frame data to discard
withPresentThreadLock([&] {
auto itr = _frameInfos.find(_currentPresentFrameIndex);
if (itr != _frameInfos.end()) {
_frameInfos.erase(itr);
}
});
Parent::updateFrameData();
withPresentThreadLock([&] {
_currentPresentFrameInfo = _frameInfos[_currentPresentFrameIndex];
});
}
glm::mat4 HmdDisplayPlugin::getHeadPose() const {
return _currentRenderFrameInfo.renderPose;
_overlayRenderer.render(*this);
}
bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const vec4& color, const vec3& direction) {
@ -508,7 +494,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
info.mode = mode;
info.color = color;
info.direction = direction;
withRenderThreadLock([&] {
withNonPresentThreadLock([&] {
if (hands & Hand::LeftHand) {
_handLasers[0] = info;
}
@ -522,7 +508,6 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
}
void HmdDisplayPlugin::compositeExtra() {
#ifdef HMD_HAND_LASER_SUPPORT
// If neither hand laser is activated, exit
if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) {
return;
@ -532,64 +517,20 @@ void HmdDisplayPlugin::compositeExtra() {
return;
}
updateLaserProgram();
// Render hand lasers
using namespace oglplus;
useProgram(_laserProgram);
_laserGeometry->Use();
std::array<mat4, NUMBER_OF_HANDS> handLaserModelMatrices;
for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
if (_presentHandPoses[i] == IDENTITY_MATRIX) {
continue;
}
const auto& handLaser = _presentHandLasers[i];
if (!handLaser.valid()) {
continue;
}
const auto& laserDirection = handLaser.direction;
auto model = _presentHandPoses[i];
auto castDirection = glm::quat_cast(model) * laserDirection;
if (glm::abs(glm::length2(castDirection) - 1.0f) > EPSILON) {
castDirection = glm::normalize(castDirection);
}
// FIXME fetch the actual UI radius from... somewhere?
float uiRadius = 1.0f;
// Find the intersection of the laser with he UI and use it to scale the model matrix
float distance;
if (!glm::intersectRaySphere(vec3(_presentHandPoses[i][3]), castDirection, _presentUiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) {
continue;
}
// Make sure we rotate to match the desired laser direction
if (laserDirection != Vectors::UNIT_NEG_Z) {
auto rotation = glm::rotation(Vectors::UNIT_NEG_Z, laserDirection);
model = model * glm::mat4_cast(rotation);
}
model = glm::scale(model, vec3(distance));
handLaserModelMatrices[i] = model;
}
glEnable(GL_BLEND);
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
auto view = glm::inverse(eyePose);
const auto& projection = _eyeProjections[eye];
for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
if (handLaserModelMatrices[i] == IDENTITY_MATRIX) {
continue;
auto geometryCache = DependencyManager::get<GeometryCache>();
render([&](gpu::Batch& batch) {
batch.setFramebuffer(_compositeFramebuffer);
batch.setViewportTransform(ivec4(uvec2(0), _renderTargetSize));
batch.setViewTransform(_currentPresentFrameInfo.presentPose, false);
bilateral::for_each_side([&](bilateral::Side side){
auto index = bilateral::index(side);
if (_presentHandPoses[index] == IDENTITY_MATRIX) {
return;
}
Uniform<glm::mat4>(*_laserProgram, "mvp").Set(projection * view * handLaserModelMatrices[i]);
Uniform<glm::vec4>(*_laserProgram, "color").Set(_presentHandLasers[i].color);
_laserGeometry->Draw();
}
const auto& points = _presentHandLaserPoints[index];
const auto& lasers = _presentHandLasers[index];
geometryCache->renderGlowLine(batch, points.first, points.second, lasers.color);
});
});
glDisable(GL_BLEND);
#endif
}

View file

@ -9,14 +9,16 @@
#include <ThreadSafeValueCache.h>
#include <array>
#include <QtGlobal>
#include <Transform.h>
#include "../OpenGLDisplayPlugin.h"
#include <gpu/Format.h>
#include <gpu/Stream.h>
#ifdef Q_OS_WIN
#define HMD_HAND_LASER_SUPPORT
#endif
#include "../CompositorHelper.h"
#include "../OpenGLDisplayPlugin.h"
class HmdDisplayPlugin : public OpenGLDisplayPlugin {
using Parent = OpenGLDisplayPlugin;
@ -28,7 +30,6 @@ public:
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; }
glm::uvec2 getRecommendedUiSize() const override final;
glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; }
void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
bool isDisplayVisible() const override { return isHmdMounted(); }
QRect getRecommendedOverlayRect() const override final;
@ -37,15 +38,19 @@ public:
bool setHandLaser(uint32_t hands, HandLaserMode mode, const vec4& color, const vec3& direction) override;
bool wantVsync() const override {
return false;
}
protected:
virtual void hmdPresent() = 0;
virtual bool isHmdMounted() const = 0;
virtual void postPreview() {};
virtual void updatePresentPose();
bool beginFrameRender(uint32_t frameIndex) override;
bool internalActivate() override;
void internalDeactivate() override;
void compositeScene() override;
void compositeOverlay() override;
void compositePointer() override;
void internalPresent() override;
@ -67,82 +72,74 @@ protected:
Transform _uiModelTransform;
std::array<HandLaserInfo, 2> _handLasers;
std::array<glm::mat4, 2> _handPoses;
std::array<mat4, 2> _handPoses;
Transform _presentUiModelTransform;
std::array<HandLaserInfo, 2> _presentHandLasers;
std::array<mat4, 2> _presentHandPoses;
std::array<std::pair<vec3, vec3>, 2> _presentHandLaserPoints;
std::array<glm::mat4, 2> _eyeOffsets;
std::array<glm::mat4, 2> _eyeProjections;
std::array<glm::mat4, 2> _eyeInverseProjections;
std::array<mat4, 2> _eyeOffsets;
std::array<mat4, 2> _eyeProjections;
std::array<mat4, 2> _eyeInverseProjections;
glm::mat4 _cullingProjection;
glm::uvec2 _renderTargetSize;
mat4 _cullingProjection;
uvec2 _renderTargetSize;
float _ipd { 0.064f };
struct FrameInfo {
glm::mat4 rawRenderPose;
glm::mat4 renderPose;
glm::mat4 rawPresentPose;
glm::mat4 presentPose;
mat4 renderPose;
mat4 presentPose;
double sensorSampleTime { 0 };
double predictedDisplayTime { 0 };
glm::mat3 presentReprojection;
mat3 presentReprojection;
};
QMap<uint32_t, FrameInfo> _frameInfos;
FrameInfo _currentPresentFrameInfo;
FrameInfo _currentRenderFrameInfo;
bool _disablePreview{ true };
private:
void updateOverlayProgram();
#ifdef HMD_HAND_LASER_SUPPORT
void updateLaserProgram();
#endif
void updateReprojectionProgram();
ivec4 getViewportForSourceSize(const uvec2& size) const;
bool _enablePreview { false };
bool _disablePreviewItemAdded { false };
bool _monoPreview { true };
bool _enableReprojection { true };
bool _firstPreview { true };
bool _clearPreviewFlag { false };
gpu::TexturePointer _previewTexture;
ProgramPtr _overlayProgram;
struct OverlayUniforms {
int32_t mvp { -1 };
int32_t alpha { -1 };
int32_t glowColors { -1 };
int32_t glowPoints { -1 };
int32_t resolution { -1 };
int32_t radius { -1 };
} _overlayUniforms;
struct OverlayRenderer {
gpu::Stream::FormatPointer format;
gpu::BufferPointer vertices;
gpu::BufferPointer indices;
uint32_t indexCount { 0 };
gpu::PipelinePointer pipeline;
int32_t uniformsLocation { -1 };
ProgramPtr _previewProgram;
struct PreviewUniforms {
int32_t previewTexture { -1 };
} _previewUniforms;
// FIXME this is stupid, use the built in transformation pipeline
std::array<gpu::BufferPointer, 2> uniformBuffers;
std::array<mat4, 2> mvps;
float _previewAspect { 0 };
GLuint _previewTextureID { 0 };
glm::uvec2 _prevWindowSize { 0, 0 };
qreal _prevDevicePixelRatio { 0 };
struct Uniforms {
mat4 mvp;
vec4 glowPoints { -1 };
vec4 glowColors[2];
vec2 resolution { CompositorHelper::VIRTUAL_SCREEN_SIZE };
float radius { 0.005f };
float alpha { 1.0f };
} uniforms;
struct Vertex {
vec3 pos;
vec2 uv;
} vertex;
ProgramPtr _reprojectionProgram;
struct ReprojectionUniforms {
int32_t reprojectionMatrix { -1 };
int32_t inverseProjectionMatrix { -1 };
int32_t projectionMatrix { -1 };
} _reprojectionUniforms;
static const size_t VERTEX_OFFSET { offsetof(Vertex, pos) };
static const size_t TEXTURE_OFFSET { offsetof(Vertex, uv) };
static const int VERTEX_STRIDE { sizeof(Vertex) };
ShapeWrapperPtr _sphereSection;
#ifdef HMD_HAND_LASER_SUPPORT
ProgramPtr _laserProgram;
struct LaserUniforms {
int32_t mvp { -1 };
int32_t color { -1 };
} _laserUniforms;
ShapeWrapperPtr _laserGeometry;
#endif
void build();
void updatePipeline();
void render(HmdDisplayPlugin& plugin);
} _overlayRenderer;
};

View file

@ -8,52 +8,56 @@
#include "InterleavedStereoDisplayPlugin.h"
static const char * INTERLEAVED_TEXTURED_VS = R"VS(#version 410 core
#pragma line __LINE__
#include <gpu/StandardShaderLib.h>
#include <gpu/Pipeline.h>
#include <gpu/Batch.h>
#include <gpu/Context.h>
in vec3 Position;
in vec2 TexCoord;
static const char* INTERLEAVED_SRGB_TO_LINEAR_FRAG = R"SCRIBE(
out vec2 vTexCoord;
struct TextureData {
ivec2 textureSize;
};
void main() {
gl_Position = vec4(Position, 1);
vTexCoord = TexCoord;
}
layout(std140) uniform textureDataBuffer {
TextureData textureData;
};
)VS";
uniform sampler2D colorMap;
static const char * INTERLEAVED_TEXTURED_FS = R"FS(#version 410 core
#pragma line __LINE__
in vec2 varTexCoord0;
uniform sampler2D sampler;
uniform ivec2 textureSize;
out vec4 outFragColor;
in vec2 vTexCoord;
out vec4 FragColor;
void main() {
ivec2 texCoord = ivec2(floor(vTexCoord * textureSize));
void main(void) {
ivec2 texCoord = ivec2(floor(varTexCoord0 * textureData.textureSize));
texCoord.x /= 2;
int row = int(floor(gl_FragCoord.y));
if (row % 2 > 0) {
texCoord.x += (textureSize.x / 2);
texCoord.x += (textureData.textureSize.x / 2);
}
FragColor = texelFetch(sampler, texCoord, 0); //texture(sampler, texCoord);
outFragColor = vec4(pow(texelFetch(colorMap, texCoord, 0).rgb, vec3(2.2)), 1.0);
}
)FS";
)SCRIBE";
const QString InterleavedStereoDisplayPlugin::NAME("3D TV - Interleaved");
void InterleavedStereoDisplayPlugin::customizeContext() {
StereoDisplayPlugin::customizeContext();
// Set up the stencil buffers? Or use a custom shader?
compileProgram(_interleavedProgram, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS);
if (!_interleavedPresentPipeline) {
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(INTERLEAVED_SRGB_TO_LINEAR_FRAG));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
_interleavedPresentPipeline = gpu::Pipeline::create(program, state);
}
}
void InterleavedStereoDisplayPlugin::uncustomizeContext() {
_interleavedProgram.reset();
_interleavedPresentPipeline.reset();
StereoDisplayPlugin::uncustomizeContext();
}
@ -65,15 +69,14 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const {
}
void InterleavedStereoDisplayPlugin::internalPresent() {
using namespace oglplus;
auto sceneSize = getRecommendedRenderSize();
_interleavedProgram->Bind();
Uniform<ivec2>(*_interleavedProgram, "textureSize").SetValue(sceneSize);
auto surfaceSize = getSurfacePixels();
Context::Viewport(0, 0, surfaceSize.x, surfaceSize.y);
glBindTexture(GL_TEXTURE_2D, GetName(_compositeFramebuffer->color));
_plane->Use();
_plane->Draw();
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(gpu::FramebufferPointer());
batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
batch.setPipeline(_interleavedPresentPipeline);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
swapBuffers();
}

View file

@ -24,6 +24,7 @@ protected:
void internalPresent() override;
private:
ProgramPtr _interleavedProgram;
static const QString NAME;
gpu::PipelinePointer _interleavedPresentPipeline;
gpu::BufferPointer _textureDataBuffer;
};

View file

@ -7,11 +7,6 @@
//
#include "SideBySideStereoDisplayPlugin.h"
#include <GLMHelpers.h>
#include <CursorManager.h>
#include <ui-plugins/PluginContainer.h>
#include <gl/GLWidget.h>
#include "../CompositorHelper.h"
const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo");

View file

@ -101,4 +101,3 @@ void StereoDisplayPlugin::internalDeactivate() {
float StereoDisplayPlugin::getRecommendedAspectRatio() const {
return aspect(Parent::getRecommendedRenderSize());
}

View file

@ -0,0 +1,75 @@
//
// Created by Bradley Austin Davis on 2016/07/11
// Copyright 2013-2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
uniform sampler2D sampler;
struct OverlayData {
mat4 mvp;
vec4 glowPoints;
vec4 glowColors[2];
vec4 resolutionRadiusAlpha;
};
layout(std140) uniform overlayBuffer {
OverlayData overlay;
};
vec2 resolution = overlay.resolutionRadiusAlpha.xy;
float radius = overlay.resolutionRadiusAlpha.z;
float alpha = overlay.resolutionRadiusAlpha.w;
vec4 glowPoints = overlay.glowPoints;
vec4 glowColors[2] = overlay.glowColors;
in vec3 vPosition;
in vec2 vTexCoord;
out vec4 FragColor;
float easeInOutCubic(float f) {
const float d = 1.0;
const float b = 0.0;
const float c = 1.0;
float t = f;
if ((t /= d / 2.0) < 1.0) return c / 2.0 * t * t * t + b;
return c / 2.0 * ((t -= 2.0) * t * t + 2.0) + b;
}
void main() {
FragColor = texture(sampler, vTexCoord);
vec2 aspect = resolution;
aspect /= resolution.x;
float glowIntensity = 0.0;
float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect);
float dist2 = distance(vTexCoord * aspect, glowPoints.zw * aspect);
float dist = min(dist1, dist2);
vec3 glowColor = glowColors[0].rgb;
if (dist2 < dist1) {
glowColor = glowColors[1].rgb;
}
if (dist <= radius) {
glowIntensity = 1.0 - (dist / radius);
glowColor.rgb = pow(glowColor, vec3(1.0 - glowIntensity));
glowIntensity = easeInOutCubic(glowIntensity);
glowIntensity = pow(glowIntensity, 0.5);
}
if (alpha <= 0.0) {
if (glowIntensity <= 0.0) {
discard;
}
FragColor = vec4(glowColor, glowIntensity);
return;
}
FragColor.rgb = mix(FragColor.rgb, glowColor.rgb, glowIntensity);
FragColor.a *= alpha;
}

View file

@ -0,0 +1,32 @@
//
// Created by Bradley Austin Davis on 2016/07/11
// Copyright 2013-2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
struct OverlayData {
mat4 mvp;
vec4 glowPoints;
vec4 glowColors[2];
vec4 resolutionRadiusAlpha;
};
layout(std140) uniform overlayBuffer {
OverlayData overlay;
};
mat4 mvp = overlay.mvp;
layout(location = 0) in vec3 Position;
layout(location = 3) in vec2 TexCoord;
out vec3 vPosition;
out vec2 vTexCoord;
void main() {
gl_Position = mvp * vec4(Position, 1);
vTexCoord = TexCoord;
vPosition = Position;
}

View file

@ -57,7 +57,10 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, RenderableWebEntityItem::factory)
// Offscreen web surfaces are incompatible with nSight
if (!nsightActive()) {
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, RenderableWebEntityItem::factory)
}
REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Zone, RenderableZoneEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Line, RenderableLineEntityItem::factory)
@ -159,7 +162,9 @@ void EntityTreeRenderer::init() {
}
void EntityTreeRenderer::shutdown() {
_entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential
if (_entitiesScriptEngine) {
_entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential
}
_shuttingDown = true;
clear(); // always clear() on shutdown

View file

@ -20,6 +20,8 @@
#include <SharedUtil.h>
#include <NumericalConstants.h>
#include "Config.h"
// The GLEscrow class provides a simple mechanism for producer GL contexts to provide
// content to a consumer where the consumer is assumed to be connected to a display and
// therefore must never be blocked.

View file

@ -2,18 +2,16 @@
#include <mutex>
#include <QtGui/QSurfaceFormat>
#include <QtOpenGL/QGL>
#include <QOpenGLContext>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QRegularExpression>
#include <QtCore/QProcessEnvironment>
#ifdef DEBUG
static bool enableDebug = true;
#else
static const QString DEBUG_FLAG("HIFI_ENABLE_OPENGL_45");
static bool enableDebug = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
#endif
#include <QtGui/QSurfaceFormat>
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLDebugLogger>
#include <QtOpenGL/QGL>
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
static QSurfaceFormat format;
@ -23,7 +21,8 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS);
format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS);
setGLFormatVersion(format);
if (enableDebug) {
if (GLDebug::enabled()) {
qDebug() << "Enabling debug context";
format.setOption(QSurfaceFormat::DebugContext);
}
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
@ -72,3 +71,36 @@ QJsonObject getGLContextData() {
{ "renderer", glRenderer },
};
}
QThread* RENDER_THREAD = nullptr;
bool isRenderThread() {
return QThread::currentThread() == RENDER_THREAD;
}
#ifdef DEBUG
static bool enableDebugLogger = true;
#else
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL");
static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
#endif
bool GLDebug::enabled() {
return enableDebugLogger;
}
void GLDebug::log(const QOpenGLDebugMessage & debugMessage) {
qDebug() << debugMessage;
}
void GLDebug::setupLogger(QObject* window) {
if (enabled()) {
QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(window);
logger->initialize(); // initializes in the current context, i.e. ctx
logger->enableMessages();
QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, window, [&](const QOpenGLDebugMessage & debugMessage) {
GLDebug::log(debugMessage);
});
}
}

View file

@ -18,6 +18,8 @@
// but GL implementations usually just come with buffer sizes in multiples of 8)
#define DEFAULT_GL_STENCIL_BUFFER_BITS 8
class QObject;
class QOpenGLDebugMessage;
class QSurfaceFormat;
class QGLFormat;
@ -29,4 +31,15 @@ const QGLFormat& getDefaultGLFormat();
QJsonObject getGLContextData();
int glVersionToInteger(QString glVersion);
bool isRenderThread();
class GLDebug {
public:
static bool enabled();
static void log(const QOpenGLDebugMessage& debugMessage);
static void setupLogger(QObject* window = nullptr);
};
#endif

View file

@ -8,8 +8,8 @@
#include "GLWindow.h"
#include <QtCore/QDebug>
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLDebugLogger>
#include "GLHelpers.h"

View file

@ -13,30 +13,16 @@
#include "OffscreenGLCanvas.h"
#include <QtCore/QProcessEnvironment>
#include <QtCore/QDebug>
#include <QtGui/QOffscreenSurface>
#include <QtGui/QOpenGLDebugLogger>
#include <QtGui/QOpenGLContext>
#include "GLHelpers.h"
#ifdef DEBUG
static bool enableDebugLogger = true;
#else
static const QString DEBUG_FLAG("HIFI_ENABLE_OPENGL_45");
static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
#endif
OffscreenGLCanvas::OffscreenGLCanvas() : _context(new QOpenGLContext), _offscreenSurface(new QOffscreenSurface){
}
OffscreenGLCanvas::~OffscreenGLCanvas() {
if (_logger) {
makeCurrent();
delete _logger;
_logger = nullptr;
}
_context->doneCurrent();
delete _context;
_context = nullptr;
@ -68,25 +54,14 @@ bool OffscreenGLCanvas::makeCurrent() {
bool result = _context->makeCurrent(_offscreenSurface);
Q_ASSERT(result);
std::call_once(_reportOnce, []{
std::call_once(_reportOnce, [this]{
qDebug() << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
qDebug() << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
qDebug() << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR));
qDebug() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
GLDebug::setupLogger(this);
});
if (result && !_logger) {
_logger = new QOpenGLDebugLogger(this);
if (_logger->initialize()) {
connect(_logger, &QOpenGLDebugLogger::messageLogged, [](const QOpenGLDebugMessage& message) {
qDebug() << message;
});
_logger->disableMessages(QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::NotificationSeverity);
_logger->startLogging(QOpenGLDebugLogger::LoggingMode::SynchronousLogging);
}
}
return result;
}
@ -101,4 +76,4 @@ QObject* OffscreenGLCanvas::getContextObject() {
void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) {
moveToThread(thread);
_context->moveToThread(thread);
}
}

View file

@ -36,7 +36,6 @@ protected:
std::once_flag _reportOnce;
QOpenGLContext* _context{ nullptr };
QOffscreenSurface* _offscreenSurface{ nullptr };
QOpenGLDebugLogger* _logger{ nullptr };
};
#endif // hifi_OffscreenGLCanvas_h

View file

@ -1,29 +0,0 @@
//
// QOpenGLDebugLoggerWrapper.cpp
//
//
// Created by Clement on 12/4/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "QOpenGLDebugLoggerWrapper.h"
#include <QObject>
#include <QOpenGLDebugLogger>
void OpenGLDebug::log(const QOpenGLDebugMessage & debugMessage) {
qDebug() << debugMessage;
}
void setupDebugLogger(QObject* window) {
QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(window);
logger->initialize(); // initializes in the current context, i.e. ctx
logger->enableMessages();
QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, window, [&](const QOpenGLDebugMessage & debugMessage) {
OpenGLDebug::log(debugMessage);
});
}

View file

@ -1,25 +0,0 @@
//
// QOpenGLDebugLoggerWrapper.h
//
//
// Created by Clement on 12/4/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_QOpenGLDebugLoggerWrapper_h
#define hifi_QOpenGLDebugLoggerWrapper_h
class QObject;
class QOpenGLDebugMessage;
void setupDebugLogger(QObject* window);
class OpenGLDebug {
public:
static void log(const QOpenGLDebugMessage & debugMessage);
};
#endif // hifi_QOpenGLDebugLoggerWrapper_h

View file

@ -32,30 +32,44 @@
using namespace gpu;
using namespace gpu::gl;
static const QString DEBUG_FLAG("HIFI_ENABLE_OPENGL_45");
static bool enableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
static const QString DEBUG_FLAG("HIFI_DISABLE_OPENGL_45");
static bool disableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
Backend* GLBackend::createBackend() {
static GLBackend* INSTANCE{ nullptr };
static const char* GL_BACKEND_PROPERTY_NAME = "com.highfidelity.gl.backend";
BackendPointer GLBackend::createBackend() {
// FIXME provide a mechanism to override the backend for testing
// Where the gpuContext is initialized and where the TRUE Backend is created and assigned
auto version = QOpenGLContextWrapper::currentContextVersion();
GLBackend* result;
if (enableOpenGL45 && version >= 0x0405) {
std::shared_ptr<GLBackend> result;
if (!disableOpenGL45 && version >= 0x0405) {
qDebug() << "Using OpenGL 4.5 backend";
result = new gpu::gl45::GL45Backend();
result = std::make_shared<gpu::gl45::GL45Backend>();
} else {
qDebug() << "Using OpenGL 4.1 backend";
result = new gpu::gl41::GL41Backend();
result = std::make_shared<gpu::gl41::GL41Backend>();
}
result->initInput();
result->initTransform();
INSTANCE = result.get();
void* voidInstance = &(*result);
qApp->setProperty(GL_BACKEND_PROPERTY_NAME, QVariant::fromValue(voidInstance));
gl::GLTexture::initTextureTransferHelper();
return result;
}
GLBackend& getBackend() {
if (!INSTANCE) {
INSTANCE = static_cast<GLBackend*>(qApp->property(GL_BACKEND_PROPERTY_NAME).value<void*>());
}
return *INSTANCE;
}
bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) {
return GLShader::makeProgram(shader, slotBindings);
return GLShader::makeProgram(getBackend(), shader, slotBindings);
}
GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
@ -120,24 +134,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::gl::GLBackend::do_popProfileRange),
};
extern std::function<uint32(const Texture& texture)> TEXTURE_ID_RESOLVER;
void GLBackend::init() {
static std::once_flag once;
std::call_once(once, [] {
TEXTURE_ID_RESOLVER = [](const Texture& texture)->uint32 {
auto object = Backend::getGPUObject<GLTexture>(texture);
if (!object) {
return 0;
}
if (object->getSyncState() != GLSyncState::Idle) {
return object->_downsampleSource._texture;
}
return object->_texture;
};
QString vendor{ (const char*)glGetString(GL_VENDOR) };
QString renderer{ (const char*)glGetString(GL_RENDERER) };
qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
@ -178,6 +177,7 @@ void GLBackend::init() {
}
GLBackend::GLBackend() {
_pipeline._cameraCorrectionBuffer._buffer->flush();
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment);
}
@ -189,7 +189,7 @@ GLBackend::~GLBackend() {
killTransform();
}
void GLBackend::renderPassTransfer(Batch& batch) {
void GLBackend::renderPassTransfer(const Batch& batch) {
const size_t numCommands = batch.getCommands().size();
const Batch::Commands::value_type* command = batch.getCommands().data();
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
@ -245,7 +245,7 @@ void GLBackend::renderPassTransfer(Batch& batch) {
_inRenderTransferPass = false;
}
void GLBackend::renderPassDraw(Batch& batch) {
void GLBackend::renderPassDraw(const Batch& batch) {
_currentDraw = -1;
_transform._camerasItr = _transform._cameraOffsets.begin();
const size_t numCommands = batch.getCommands().size();
@ -290,11 +290,8 @@ void GLBackend::renderPassDraw(Batch& batch) {
}
}
void GLBackend::render(Batch& batch) {
// Finalize the batch by moving all the instanced rendering into the command buffer
batch.preExecute();
_stereo._skybox = batch.isSkyboxEnabled();
void GLBackend::render(const Batch& batch) {
_transform._skybox = _stereo._skybox = batch.isSkyboxEnabled();
// Allow the batch to override the rendering stereo settings
// for things like full framebuffer copy operations (deferred lighting passes)
bool savedStereo = _stereo._enable;
@ -318,6 +315,7 @@ void GLBackend::render(Batch& batch) {
void GLBackend::syncCache() {
recycle();
syncTransformStateCache();
syncPipelineStateCache();
syncInputStateCache();
@ -334,21 +332,21 @@ void GLBackend::setupStereoSide(int side) {
_transform.bindCurrentCamera(side);
}
void GLBackend::do_resetStages(Batch& batch, size_t paramOffset) {
void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) {
resetStages();
}
void GLBackend::do_runLambda(Batch& batch, size_t paramOffset) {
void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) {
std::function<void()> f = batch._lambdas.get(batch._params[paramOffset]._uint);
f();
}
void GLBackend::do_startNamedCall(Batch& batch, size_t paramOffset) {
void GLBackend::do_startNamedCall(const Batch& batch, size_t paramOffset) {
batch._currentNamedCall = batch._names.get(batch._params[paramOffset]._uint);
_currentDraw = -1;
}
void GLBackend::do_stopNamedCall(Batch& batch, size_t paramOffset) {
void GLBackend::do_stopNamedCall(const Batch& batch, size_t paramOffset) {
batch._currentNamedCall.clear();
}
@ -365,14 +363,16 @@ void GLBackend::resetStages() {
}
void GLBackend::do_pushProfileRange(Batch& batch, size_t paramOffset) {
#if defined(NSIGHT_FOUND)
void GLBackend::do_pushProfileRange(const Batch& batch, size_t paramOffset) {
auto name = batch._profileRanges.get(batch._params[paramOffset]._uint);
profileRanges.push_back(name);
#if defined(NSIGHT_FOUND)
nvtxRangePush(name.c_str());
#endif
}
void GLBackend::do_popProfileRange(Batch& batch, size_t paramOffset) {
void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) {
profileRanges.pop_back();
#if defined(NSIGHT_FOUND)
nvtxRangePop();
#endif
@ -385,7 +385,7 @@ void GLBackend::do_popProfileRange(Batch& batch, size_t paramOffset) {
// As long as we don;t use several versions of shaders we can avoid this more complex code path
// #define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, isStereo());
#define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc
void GLBackend::do_glActiveBindTexture(Batch& batch, size_t paramOffset) {
void GLBackend::do_glActiveBindTexture(const Batch& batch, size_t paramOffset) {
glActiveTexture(batch._params[paramOffset + 2]._uint);
glBindTexture(
GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._uint),
@ -394,7 +394,7 @@ void GLBackend::do_glActiveBindTexture(Batch& batch, size_t paramOffset) {
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glUniform1i(Batch& batch, size_t paramOffset) {
void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
@ -408,7 +408,7 @@ void GLBackend::do_glUniform1i(Batch& batch, size_t paramOffset) {
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glUniform1f(Batch& batch, size_t paramOffset) {
void GLBackend::do_glUniform1f(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
@ -422,7 +422,7 @@ void GLBackend::do_glUniform1f(Batch& batch, size_t paramOffset) {
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glUniform2f(Batch& batch, size_t paramOffset) {
void GLBackend::do_glUniform2f(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
@ -436,7 +436,7 @@ void GLBackend::do_glUniform2f(Batch& batch, size_t paramOffset) {
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glUniform3f(Batch& batch, size_t paramOffset) {
void GLBackend::do_glUniform3f(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
@ -451,7 +451,7 @@ void GLBackend::do_glUniform3f(Batch& batch, size_t paramOffset) {
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glUniform4f(Batch& batch, size_t paramOffset) {
void GLBackend::do_glUniform4f(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
@ -467,7 +467,7 @@ void GLBackend::do_glUniform4f(Batch& batch, size_t paramOffset) {
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glUniform3fv(Batch& batch, size_t paramOffset) {
void GLBackend::do_glUniform3fv(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
@ -477,12 +477,12 @@ void GLBackend::do_glUniform3fv(Batch& batch, size_t paramOffset) {
glUniform3fv(
GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int),
batch._params[paramOffset + 1]._uint,
(const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint));
(const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint));
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glUniform4fv(Batch& batch, size_t paramOffset) {
void GLBackend::do_glUniform4fv(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
@ -492,13 +492,13 @@ void GLBackend::do_glUniform4fv(Batch& batch, size_t paramOffset) {
GLint location = GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int);
GLsizei count = batch._params[paramOffset + 1]._uint;
const GLfloat* value = (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint);
const GLfloat* value = (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint);
glUniform4fv(location, count, value);
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glUniform4iv(Batch& batch, size_t paramOffset) {
void GLBackend::do_glUniform4iv(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
@ -508,12 +508,12 @@ void GLBackend::do_glUniform4iv(Batch& batch, size_t paramOffset) {
glUniform4iv(
GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int),
batch._params[paramOffset + 1]._uint,
(const GLint*)batch.editData(batch._params[paramOffset + 0]._uint));
(const GLint*)batch.readData(batch._params[paramOffset + 0]._uint));
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glUniformMatrix3fv(Batch& batch, size_t paramOffset) {
void GLBackend::do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
@ -525,11 +525,11 @@ void GLBackend::do_glUniformMatrix3fv(Batch& batch, size_t paramOffset) {
GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int),
batch._params[paramOffset + 2]._uint,
batch._params[paramOffset + 1]._uint,
(const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint));
(const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint));
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) {
void GLBackend::do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
@ -541,11 +541,11 @@ void GLBackend::do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) {
GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int),
batch._params[paramOffset + 2]._uint,
batch._params[paramOffset + 1]._uint,
(const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint));
(const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint));
(void)CHECK_GL_ERROR();
}
void GLBackend::do_glColor4f(Batch& batch, size_t paramOffset) {
void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) {
glm::vec4 newColor(
batch._params[paramOffset + 3]._float,
@ -559,3 +559,132 @@ void GLBackend::do_glColor4f(Batch& batch, size_t paramOffset) {
}
(void)CHECK_GL_ERROR();
}
void GLBackend::releaseBuffer(GLuint id, Size size) const {
Lock lock(_trashMutex);
_buffersTrash.push_back({ id, size });
}
void GLBackend::releaseTexture(GLuint id, Size size) const {
Lock lock(_trashMutex);
_texturesTrash.push_back({ id, size });
}
void GLBackend::releaseFramebuffer(GLuint id) const {
Lock lock(_trashMutex);
_framebuffersTrash.push_back(id);
}
void GLBackend::releaseShader(GLuint id) const {
Lock lock(_trashMutex);
_shadersTrash.push_back(id);
}
void GLBackend::releaseProgram(GLuint id) const {
Lock lock(_trashMutex);
_shadersTrash.push_back(id);
}
void GLBackend::releaseQuery(GLuint id) const {
Lock lock(_trashMutex);
_queriesTrash.push_back(id);
}
void GLBackend::recycle() const {
{
std::vector<GLuint> ids;
std::list<std::pair<GLuint, Size>> buffersTrash;
{
Lock lock(_trashMutex);
std::swap(_buffersTrash, buffersTrash);
}
ids.reserve(buffersTrash.size());
for (auto pair : buffersTrash) {
ids.push_back(pair.first);
decrementBufferGPUCount();
updateBufferGPUMemoryUsage(pair.second, 0);
}
if (!ids.empty()) {
glDeleteBuffers((GLsizei)ids.size(), ids.data());
}
}
{
std::vector<GLuint> ids;
std::list<GLuint> 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<GLuint> ids;
std::list<std::pair<GLuint, Size>> texturesTrash;
{
Lock lock(_trashMutex);
std::swap(_texturesTrash, texturesTrash);
}
ids.reserve(texturesTrash.size());
for (auto pair : texturesTrash) {
ids.push_back(pair.first);
decrementTextureGPUCount();
updateTextureGPUMemoryUsage(pair.second, 0);
}
if (!ids.empty()) {
glDeleteTextures((GLsizei)ids.size(), ids.data());
}
}
{
std::list<GLuint> programsTrash;
{
Lock lock(_trashMutex);
std::swap(_programsTrash, programsTrash);
}
for (auto id : programsTrash) {
glDeleteProgram(id);
}
}
{
std::list<GLuint> shadersTrash;
{
Lock lock(_trashMutex);
std::swap(_shadersTrash, shadersTrash);
}
for (auto id : shadersTrash) {
glDeleteShader(id);
}
}
{
std::vector<GLuint> ids;
std::list<GLuint> 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());
}
}
}
void GLBackend::setCameraCorrection(const Mat4& correction) {
_transform._correction.correction = correction;
_transform._correction.correctionInverse = glm::inverse(correction);
_pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction);
_pipeline._cameraCorrectionBuffer._buffer->flush();
}

View file

@ -13,6 +13,7 @@
#include <assert.h>
#include <functional>
#include <memory>
#include <bitset>
#include <queue>
#include <utility>
@ -30,20 +31,22 @@
namespace gpu { namespace gl {
class GLBackend : public Backend {
class GLBackend : public Backend, public std::enable_shared_from_this<GLBackend> {
// Context Backend static interface required
friend class gpu::Context;
static void init();
static Backend* createBackend();
static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings);
static BackendPointer createBackend();
protected:
explicit GLBackend(bool syncCache);
GLBackend();
public:
static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet());
~GLBackend();
void render(Batch& batch) final;
void setCameraCorrection(const Mat4& correction);
void render(const Batch& batch) final;
// This call synchronize the Full Backend cache with the current GLState
// THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync
@ -72,74 +75,74 @@ public:
size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; }
// Draw Stage
virtual void do_draw(Batch& batch, size_t paramOffset) = 0;
virtual void do_drawIndexed(Batch& batch, size_t paramOffset) = 0;
virtual void do_drawInstanced(Batch& batch, size_t paramOffset) = 0;
virtual void do_drawIndexedInstanced(Batch& batch, size_t paramOffset) = 0;
virtual void do_multiDrawIndirect(Batch& batch, size_t paramOffset) = 0;
virtual void do_multiDrawIndexedIndirect(Batch& batch, size_t paramOffset) = 0;
virtual void do_draw(const Batch& batch, size_t paramOffset) = 0;
virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0;
virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0;
virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0;
virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0;
virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0;
// Input Stage
virtual void do_setInputFormat(Batch& batch, size_t paramOffset) final;
virtual void do_setInputBuffer(Batch& batch, size_t paramOffset) final;
virtual void do_setIndexBuffer(Batch& batch, size_t paramOffset) final;
virtual void do_setIndirectBuffer(Batch& batch, size_t paramOffset) final;
virtual void do_generateTextureMips(Batch& batch, size_t paramOffset) final;
virtual void do_setInputFormat(const Batch& batch, size_t paramOffset) final;
virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final;
// Transform Stage
virtual void do_setModelTransform(Batch& batch, size_t paramOffset) final;
virtual void do_setViewTransform(Batch& batch, size_t paramOffset) final;
virtual void do_setProjectionTransform(Batch& batch, size_t paramOffset) final;
virtual void do_setViewportTransform(Batch& batch, size_t paramOffset) final;
virtual void do_setDepthRangeTransform(Batch& batch, size_t paramOffset) final;
virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final;
virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final;
// Uniform Stage
virtual void do_setUniformBuffer(Batch& batch, size_t paramOffset) final;
virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final;
// Resource Stage
virtual void do_setResourceTexture(Batch& batch, size_t paramOffset) final;
virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final;
// Pipeline Stage
virtual void do_setPipeline(Batch& batch, size_t paramOffset) final;
virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final;
// Output stage
virtual void do_setFramebuffer(Batch& batch, size_t paramOffset) final;
virtual void do_clearFramebuffer(Batch& batch, size_t paramOffset) final;
virtual void do_blit(Batch& batch, size_t paramOffset) = 0;
virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final;
virtual void do_blit(const Batch& batch, size_t paramOffset) = 0;
// Query section
virtual void do_beginQuery(Batch& batch, size_t paramOffset) final;
virtual void do_endQuery(Batch& batch, size_t paramOffset) final;
virtual void do_getQuery(Batch& batch, size_t paramOffset) final;
virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final;
virtual void do_endQuery(const Batch& batch, size_t paramOffset) final;
virtual void do_getQuery(const Batch& batch, size_t paramOffset) final;
// Reset stages
virtual void do_resetStages(Batch& batch, size_t paramOffset) final;
virtual void do_resetStages(const Batch& batch, size_t paramOffset) final;
virtual void do_runLambda(Batch& batch, size_t paramOffset) final;
virtual void do_runLambda(const Batch& batch, size_t paramOffset) final;
virtual void do_startNamedCall(Batch& batch, size_t paramOffset) final;
virtual void do_stopNamedCall(Batch& batch, size_t paramOffset) final;
virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final;
virtual void do_stopNamedCall(const Batch& batch, size_t paramOffset) final;
virtual void do_pushProfileRange(Batch& batch, size_t paramOffset) final;
virtual void do_popProfileRange(Batch& batch, size_t paramOffset) final;
virtual void do_pushProfileRange(const Batch& batch, size_t paramOffset) final;
virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final;
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
virtual void do_glActiveBindTexture(Batch& batch, size_t paramOffset) final;
virtual void do_glActiveBindTexture(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform1i(Batch& batch, size_t paramOffset) final;
virtual void do_glUniform1f(Batch& batch, size_t paramOffset) final;
virtual void do_glUniform2f(Batch& batch, size_t paramOffset) final;
virtual void do_glUniform3f(Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4f(Batch& batch, size_t paramOffset) final;
virtual void do_glUniform3fv(Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4fv(Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4iv(Batch& batch, size_t paramOffset) final;
virtual void do_glUniformMatrix3fv(Batch& batch, size_t paramOffset) final;
virtual void do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) final;
virtual void do_glUniform1i(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform1f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform2f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform3f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4f(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform3fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniform4iv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final;
virtual void do_glColor4f(Batch& batch, size_t paramOffset) final;
virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final;
// The State setters called by the GLState::Commands when a new state is assigned
virtual void do_setStateFillMode(int32 mode) final;
@ -156,21 +159,28 @@ public:
virtual void do_setStateSampleMask(uint32 mask) final;
virtual void do_setStateBlend(State::BlendFunction blendFunction) final;
virtual void do_setStateColorWriteMask(uint32 mask) final;
virtual void do_setStateBlendFactor(Batch& batch, size_t paramOffset) final;
virtual void do_setStateScissorRect(Batch& batch, size_t paramOffset) final;
virtual void do_setStateBlendFactor(const Batch& batch, size_t paramOffset) final;
virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final;
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0;
virtual GLuint getBufferID(const Buffer& buffer) = 0;
virtual GLuint getQueryID(const QueryPointer& query) = 0;
virtual bool isTextureReady(const TexturePointer& texture);
virtual void releaseBuffer(GLuint id, Size size) const;
virtual void releaseTexture(GLuint id, Size size) const;
virtual void releaseFramebuffer(GLuint id) const;
virtual void releaseShader(GLuint id) const;
virtual void releaseProgram(GLuint id) const;
virtual void releaseQuery(GLuint id) const;
protected:
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
void recycle() const override;
virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0;
virtual GLuint getBufferID(const Buffer& buffer) = 0;
virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0;
virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0;
virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0;
virtual GLuint getQueryID(const QueryPointer& query) = 0;
virtual GLQuery* syncGPUObject(const Query& query) = 0;
static const size_t INVALID_OFFSET = (size_t)-1;
@ -178,8 +188,17 @@ protected:
int32_t _uboAlignment { 0 };
int _currentDraw { -1 };
void renderPassTransfer(Batch& batch);
void renderPassDraw(Batch& batch);
std::list<std::string> profileRanges;
mutable Mutex _trashMutex;
mutable std::list<std::pair<GLuint, Size>> _buffersTrash;
mutable std::list<std::pair<GLuint, Size>> _texturesTrash;
mutable std::list<GLuint> _framebuffersTrash;
mutable std::list<GLuint> _shadersTrash;
mutable std::list<GLuint> _programsTrash;
mutable std::list<GLuint> _queriesTrash;
void renderPassTransfer(const Batch& batch);
void renderPassDraw(const Batch& batch);
void setupStereoSide(int side);
virtual void initInput() final;
@ -229,6 +248,14 @@ protected:
void updateTransform(const Batch& batch);
void resetTransformStage();
// Allows for correction of the camera pose to account for changes
// between the time when a was recorded and the time(s) when it is
// executed
struct CameraCorrection {
Mat4 correction;
Mat4 correctionInverse;
};
struct TransformStageState {
using CameraBufferElement = TransformCamera;
using TransformCameras = std::vector<CameraBufferElement>;
@ -243,7 +270,11 @@ protected:
GLuint _drawCallInfoBuffer { 0 };
GLuint _objectBufferTexture { 0 };
size_t _cameraUboSize { 0 };
bool _viewIsCamera{ false };
bool _skybox { false };
Transform _view;
CameraCorrection _correction;
Mat4 _projection;
Vec4i _viewport { 0, 0, 1, 1 };
Vec2 _depthRange { 0.0f, 1.0f };
@ -297,14 +328,21 @@ protected:
PipelinePointer _pipeline;
GLuint _program { 0 };
GLint _cameraCorrectionLocation { -1 };
GLShader* _programShader { nullptr };
bool _invalidProgram { false };
State::Data _stateCache { State::DEFAULT };
BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
State::Data _stateCache{ State::DEFAULT };
State::Signature _stateSignatureCache { 0 };
GLState* _state { nullptr };
bool _invalidState { false };
PipelineStageState() {
_cameraCorrectionBuffer.edit<CameraCorrection>() = CameraCorrection();
}
} _pipeline;
// Synchronize the state cache of this Backend with the actual real state of the GL Context
@ -323,7 +361,7 @@ protected:
void resetStages();
typedef void (GLBackend::*CommandCall)(Batch&, size_t);
typedef void (GLBackend::*CommandCall)(const Batch&, size_t);
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
friend class GLState;
};

View file

@ -14,7 +14,7 @@
using namespace gpu;
using namespace gpu::gl;
void GLBackend::do_setInputFormat(Batch& batch, size_t paramOffset) {
void GLBackend::do_setInputFormat(const Batch& batch, size_t paramOffset) {
Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint);
if (format != _input._format) {
@ -23,7 +23,7 @@ void GLBackend::do_setInputFormat(Batch& batch, size_t paramOffset) {
}
}
void GLBackend::do_setInputBuffer(Batch& batch, size_t paramOffset) {
void GLBackend::do_setInputBuffer(const Batch& batch, size_t paramOffset) {
Offset stride = batch._params[paramOffset + 0]._uint;
Offset offset = batch._params[paramOffset + 1]._uint;
BufferPointer buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
@ -116,7 +116,7 @@ void GLBackend::resetInputStage() {
}
void GLBackend::do_setIndexBuffer(Batch& batch, size_t paramOffset) {
void GLBackend::do_setIndexBuffer(const Batch& batch, size_t paramOffset) {
_input._indexBufferType = (Type)batch._params[paramOffset + 2]._uint;
_input._indexBufferOffset = batch._params[paramOffset + 0]._uint;
@ -133,7 +133,7 @@ void GLBackend::do_setIndexBuffer(Batch& batch, size_t paramOffset) {
(void) CHECK_GL_ERROR();
}
void GLBackend::do_setIndirectBuffer(Batch& batch, size_t paramOffset) {
void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) {
_input._indirectBufferOffset = batch._params[paramOffset + 1]._uint;
_input._indirectBufferStride = batch._params[paramOffset + 2]._uint;

View file

@ -35,7 +35,7 @@ void GLBackend::resetOutputStage() {
glEnable(GL_FRAMEBUFFER_SRGB);
}
void GLBackend::do_setFramebuffer(Batch& batch, size_t paramOffset) {
void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) {
auto framebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint);
if (_output._framebuffer != framebuffer) {
auto newFBO = getFramebufferID(framebuffer);
@ -47,7 +47,7 @@ void GLBackend::do_setFramebuffer(Batch& batch, size_t paramOffset) {
}
}
void GLBackend::do_clearFramebuffer(Batch& batch, size_t paramOffset) {
void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
if (_stereo._enable && !_pipeline._stateCache.scissorEnable) {
qWarning("Clear without scissor in stereo mode");
}

View file

@ -19,7 +19,7 @@
using namespace gpu;
using namespace gpu::gl;
void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) {
void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) {
PipelinePointer pipeline = batch._pipelines.get(batch._params[paramOffset + 0]._uint);
if (_pipeline._pipeline == pipeline) {
@ -34,13 +34,14 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) {
_pipeline._pipeline.reset();
_pipeline._program = 0;
_pipeline._cameraCorrectionLocation = -1;
_pipeline._programShader = nullptr;
_pipeline._invalidProgram = true;
_pipeline._state = nullptr;
_pipeline._invalidState = true;
} else {
auto pipelineObject = GLPipeline::sync(*pipeline);
auto pipelineObject = GLPipeline::sync(*this, *pipeline);
if (!pipelineObject) {
return;
}
@ -53,6 +54,7 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) {
_pipeline._program = glprogram;
_pipeline._programShader = pipelineObject->_program;
_pipeline._invalidProgram = true;
_pipeline._cameraCorrectionLocation = pipelineObject->_cameraCorrection;
}
// Now for the state
@ -68,6 +70,10 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) {
// THis should be done on Pipeline::update...
if (_pipeline._invalidProgram) {
glUseProgram(_pipeline._program);
if (_pipeline._cameraCorrectionLocation != -1) {
auto cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer);
glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection));
}
(void) CHECK_GL_ERROR();
_pipeline._invalidProgram = false;
}
@ -135,7 +141,7 @@ void GLBackend::resetUniformStage() {
}
}
void GLBackend::do_setUniformBuffer(Batch& batch, size_t paramOffset) {
void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) {
GLuint slot = batch._params[paramOffset + 3]._uint;
BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
GLintptr rangeStart = batch._params[paramOffset + 1]._uint;
@ -184,7 +190,7 @@ void GLBackend::resetResourceStage() {
}
}
void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) {
void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) {
GLuint slot = batch._params[paramOffset + 1]._uint;
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);

View file

@ -21,7 +21,7 @@ static bool timeElapsed = true;
static bool timeElapsed = false;
#endif
void GLBackend::do_beginQuery(Batch& batch, size_t paramOffset) {
void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) {
auto query = batch._queries.get(batch._params[paramOffset]._uint);
GLQuery* glquery = syncGPUObject(*query);
if (glquery) {
@ -34,7 +34,7 @@ void GLBackend::do_beginQuery(Batch& batch, size_t paramOffset) {
}
}
void GLBackend::do_endQuery(Batch& batch, size_t paramOffset) {
void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) {
auto query = batch._queries.get(batch._params[paramOffset]._uint);
GLQuery* glquery = syncGPUObject(*query);
if (glquery) {
@ -47,7 +47,7 @@ void GLBackend::do_endQuery(Batch& batch, size_t paramOffset) {
}
}
void GLBackend::do_getQuery(Batch& batch, size_t paramOffset) {
void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) {
auto query = batch._queries.get(batch._params[paramOffset]._uint);
GLQuery* glquery = syncGPUObject(*query);
if (glquery) {

View file

@ -290,7 +290,7 @@ void GLBackend::do_setStateColorWriteMask(uint32 mask) {
}
void GLBackend::do_setStateBlendFactor(Batch& batch, size_t paramOffset) {
void GLBackend::do_setStateBlendFactor(const Batch& batch, size_t paramOffset) {
Vec4 factor(batch._params[paramOffset + 0]._float,
batch._params[paramOffset + 1]._float,
batch._params[paramOffset + 2]._float,
@ -300,9 +300,9 @@ void GLBackend::do_setStateBlendFactor(Batch& batch, size_t paramOffset) {
(void)CHECK_GL_ERROR();
}
void GLBackend::do_setStateScissorRect(Batch& batch, size_t paramOffset) {
void GLBackend::do_setStateScissorRect(const Batch& batch, size_t paramOffset) {
Vec4i rect;
memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
memcpy(&rect, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i));
if (_stereo._enable) {
rect.z /= 2;

View file

@ -14,7 +14,14 @@
using namespace gpu;
using namespace gpu::gl;
void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
bool GLBackend::isTextureReady(const TexturePointer& texture) {
// DO not transfer the texture, this call is expected for rendering texture
GLTexture* object = syncGPUObject(texture, true);
return object && object->isReady();
}
void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) {
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
if (!resourceTexture) {
return;

View file

@ -14,21 +14,22 @@ using namespace gpu;
using namespace gpu::gl;
// Transform Stage
void GLBackend::do_setModelTransform(Batch& batch, size_t paramOffset) {
void GLBackend::do_setModelTransform(const Batch& batch, size_t paramOffset) {
}
void GLBackend::do_setViewTransform(Batch& batch, size_t paramOffset) {
void GLBackend::do_setViewTransform(const Batch& batch, size_t paramOffset) {
_transform._view = batch._transforms.get(batch._params[paramOffset]._uint);
_transform._viewIsCamera = batch._params[paramOffset + 1]._uint != 0;
_transform._invalidView = true;
}
void GLBackend::do_setProjectionTransform(Batch& batch, size_t paramOffset) {
memcpy(&_transform._projection, batch.editData(batch._params[paramOffset]._uint), sizeof(Mat4));
void GLBackend::do_setProjectionTransform(const Batch& batch, size_t paramOffset) {
memcpy(&_transform._projection, batch.readData(batch._params[paramOffset]._uint), sizeof(Mat4));
_transform._invalidProj = true;
}
void GLBackend::do_setViewportTransform(Batch& batch, size_t paramOffset) {
memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) {
memcpy(&_transform._viewport, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i));
if (!_inRenderTransferPass && !isStereo()) {
ivec4& vp = _transform._viewport;
@ -39,7 +40,7 @@ void GLBackend::do_setViewportTransform(Batch& batch, size_t paramOffset) {
_transform._invalidViewport = true;
}
void GLBackend::do_setDepthRangeTransform(Batch& batch, size_t paramOffset) {
void GLBackend::do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) {
Vec2 depthRange(batch._params[paramOffset + 1]._float, batch._params[paramOffset + 0]._float);
@ -82,6 +83,16 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
}
if (_invalidView) {
// Apply the correction
if (_viewIsCamera && _correction.correction != glm::mat4()) {
// FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out?
Transform result;
_view.mult(result, _view, _correction.correction);
if (_skybox) {
result.setTranslation(vec3());
}
_view = result;
}
// This is when the _view matrix gets assigned
_view.getInverseMatrix(_camera._view);
}

View file

@ -7,20 +7,24 @@
//
#include "GLBuffer.h"
#include "GLBackend.h"
using namespace gpu;
using namespace gpu::gl;
GLBuffer::~GLBuffer() {
glDeleteBuffers(1, &_id);
Backend::decrementBufferGPUCount();
Backend::updateBufferGPUMemoryUsage(_size, 0);
if (_id) {
auto backend = _backend.lock();
if (backend) {
backend->releaseBuffer(_id, _size);
}
}
}
GLBuffer::GLBuffer(const Buffer& buffer, GLuint id) :
GLObject(buffer, id),
_size((GLuint)buffer._sysmem.getSize()),
_stamp(buffer._sysmem.getStamp())
GLBuffer::GLBuffer(const std::weak_ptr<GLBackend>& backend, const Buffer& buffer, GLuint id) :
GLObject(backend, buffer, id),
_size((GLuint)buffer._renderSysmem.getSize()),
_stamp(buffer._renderSysmem.getStamp())
{
Backend::incrementBufferGPUCount();
Backend::updateBufferGPUMemoryUsage(0, _size);

View file

@ -9,21 +9,30 @@
#define hifi_gpu_gl_GLBuffer_h
#include "GLShared.h"
#include "GLBackend.h"
namespace gpu { namespace gl {
class GLBuffer : public GLObject<Buffer> {
public:
template <typename GLBufferType>
static GLBufferType* sync(const Buffer& buffer) {
static GLBufferType* sync(GLBackend& backend, const Buffer& buffer) {
if (buffer.getSysmem().getSize() != 0) {
if (buffer._getUpdateCount == 0) {
qWarning() << "Unsynced buffer";
}
if (buffer._getUpdateCount < buffer._applyUpdateCount) {
qWarning() << "Unsynced buffer " << buffer._getUpdateCount << " " << buffer._applyUpdateCount;
}
}
GLBufferType* object = Backend::getGPUObject<GLBufferType>(buffer);
// Has the storage size changed?
if (!object || object->_stamp != buffer.getSysmem().getStamp()) {
object = new GLBufferType(buffer, object);
if (!object || object->_stamp != buffer._renderSysmem.getStamp()) {
object = new GLBufferType(backend.shared_from_this(), buffer, object);
}
if (0 != (buffer._flags & Buffer::DIRTY)) {
if (0 != (buffer._renderPages._flags & PageManager::DIRTY)) {
object->transfer();
}
@ -31,8 +40,8 @@ public:
}
template <typename GLBufferType>
static GLuint getId(const Buffer& buffer) {
GLBuffer* bo = sync<GLBufferType>(buffer);
static GLuint getId(GLBackend& backend, const Buffer& buffer) {
GLBuffer* bo = sync<GLBufferType>(backend, buffer);
if (bo) {
return bo->_buffer;
} else {
@ -49,7 +58,7 @@ public:
virtual void transfer() = 0;
protected:
GLBuffer(const Buffer& buffer, GLuint id);
GLBuffer(const std::weak_ptr<GLBackend>& backend, const Buffer& buffer, GLuint id);
};
} }

View file

@ -7,10 +7,19 @@
//
#include "GLFramebuffer.h"
#include "GLBackend.h"
using namespace gpu;
using namespace gpu::gl;
GLFramebuffer::~GLFramebuffer() {
if (_id) {
auto backend = _backend.lock();
if (backend) {
backend->releaseFramebuffer(_id);
}
}
}
bool GLFramebuffer::checkStatus(GLenum target) const {
bool result = false;

View file

@ -9,13 +9,14 @@
#define hifi_gpu_gl_GLFramebuffer_h
#include "GLShared.h"
#include "GLBackend.h"
namespace gpu { namespace gl {
class GLFramebuffer : public GLObject<Framebuffer> {
public:
template <typename GLFramebufferType>
static GLFramebufferType* sync(const Framebuffer& framebuffer) {
static GLFramebufferType* sync(GLBackend& backend, const Framebuffer& framebuffer) {
GLFramebufferType* object = Backend::getGPUObject<GLFramebufferType>(framebuffer);
bool needsUpate { false };
@ -36,7 +37,7 @@ public:
// need to have a gpu object?
if (!object) {
// All is green, assign the gpuobject to the Framebuffer
object = new GLFramebufferType(framebuffer);
object = new GLFramebufferType(backend.shared_from_this(), framebuffer);
Backend::setGPUObject(framebuffer, object);
(void)CHECK_GL_ERROR();
}
@ -46,8 +47,8 @@ public:
}
template <typename GLFramebufferType>
static GLuint getId(const Framebuffer& framebuffer) {
GLFramebufferType* fbo = sync<GLFramebufferType>(framebuffer);
static GLuint getId(GLBackend& backend, const Framebuffer& framebuffer) {
GLFramebufferType* fbo = sync<GLFramebufferType>(backend, framebuffer);
if (fbo) {
return fbo->_id;
} else {
@ -65,8 +66,8 @@ protected:
virtual void update() = 0;
bool checkStatus(GLenum target) const;
GLFramebuffer(const Framebuffer& framebuffer, GLuint id) : GLObject(framebuffer, id) {}
~GLFramebuffer() { if (_id) { glDeleteFramebuffers(1, &_id); } };
GLFramebuffer(const std::weak_ptr<GLBackend>& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {}
~GLFramebuffer();
};

View file

@ -14,7 +14,7 @@
using namespace gpu;
using namespace gpu::gl;
GLPipeline* GLPipeline::sync(const Pipeline& pipeline) {
GLPipeline* GLPipeline::sync(GLBackend& backend, const Pipeline& pipeline) {
GLPipeline* object = Backend::getGPUObject<GLPipeline>(pipeline);
// If GPU object already created then good
@ -30,7 +30,7 @@ GLPipeline* GLPipeline::sync(const Pipeline& pipeline) {
return nullptr;
}
GLShader* programObject = GLShader::sync(*shader);
GLShader* programObject = GLShader::sync(backend, *shader);
if (programObject == nullptr) {
shader->setCompilationHasFailed(true);
return nullptr;
@ -48,6 +48,10 @@ GLPipeline* GLPipeline::sync(const Pipeline& pipeline) {
Backend::setGPUObject(pipeline, object);
}
// Special case for view correction matrices, any pipeline that declares the correction buffer
// uniform will automatically have it provided without any client code necessary.
// Required for stable lighting in the HMD.
object->_cameraCorrection = shader->getBuffers().findLocation("cameraCorrectionBuffer");
object->_program = programObject;
object->_state = stateObject;

View file

@ -14,10 +14,13 @@ namespace gpu { namespace gl {
class GLPipeline : public GPUObject {
public:
static GLPipeline* sync(const Pipeline& pipeline);
static GLPipeline* sync(GLBackend& backend, const Pipeline& pipeline);
GLShader* _program { nullptr };
GLState* _state { nullptr };
// Bit of a hack, any pipeline can need the camera correction buffer at execution time, so
// we store whether a given pipeline has declared the uniform buffer for it.
int32 _cameraCorrection { -1 };
};
} }

View file

@ -9,6 +9,7 @@
#define hifi_gpu_gl_GLQuery_h
#include "GLShared.h"
#include "GLBackend.h"
namespace gpu { namespace gl {
@ -16,13 +17,13 @@ class GLQuery : public GLObject<Query> {
using Parent = gpu::gl::GLObject<Query>;
public:
template <typename GLQueryType>
static GLQueryType* sync(const Query& query) {
static GLQueryType* sync(GLBackend& backend, const Query& query) {
GLQueryType* object = Backend::getGPUObject<GLQueryType>(query);
// need to have a gpu object?
if (!object) {
// All is green, assign the gpuobject to the Query
object = new GLQueryType(query);
object = new GLQueryType(backend.shared_from_this(), query);
(void)CHECK_GL_ERROR();
Backend::setGPUObject(query, object);
}
@ -31,12 +32,12 @@ public:
}
template <typename GLQueryType>
static GLuint getId(const QueryPointer& query) {
static GLuint getId(GLBackend& backend, const QueryPointer& query) {
if (!query) {
return 0;
}
GLQuery* object = sync<GLQueryType>(*query);
GLQuery* object = sync<GLQueryType>(backend, *query);
if (!object) {
return 0;
}
@ -49,7 +50,7 @@ public:
GLuint64 _result { (GLuint64)-1 };
protected:
GLQuery(const Query& query, GLuint endId, GLuint beginId) : Parent(query, endId), _beginqo(beginId){}
GLQuery(const std::weak_ptr<GLBackend>& backend, const Query& query, GLuint endId, GLuint beginId) : Parent(backend, query, endId), _beginqo(beginId) {}
~GLQuery() {
if (_id) {
GLuint ids[2] = { _endqo, _beginqo };

View file

@ -11,16 +11,19 @@
using namespace gpu;
using namespace gpu::gl;
GLShader::GLShader() {
GLShader::GLShader(const std::weak_ptr<GLBackend>& backend) : _backend(backend) {
}
GLShader::~GLShader() {
for (auto& so : _shaderObjects) {
if (so.glshader != 0) {
glDeleteShader(so.glshader);
}
if (so.glprogram != 0) {
glDeleteProgram(so.glprogram);
auto backend = _backend.lock();
if (backend) {
if (so.glshader != 0) {
backend->releaseShader(so.glshader);
}
if (so.glprogram != 0) {
backend->releaseProgram(so.glprogram);
}
}
}
}
@ -54,7 +57,7 @@ static const std::array<std::string, GLShader::NumVersions> VERSION_DEFINES { {
""
} };
GLShader* compileBackendShader(const Shader& shader) {
GLShader* compileBackendShader(GLBackend& backend, const Shader& shader) {
// Any GLSLprogram ? normally yes...
const std::string& shaderSource = shader.getSource().getCode();
GLenum shaderDomain = SHADER_DOMAINS[shader.getType()];
@ -72,13 +75,13 @@ GLShader* compileBackendShader(const Shader& shader) {
}
// So far so good, the shader is created successfully
GLShader* object = new GLShader();
GLShader* object = new GLShader(backend.shared_from_this());
object->_shaderObjects = shaderObjects;
return object;
}
GLShader* compileBackendProgram(const Shader& program) {
GLShader* compileBackendProgram(GLBackend& backend, const Shader& program) {
if (!program.isProgram()) {
return nullptr;
}
@ -91,7 +94,7 @@ GLShader* compileBackendProgram(const Shader& program) {
// Let's go through every shaders and make sure they are ready to go
std::vector< GLuint > shaderGLObjects;
for (auto subShader : program.getShaders()) {
auto object = GLShader::sync(*subShader);
auto object = GLShader::sync(backend, *subShader);
if (object) {
shaderGLObjects.push_back(object->_shaderObjects[version].glshader);
} else {
@ -111,13 +114,13 @@ GLShader* compileBackendProgram(const Shader& program) {
}
// So far so good, the program versions have all been created successfully
GLShader* object = new GLShader();
GLShader* object = new GLShader(backend.shared_from_this());
object->_shaderObjects = programObjects;
return object;
}
GLShader* GLShader::sync(const Shader& shader) {
GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) {
GLShader* object = Backend::getGPUObject<GLShader>(shader);
// If GPU object already created then good
@ -126,26 +129,27 @@ GLShader* GLShader::sync(const Shader& shader) {
}
// need to have a gpu object?
if (shader.isProgram()) {
GLShader* tempObject = compileBackendProgram(shader);
GLShader* tempObject = compileBackendProgram(backend, shader);
if (tempObject) {
object = tempObject;
Backend::setGPUObject(shader, object);
}
} else if (shader.isDomain()) {
GLShader* tempObject = compileBackendShader(shader);
GLShader* tempObject = compileBackendShader(backend, shader);
if (tempObject) {
object = tempObject;
Backend::setGPUObject(shader, object);
}
}
glFinish();
return object;
}
bool GLShader::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) {
bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings) {
// First make sure the Shader has been compiled
GLShader* object = sync(shader);
GLShader* object = sync(backend, shader);
if (!object) {
return false;
}
@ -181,7 +185,6 @@ bool GLShader::makeProgram(Shader& shader, const Shader::BindingSet& slotBinding
}
}
return true;
}

View file

@ -14,8 +14,8 @@ namespace gpu { namespace gl {
class GLShader : public GPUObject {
public:
static GLShader* sync(const Shader& shader);
static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings);
static GLShader* sync(GLBackend& backend, const Shader& shader);
static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings);
enum Version {
Mono = 0,
@ -28,7 +28,7 @@ public:
using UniformMapping = std::map<GLint, GLint>;
using UniformMappingVersions = std::vector<UniformMapping>;
GLShader();
GLShader(const std::weak_ptr<GLBackend>& backend);
~GLShader();
ShaderObjects _shaderObjects;
@ -44,6 +44,7 @@ public:
return srcLoc;
}
const std::weak_ptr<GLBackend> _backend;
};
} }

View file

@ -557,46 +557,67 @@ int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindin
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots);
std::vector<GLint> uniformBufferSlotMap(maxNumUniformBufferSlots, -1);
struct UniformBlockInfo {
using Vector = std::vector<UniformBlockInfo>;
const GLuint index{ 0 };
const std::string name;
GLint binding{ -1 };
GLint size{ 0 };
static std::string getName(GLuint glprogram, GLuint i) {
static const GLint NAME_LENGTH = 256;
GLint length = 0;
GLchar nameBuffer[NAME_LENGTH];
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length);
glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer);
return std::string(nameBuffer);
}
UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) {
glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding);
glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size);
}
};
UniformBlockInfo::Vector uniformBlocks;
uniformBlocks.reserve(buffersCount);
for (int i = 0; i < buffersCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLint binding = -1;
uniformBlocks.push_back(UniformBlockInfo(glprogram, i));
}
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length);
glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, name);
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_BINDING, &binding);
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size);
GLuint blockIndex = glGetUniformBlockIndex(glprogram, name);
// CHeck if there is a requested binding for this block
auto requestedBinding = slotBindings.find(std::string(name));
for (auto& info : uniformBlocks) {
auto requestedBinding = slotBindings.find(info.name);
if (requestedBinding != slotBindings.end()) {
// If yes force it
if (binding != (*requestedBinding)._location) {
binding = (*requestedBinding)._location;
glUniformBlockBinding(glprogram, blockIndex, binding);
}
} else if (binding == 0) {
info.binding = (*requestedBinding)._location;
glUniformBlockBinding(glprogram, info.index, info.binding);
uniformBufferSlotMap[info.binding] = info.index;
}
}
for (auto& info : uniformBlocks) {
if (slotBindings.count(info.name)) {
continue;
}
// If the binding is 0, or the binding maps to an already used binding
if (info.binding == 0 || uniformBufferSlotMap[info.binding] != UNUSED_SLOT) {
// If no binding was assigned then just do it finding a free slot
auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot);
if (slotIt != uniformBufferSlotMap.end()) {
binding = slotIt - uniformBufferSlotMap.begin();
glUniformBlockBinding(glprogram, blockIndex, binding);
info.binding = slotIt - uniformBufferSlotMap.begin();
glUniformBlockBinding(glprogram, info.index, info.binding);
} else {
// This should neve happen, an active ubo cannot find an available slot among the max available?!
binding = -1;
info.binding = -1;
}
}
// If binding is valid record it
if (binding >= 0) {
uniformBufferSlotMap[binding] = blockIndex;
}
Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER);
buffers.insert(Shader::Slot(name, binding, element, Resource::BUFFER, size));
uniformBufferSlotMap[info.binding] = info.index;
}
for (auto& info : uniformBlocks) {
static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER);
buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size));
}
return buffersCount;
}

View file

@ -121,15 +121,19 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
bool checkGLError(const char* name = nullptr);
bool checkGLErrorDebug(const char* name = nullptr);
class GLBackend;
template <typename GPUType>
struct GLObject : public GPUObject {
public:
GLObject(const GPUType& gpuObject, GLuint id) : _gpuObject(gpuObject), _id(id) {}
GLObject(const std::weak_ptr<GLBackend>& backend, const GPUType& gpuObject, GLuint id) : _gpuObject(gpuObject), _id(id), _backend(backend) {}
virtual ~GLObject() { }
const GPUType& _gpuObject;
const GLuint _id;
protected:
const std::weak_ptr<GLBackend> _backend;
};
class GlBuffer;

View file

@ -11,6 +11,7 @@
#include <NumericalConstants.h>
#include "GLTextureTransfer.h"
#include "GLBackend.h"
using namespace gpu;
using namespace gpu::gl;
@ -117,7 +118,9 @@ float GLTexture::getMemoryPressure() {
return (float)consumedGpuMemory / (float)availableTextureMemory;
}
GLTexture::DownsampleSource::DownsampleSource(GLTexture* oldTexture) :
GLTexture::DownsampleSource::DownsampleSource(const std::weak_ptr<gl::GLBackend>& backend, GLTexture* oldTexture) :
_backend(backend),
_size(oldTexture ? oldTexture->_size : 0),
_texture(oldTexture ? oldTexture->takeOwnership() : 0),
_minMip(oldTexture ? oldTexture->_minMip : 0),
_maxMip(oldTexture ? oldTexture->_maxMip : 0)
@ -126,20 +129,22 @@ GLTexture::DownsampleSource::DownsampleSource(GLTexture* oldTexture) :
GLTexture::DownsampleSource::~DownsampleSource() {
if (_texture) {
glDeleteTextures(1, &_texture);
Backend::decrementTextureGPUCount();
auto backend = _backend.lock();
if (backend) {
backend->releaseTexture(_texture, _size);
}
}
}
GLTexture::GLTexture(const gpu::Texture& texture, GLuint id, GLTexture* originalTexture, bool transferrable) :
GLObject(texture, id),
GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const gpu::Texture& texture, GLuint id, GLTexture* originalTexture, bool transferrable) :
GLObject(backend, texture, id),
_storageStamp(texture.getStamp()),
_target(getGLTextureType(texture)),
_maxMip(texture.maxMip()),
_minMip(texture.minMip()),
_virtualSize(texture.evalTotalSize()),
_transferrable(transferrable),
_downsampleSource(originalTexture)
_downsampleSource(backend, originalTexture)
{
if (_transferrable) {
uint16 mipCount = usedMipLevels();
@ -156,8 +161,8 @@ GLTexture::GLTexture(const gpu::Texture& texture, GLuint id, GLTexture* original
// Create the texture and allocate storage
GLTexture::GLTexture(const Texture& texture, GLuint id, bool transferrable) :
GLTexture(texture, id, nullptr, transferrable)
GLTexture::GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id, bool transferrable) :
GLTexture(backend, texture, id, nullptr, transferrable)
{
// FIXME, do during allocation
//Backend::updateTextureGPUMemoryUsage(0, _size);
@ -165,8 +170,8 @@ GLTexture::GLTexture(const Texture& texture, GLuint id, bool transferrable) :
}
// Create the texture and copy from the original higher resolution version
GLTexture::GLTexture(const gpu::Texture& texture, GLuint id, GLTexture* originalTexture) :
GLTexture(texture, id, originalTexture, originalTexture->_transferrable)
GLTexture::GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const gpu::Texture& texture, GLuint id, GLTexture* originalTexture) :
GLTexture(backend, texture, id, originalTexture, originalTexture->_transferrable)
{
Q_ASSERT(_minMip >= originalTexture->_minMip);
// Set the GPU object last because that implicitly destroys the originalTexture object
@ -188,11 +193,11 @@ GLTexture::~GLTexture() {
}
if (_id) {
glDeleteTextures(1, &_id);
const_cast<GLuint&>(_id) = 0;
Backend::decrementTextureGPUCount();
auto backend = _backend.lock();
if (backend) {
backend->releaseTexture(_id, _size);
}
}
Backend::updateTextureGPUMemoryUsage(_size, 0);
Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0);
}

View file

@ -10,6 +10,7 @@
#include "GLShared.h"
#include "GLTextureTransfer.h"
#include "GLBackend.h"
namespace gpu { namespace gl {
@ -24,7 +25,7 @@ public:
static std::shared_ptr<GLTextureTransferHelper> _textureTransferHelper;
template <typename GLTextureType>
static GLTextureType* sync(const TexturePointer& texturePointer, bool needTransfer) {
static GLTextureType* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) {
const Texture& texture = *texturePointer;
if (!texture.isDefined()) {
// NO texture definition yet so let's avoid thinking
@ -38,7 +39,7 @@ public:
// for easier use of immutable storage)
if (!object || object->isInvalid()) {
// This automatically any previous texture
object = new GLTextureType(texture, needTransfer);
object = new GLTextureType(backend.shared_from_this(), texture, needTransfer);
if (!object->_transferrable) {
object->createTexture();
object->_contentStamp = texture.getDataStamp();
@ -62,7 +63,7 @@ public:
if (object->isOverMaxMemory() && texturePointer->incremementMinMip()) {
// WARNING, this code path will essentially `delete this`,
// so no dereferencing of this instance should be done past this point
object = new GLTextureType(texture, object);
object = new GLTextureType(backend.shared_from_this(), texture, object);
_textureTransferHelper->transferTexture(texturePointer);
}
} else if (object->isOutdated()) {
@ -75,13 +76,13 @@ public:
}
template <typename GLTextureType>
static GLuint getId(const TexturePointer& texture, bool shouldSync) {
static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) {
if (!texture) {
return 0;
}
GLTextureType* object { nullptr };
if (shouldSync) {
object = sync<GLTextureType>(texture, shouldSync);
object = sync<GLTextureType>(backend, texture, shouldSync);
} else {
object = Backend::getGPUObject<GLTextureType>(*texture);
}
@ -92,11 +93,13 @@ public:
GLuint result = object->_id;
// Don't return textures that are in transfer state
if ((object->getSyncState() != GLSyncState::Idle) ||
// Don't return transferrable textures that have never completed transfer
(!object->_transferrable || 0 != object->_transferCount)) {
// Will be either 0 or the original texture being downsampled.
result = object->_downsampleSource._texture;
if (shouldSync) {
if ((object->getSyncState() != GLSyncState::Idle) ||
// Don't return transferrable textures that have never completed transfer
(!object->_transferrable || 0 != object->_transferCount)) {
// Will be either 0 or the original texture being downsampled.
result = object->_downsampleSource._texture;
}
}
return result;
@ -123,10 +126,12 @@ public:
struct DownsampleSource {
using Pointer = std::shared_ptr<DownsampleSource>;
DownsampleSource() : _texture(0), _minMip(0), _maxMip(0) {}
DownsampleSource(GLTexture* originalTexture);
DownsampleSource(const std::weak_ptr<gl::GLBackend>& backend) : _backend(backend), _size(0), _texture(0), _minMip(0), _maxMip(0) {}
DownsampleSource(const std::weak_ptr<gl::GLBackend>& backend, GLTexture* originalTexture);
~DownsampleSource();
void reset() const { const_cast<GLuint&>(_texture) = 0; }
const std::weak_ptr<gl::GLBackend>& _backend;
const GLuint _size { 0 };
const GLuint _texture { 0 };
const uint16 _minMip { 0 };
const uint16 _maxMip { 0 };
@ -168,8 +173,8 @@ protected:
const GLuint _size { 0 }; // true size as reported by the gl api
std::atomic<GLSyncState> _syncState { GLSyncState::Idle };
GLTexture(const Texture& texture, GLuint id, bool transferrable);
GLTexture(const Texture& texture, GLuint id, GLTexture* originalTexture);
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id, bool transferrable);
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id, GLTexture* originalTexture);
void setSyncState(GLSyncState syncState) { _syncState = syncState; }
uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; }
@ -188,7 +193,7 @@ protected:
private:
GLTexture(const gpu::Texture& gpuTexture, GLuint id, GLTexture* originalTexture, bool transferrable);
GLTexture(const std::weak_ptr<GLBackend>& backend, const gpu::Texture& gpuTexture, GLuint id, GLTexture* originalTexture, bool transferrable);
friend class GLTextureTransferHelper;
friend class GLBackend;

View file

@ -18,7 +18,7 @@ Q_LOGGING_CATEGORY(gpugl41logging, "hifi.gpu.gl41")
using namespace gpu;
using namespace gpu::gl41;
void GL41Backend::do_draw(Batch& batch, size_t paramOffset) {
void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) {
Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint;
GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType];
uint32 numVertices = batch._params[paramOffset + 1]._uint;
@ -43,7 +43,7 @@ void GL41Backend::do_draw(Batch& batch, size_t paramOffset) {
(void) CHECK_GL_ERROR();
}
void GL41Backend::do_drawIndexed(Batch& batch, size_t paramOffset) {
void GL41Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) {
Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint;
GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType];
uint32 numIndices = batch._params[paramOffset + 1]._uint;
@ -72,7 +72,7 @@ void GL41Backend::do_drawIndexed(Batch& batch, size_t paramOffset) {
(void) CHECK_GL_ERROR();
}
void GL41Backend::do_drawInstanced(Batch& batch, size_t paramOffset) {
void GL41Backend::do_drawInstanced(const Batch& batch, size_t paramOffset) {
GLint numInstances = batch._params[paramOffset + 4]._uint;
Primitive primitiveType = (Primitive)batch._params[paramOffset + 3]._uint;
GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType];
@ -108,7 +108,7 @@ void glbackend_glDrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsize
#endif
}
void GL41Backend::do_drawIndexedInstanced(Batch& batch, size_t paramOffset) {
void GL41Backend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) {
GLint numInstances = batch._params[paramOffset + 4]._uint;
GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 3]._uint];
uint32 numIndices = batch._params[paramOffset + 2]._uint;
@ -143,7 +143,7 @@ void GL41Backend::do_drawIndexedInstanced(Batch& batch, size_t paramOffset) {
}
void GL41Backend::do_multiDrawIndirect(Batch& batch, size_t paramOffset) {
void GL41Backend::do_multiDrawIndirect(const Batch& batch, size_t paramOffset) {
#if (GPU_INPUT_PROFILE == GPU_CORE_43)
uint commandCount = batch._params[paramOffset + 0]._uint;
GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 1]._uint];
@ -159,7 +159,7 @@ void GL41Backend::do_multiDrawIndirect(Batch& batch, size_t paramOffset) {
}
void GL41Backend::do_multiDrawIndexedIndirect(Batch& batch, size_t paramOffset) {
void GL41Backend::do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) {
#if (GPU_INPUT_PROFILE == GPU_CORE_43)
uint commandCount = batch._params[paramOffset + 0]._uint;
GLenum mode = gl::PRIMITIVE_TO_GL[(Primitive)batch._params[paramOffset + 1]._uint];

View file

@ -40,8 +40,8 @@ public:
using Parent = gpu::gl::GLTexture;
GLuint allocate();
public:
GL41Texture(const Texture& buffer, bool transferrable);
GL41Texture(const Texture& buffer, GL41Texture* original);
GL41Texture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& buffer, bool transferrable);
GL41Texture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& buffer, GL41Texture* original);
protected:
void transferMip(uint16_t mipLevel, uint8_t face = 0) const;
@ -68,12 +68,12 @@ protected:
gl::GLQuery* syncGPUObject(const Query& query) override;
// Draw Stage
void do_draw(Batch& batch, size_t paramOffset) override;
void do_drawIndexed(Batch& batch, size_t paramOffset) override;
void do_drawInstanced(Batch& batch, size_t paramOffset) override;
void do_drawIndexedInstanced(Batch& batch, size_t paramOffset) override;
void do_multiDrawIndirect(Batch& batch, size_t paramOffset) override;
void do_multiDrawIndexedIndirect(Batch& batch, size_t paramOffset) override;
void do_draw(const Batch& batch, size_t paramOffset) override;
void do_drawIndexed(const Batch& batch, size_t paramOffset) override;
void do_drawInstanced(const Batch& batch, size_t paramOffset) override;
void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) override;
void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) override;
void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) override;
// Input Stage
void updateInput() override;
@ -85,7 +85,7 @@ protected:
void resetTransformStage();
// Output stage
void do_blit(Batch& batch, size_t paramOffset) override;
void do_blit(const Batch& batch, size_t paramOffset) override;
};
} }

View file

@ -8,55 +8,60 @@
#include "GL41Backend.h"
#include "../gl/GLBuffer.h"
namespace gpu {
namespace gl41 {
class GL41Buffer : public gl::GLBuffer {
using Parent = gpu::gl::GLBuffer;
static GLuint allocate() {
GLuint result;
glGenBuffers(1, &result);
return result;
}
public:
GL41Buffer(const std::weak_ptr<gl::GLBackend>& backend, const Buffer& buffer, GL41Buffer* original) : Parent(backend, buffer, allocate()) {
glBindBuffer(GL_ARRAY_BUFFER, _buffer);
glBufferData(GL_ARRAY_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
if (original && original->_size) {
glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer);
glBindBuffer(GL_COPY_READ_BUFFER, original->_buffer);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, original->_size);
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
(void)CHECK_GL_ERROR();
}
Backend::setGPUObject(buffer, this);
}
void transfer() override {
glBindBuffer(GL_ARRAY_BUFFER, _buffer);
(void)CHECK_GL_ERROR();
Size offset;
Size size;
Size currentPage { 0 };
auto data = _gpuObject._renderSysmem.readData();
while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) {
glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset);
(void)CHECK_GL_ERROR();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
(void)CHECK_GL_ERROR();
_gpuObject._renderPages._flags &= ~PageManager::DIRTY;
}
};
}
}
using namespace gpu;
using namespace gpu::gl41;
class GL41Buffer : public gl::GLBuffer {
using Parent = gpu::gl::GLBuffer;
static GLuint allocate() {
GLuint result;
glGenBuffers(1, &result);
return result;
}
public:
GL41Buffer(const Buffer& buffer, GL41Buffer* original) : Parent(buffer, allocate()) {
glBindBuffer(GL_ARRAY_BUFFER, _buffer);
glBufferData(GL_ARRAY_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
if (original && original->_size) {
glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer);
glBindBuffer(GL_COPY_READ_BUFFER, original->_buffer);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, original->_size);
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
(void)CHECK_GL_ERROR();
}
Backend::setGPUObject(buffer, this);
}
void transfer() override {
glBindBuffer(GL_ARRAY_BUFFER, _buffer);
(void)CHECK_GL_ERROR();
Size offset;
Size size;
Size currentPage { 0 };
auto data = _gpuObject.getSysmem().readData();
while (_gpuObject.getNextTransferBlock(offset, size, currentPage)) {
glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset);
(void)CHECK_GL_ERROR();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
(void)CHECK_GL_ERROR();
_gpuObject._flags &= ~Buffer::DIRTY;
}
};
GLuint GL41Backend::getBufferID(const Buffer& buffer) {
return GL41Buffer::getId<GL41Buffer>(buffer);
return GL41Buffer::getId<GL41Buffer>(*this, buffer);
}
gl::GLBuffer* GL41Backend::syncGPUObject(const Buffer& buffer) {
return GL41Buffer::sync<GL41Buffer>(buffer);
return GL41Buffer::sync<GL41Buffer>(*this, buffer);
}

View file

@ -56,7 +56,7 @@ public:
for (auto& b : _gpuObject.getRenderBuffers()) {
surface = b._texture;
if (surface) {
gltexture = gl::GLTexture::sync<GL41Backend::GL41Texture>(surface, false); // Grab the gltexture and don't transfer
gltexture = gl::GLTexture::sync<GL41Backend::GL41Texture>(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer
} else {
gltexture = nullptr;
}
@ -83,7 +83,7 @@ public:
if (_gpuObject.getDepthStamp() != _depthStamp) {
auto surface = _gpuObject.getDepthStencilBuffer();
if (_gpuObject.hasDepthStencil() && surface) {
gltexture = gl::GLTexture::sync<GL41Backend::GL41Texture>(surface, false); // Grab the gltexture and don't transfer
gltexture = gl::GLTexture::sync<GL41Backend::GL41Texture>(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer
}
if (gltexture) {
@ -115,19 +115,19 @@ public:
public:
GL41Framebuffer(const gpu::Framebuffer& framebuffer)
: Parent(framebuffer, allocate()) { }
GL41Framebuffer(const std::weak_ptr<gl::GLBackend>& backend, const gpu::Framebuffer& framebuffer)
: Parent(backend, framebuffer, allocate()) { }
};
gl::GLFramebuffer* GL41Backend::syncGPUObject(const Framebuffer& framebuffer) {
return GL41Framebuffer::sync<GL41Framebuffer>(framebuffer);
return GL41Framebuffer::sync<GL41Framebuffer>(*this, framebuffer);
}
GLuint GL41Backend::getFramebufferID(const FramebufferPointer& framebuffer) {
return framebuffer ? GL41Framebuffer::getId<GL41Framebuffer>(*framebuffer) : 0;
return framebuffer ? GL41Framebuffer::getId<GL41Framebuffer>(*this, *framebuffer) : 0;
}
void GL41Backend::do_blit(Batch& batch, size_t paramOffset) {
void GL41Backend::do_blit(const Batch& batch, size_t paramOffset) {
auto srcframebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint);
Vec4i srcvp;
for (auto i = 0; i < 4; ++i) {

View file

@ -24,14 +24,14 @@ public:
return result;
}
GL41Query(const Query& query)
: Parent(query, allocateQuery(), allocateQuery()) { }
GL41Query(const std::weak_ptr<gl::GLBackend>& backend, const Query& query)
: Parent(backend, query, allocateQuery(), allocateQuery()) { }
};
gl::GLQuery* GL41Backend::syncGPUObject(const Query& query) {
return GL41Query::sync<GL41Query>(query);
return GL41Query::sync<GL41Query>(*this, query);
}
GLuint GL41Backend::getQueryID(const QueryPointer& query) {
return GL41Query::getId<GL41Query>(query);
return GL41Query::getId<GL41Query>(*this, query);
}

View file

@ -30,16 +30,16 @@ GLuint GL41Texture::allocate() {
}
GLuint GL41Backend::getTextureID(const TexturePointer& texture, bool transfer) {
return GL41Texture::getId<GL41Texture>(texture, transfer);
return GL41Texture::getId<GL41Texture>(*this, texture, transfer);
}
gl::GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texture, bool transfer) {
return GL41Texture::sync<GL41Texture>(texture, transfer);
return GL41Texture::sync<GL41Texture>(*this, texture, transfer);
}
GL41Texture::GL41Texture(const Texture& texture, bool transferrable) : gl::GLTexture(texture, allocate(), transferrable) {}
GL41Texture::GL41Texture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, bool transferrable) : gl::GLTexture(backend, texture, allocate(), transferrable) {}
GL41Texture::GL41Texture(const Texture& texture, GL41Texture* original) : gl::GLTexture(texture, allocate(), original) {}
GL41Texture::GL41Texture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GL41Texture* original) : gl::GLTexture(backend, texture, allocate(), original) {}
void GL41Backend::GL41Texture::withPreservedTexture(std::function<void()> f) const {
GLint boundTex = -1;

Some files were not shown because too many files have changed in this diff Show more