diff --git a/android/libraries/oculus/src/main/assets/shaders/present.frag b/android/libraries/oculus/src/main/assets/shaders/present.frag new file mode 100644 index 0000000000..f5d96932f8 --- /dev/null +++ b/android/libraries/oculus/src/main/assets/shaders/present.frag @@ -0,0 +1,39 @@ +#version 320 es + +precision highp float; +precision highp sampler2D; + +layout(location = 0) in vec4 vTexCoordLR; + +layout(location = 0) out vec4 FragColorL; +layout(location = 1) out vec4 FragColorR; + +uniform sampler2D sampler; + +// https://software.intel.com/en-us/node/503873 + +// sRGB ====> Linear +vec3 color_sRGBToLinear(vec3 srgb) { + return mix(pow((srgb + vec3(0.055)) / vec3(1.055), vec3(2.4)), srgb / vec3(12.92), vec3(lessThanEqual(srgb, vec3(0.04045)))); +} + +vec4 color_sRGBAToLinear(vec4 srgba) { + return vec4(color_sRGBToLinear(srgba.xyz), srgba.w); +} + +// Linear ====> sRGB +vec3 color_LinearTosRGB(vec3 lrgb) { + return mix(vec3(1.055) * pow(vec3(lrgb), vec3(0.41666)) - vec3(0.055), vec3(lrgb) * vec3(12.92), vec3(lessThan(lrgb, vec3(0.0031308)))); +} + +vec4 color_LinearTosRGBA(vec4 lrgba) { + return vec4(color_LinearTosRGB(lrgba.xyz), lrgba.w); +} + +// FIXME switch to texelfetch for getting from the source texture? +void main() { + //FragColorL = color_LinearTosRGBA(texture(sampler, vTexCoordLR.xy)); + //FragColorR = color_LinearTosRGBA(texture(sampler, vTexCoordLR.zw)); + FragColorL = texture(sampler, vTexCoordLR.xy); + FragColorR = texture(sampler, vTexCoordLR.zw); +} diff --git a/android/libraries/oculus/src/main/assets/shaders/present.vert b/android/libraries/oculus/src/main/assets/shaders/present.vert new file mode 100644 index 0000000000..dfd6b1412f --- /dev/null +++ b/android/libraries/oculus/src/main/assets/shaders/present.vert @@ -0,0 +1,21 @@ +#version 320 es + +layout(location = 0) out vec4 vTexCoordLR; + +void main(void) { + const float depth = 0.0; + const vec4 UNIT_QUAD[4] = vec4[4]( + vec4(-1.0, -1.0, depth, 1.0), + vec4(1.0, -1.0, depth, 1.0), + vec4(-1.0, 1.0, depth, 1.0), + vec4(1.0, 1.0, depth, 1.0) + ); + vec4 pos = UNIT_QUAD[gl_VertexID]; + gl_Position = pos; + vTexCoordLR.xy = pos.xy; + vTexCoordLR.xy += 1.0; + vTexCoordLR.y *= 0.5; + vTexCoordLR.x *= 0.25; + vTexCoordLR.zw = vTexCoordLR.xy; + vTexCoordLR.z += 0.5; +} diff --git a/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java b/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java index 8ee22749c9..19865e7751 100644 --- a/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java +++ b/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java @@ -7,6 +7,7 @@ // package io.highfidelity.oculus; +import android.content.res.AssetManager; import android.os.Bundle; import android.util.Log; import android.view.Surface; @@ -24,7 +25,7 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca private static final String TAG = OculusMobileActivity.class.getSimpleName(); static { System.loadLibrary("oculusMobile"); } - private native void nativeOnCreate(); + private native void nativeOnCreate(AssetManager assetManager); private native static void nativeOnResume(); private native static void nativeOnPause(); private native static void nativeOnSurfaceChanged(Surface s); @@ -53,7 +54,7 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca mView = new SurfaceView(this); mView.getHolder().addCallback(this); - nativeOnCreate(); + nativeOnCreate(getAssets()); questNativeOnCreate(); } @@ -81,7 +82,7 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca Log.w(TAG, "QQQ onResume"); super.onResume(); //Reconnect the global reference back to handler - nativeOnCreate(); + nativeOnCreate(getAssets()); questNativeOnResume(); nativeOnResume(); diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index da306f911b..cd0f290da4 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 @@ -31,6 +31,8 @@ Rectangle { property string title: "Audio Settings" property int switchHeight: 16 property int switchWidth: 40 + readonly property real verticalScrollWidth: 10 + readonly property real verticalScrollShaft: 8 signal sendToScript(var message); color: hifi.colors.baseGray; @@ -42,7 +44,7 @@ Rectangle { property bool isVR: AudioScriptingInterface.context === "VR" - property real rightMostInputLevelPos: 450 + property real rightMostInputLevelPos: 440 //placeholder for control sizes and paddings //recalculates dynamically in case of UI size is changed QtObject { @@ -60,8 +62,8 @@ Rectangle { id: bar spacing: 0 width: parent.width - height: 42 - currentIndex: isVR ? 1 : 0 + height: 28; + currentIndex: isVR ? 1 : 0; AudioControls.AudioTabButton { height: parent.height @@ -92,25 +94,74 @@ Rectangle { Component.onCompleted: enablePeakValues(); - Column { - id: column - spacing: 12; - anchors.top: bar.bottom - anchors.bottom: parent.bottom - anchors.bottomMargin: 5 + Flickable { + id: flickView; + anchors.top: bar.bottom; + anchors.left: parent.left; + anchors.bottom: parent.bottom; width: parent.width; + contentWidth: parent.width; + contentHeight: contentItem.childrenRect.height; + boundsBehavior: Flickable.DragOverBounds; + flickableDirection: Flickable.VerticalFlick; + property bool isScrolling: (contentHeight - height) > 10 ? true : false; + clip: true; - Separator { } + ScrollBar.vertical: ScrollBar { + policy: flickView.isScrolling ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff; + parent: flickView.parent; + anchors.top: flickView.top; + anchors.right: flickView.right; + anchors.bottom: flickView.bottom; + anchors.rightMargin: -verticalScrollWidth; //compensate flickView's right margin + background: Item { + implicitWidth: verticalScrollWidth; + Rectangle { + color: hifi.colors.darkGray30; + radius: 4; + anchors { + fill: parent; + topMargin: -1; // Finesse size + bottomMargin: -2; + } + } + } + contentItem: Item { + implicitWidth: verticalScrollShaft; + Rectangle { + radius: verticalScrollShaft/2; + color: hifi.colors.white30; + anchors { + fill: parent; + leftMargin: 2; // Finesse size and position. + topMargin: 1; + bottomMargin: 1; + } + } + } + } - RowLayout { + Separator { + id: firstSeparator; + anchors.top: parent.top; + } + + Item { + id: switchesContainer; x: 2 * margins.paddings; width: parent.width; + // switch heights + 2 * top margins + height: (root.switchHeight) * 3 + 48; + anchors.top: firstSeparator.bottom; + anchors.topMargin: 10; // mute is in its own row - ColumnLayout { - id: columnOne - spacing: 24; - x: margins.paddings + Item { + id: switchContainer; + x: margins.paddings; + width: parent.width / 2; + height: parent.height; + anchors.left: parent.left; HifiControlsUit.Switch { id: muteMic; height: root.switchHeight; @@ -129,8 +180,12 @@ Rectangle { } HifiControlsUit.Switch { + id: noiseReductionSwitch; height: root.switchHeight; switchWidth: root.switchWidth; + anchors.top: muteMic.bottom; + anchors.topMargin: 24 + anchors.left: parent.left labelTextOn: "Noise Reduction"; backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.noiseReduction; @@ -144,6 +199,9 @@ Rectangle { id: pttSwitch height: root.switchHeight; switchWidth: root.switchWidth; + anchors.top: noiseReductionSwitch.bottom + anchors.topMargin: 24 + anchors.left: parent.left labelTextOn: qsTr("Push To Talk (T)"); backgroundOnColor: "#E3E3E3"; checked: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD; @@ -164,12 +222,18 @@ Rectangle { } } - ColumnLayout { - spacing: 24; + Item { + id: additionalSwitchContainer + width: switchContainer.width - margins.paddings; + height: parent.height; + anchors.top: parent.top + anchors.left: switchContainer.right; HifiControlsUit.Switch { id: warnMutedSwitch height: root.switchHeight; switchWidth: root.switchWidth; + anchors.top: parent.top + anchors.left: parent.left labelTextOn: qsTr("Warn when muted"); backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.warnWhenMuted; @@ -184,6 +248,9 @@ Rectangle { id: audioLevelSwitch height: root.switchHeight; switchWidth: root.switchWidth; + anchors.top: warnMutedSwitch.bottom + anchors.topMargin: 24 + anchors.left: parent.left labelTextOn: qsTr("Audio Level Meter"); backgroundOnColor: "#E3E3E3"; checked: AvatarInputs.showAudioTools; @@ -197,6 +264,9 @@ Rectangle { id: stereoInput; height: root.switchHeight; switchWidth: root.switchWidth; + anchors.top: audioLevelSwitch.bottom + anchors.topMargin: 24 + anchors.left: parent.left labelTextOn: qsTr("Stereo input"); backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.isStereoInput; @@ -210,17 +280,20 @@ Rectangle { } Item { - anchors.left: parent.left + id: pttTextContainer + anchors.top: switchesContainer.bottom; + anchors.topMargin: 10; + anchors.left: parent.left; width: rightMostInputLevelPos; height: pttText.height; RalewayRegular { - id: pttText + id: pttText; x: margins.paddings; color: hifi.colors.white; width: rightMostInputLevelPos; height: paintedHeight; wrapMode: Text.WordWrap; - font.italic: true + font.italic: true; size: 16; text: (bar.currentIndex === 0) ? qsTr("Press and hold the button \"T\" to talk.") : @@ -228,28 +301,35 @@ Rectangle { } } - Separator { } + Separator { + id: secondSeparator; + anchors.top: pttTextContainer.bottom; + anchors.topMargin: 10; + } Item { + id: inputDeviceHeader x: margins.paddings; - width: parent.width - margins.paddings*2 - height: 36 + width: parent.width - margins.paddings*2; + height: 36; + anchors.top: secondSeparator.bottom; + anchors.topMargin: 10; HiFiGlyphs { - width: margins.sizeCheckBox + width: margins.sizeCheckBox; text: hifi.glyphs.mic; color: hifi.colors.white; - anchors.left: parent.left - anchors.leftMargin: -size/4 //the glyph has empty space at left about 25% + anchors.left: parent.left; + anchors.leftMargin: -size/4; //the glyph has empty space at left about 25% anchors.verticalCenter: parent.verticalCenter; size: 30; } RalewayRegular { anchors.verticalCenter: parent.verticalCenter; - width: margins.sizeText + margins.sizeLevel - anchors.left: parent.left - anchors.leftMargin: margins.sizeCheckBox + width: margins.sizeText + margins.sizeLevel; + anchors.left: parent.left; + anchors.leftMargin: margins.sizeCheckBox; size: 16; color: hifi.colors.white; text: qsTr("Choose input device"); @@ -257,12 +337,13 @@ Rectangle { } ListView { - id: inputView - width: parent.width - margins.paddings*2 + id: inputView; + width: parent.width - margins.paddings*2; + anchors.top: inputDeviceHeader.bottom; + anchors.topMargin: 10; x: margins.paddings - height: Math.min(150, contentHeight); + height: contentHeight; spacing: 4; - snapMode: ListView.SnapToItem; clip: true; model: AudioScriptingInterface.devices.input; delegate: Item { @@ -302,16 +383,26 @@ Rectangle { } } AudioControls.LoopbackAudio { + id: loopbackAudio x: margins.paddings + anchors.top: inputView.bottom; + anchors.topMargin: 10; visible: (bar.currentIndex === 1 && isVR) || (bar.currentIndex === 0 && !isVR); anchors { left: parent.left; leftMargin: margins.paddings } } - Separator {} + Separator { + id: thirdSeparator; + anchors.top: loopbackAudio.bottom; + anchors.topMargin: 10; + } Item { + id: outputDeviceHeader; + anchors.topMargin: 10; + anchors.top: thirdSeparator.bottom; x: margins.paddings; width: parent.width - margins.paddings*2 height: 36 @@ -341,9 +432,10 @@ Rectangle { id: outputView width: parent.width - margins.paddings*2 x: margins.paddings - height: Math.min(360 - inputView.height, contentHeight); + height: contentHeight; + anchors.top: outputDeviceHeader.bottom; + anchors.topMargin: 10; spacing: 4; - snapMode: ListView.SnapToItem; clip: true; model: AudioScriptingInterface.devices.output; delegate: Item { @@ -372,6 +464,8 @@ Rectangle { Item { id: gainContainer x: margins.paddings; + anchors.top: outputView.bottom; + anchors.topMargin: 10; width: parent.width - margins.paddings*2 height: gainSliderTextMetrics.height @@ -430,7 +524,10 @@ Rectangle { } AudioControls.PlaySampleSound { + id: playSampleSound x: margins.paddings + anchors.top: gainContainer.bottom; + anchors.topMargin: 10; visible: (bar.currentIndex === 1 && isVR) || (bar.currentIndex === 0 && !isVR); diff --git a/interface/resources/qml/hifi/audio/AudioTabButton.qml b/interface/resources/qml/hifi/audio/AudioTabButton.qml index 32331ccb6e..c81377e524 100644 --- a/interface/resources/qml/hifi/audio/AudioTabButton.qml +++ b/interface/resources/qml/hifi/audio/AudioTabButton.qml @@ -16,7 +16,7 @@ import stylesUit 1.0 TabButton { id: control - font.pixelSize: height / 2 + font.pixelSize: 14 HifiConstants { id: hifi; } diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index 626ac4da65..e159344d5c 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -2248,6 +2248,7 @@ Item { if (sendAssetStep.selectedRecipientUserName === "") { console.log("SendAsset: Script didn't specify a recipient username!"); sendAssetHome.visible = false; + root.nextActiveView = 'paymentFailure'; return; } diff --git a/interface/resources/qml/hifi/dialogs/Audio.qml b/interface/resources/qml/hifi/dialogs/Audio.qml deleted file mode 100644 index 4ce9e14c42..0000000000 --- a/interface/resources/qml/hifi/dialogs/Audio.qml +++ /dev/null @@ -1,27 +0,0 @@ -// -// Audio.qml -// -// Created by Zach Pomerantz on 6/12/2017 -// Copyright 2017 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 "../../windows" -import "../audio" - -ScrollingWindow { - id: root; - - resizable: true; - destroyOnHidden: true; - width: 400; - height: 577; - minSize: Qt.vector2d(400, 500); - - Audio { id: audio; width: root.width } - - objectName: "AudioDialog"; - title: audio.title; -} diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 810e21daf5..394c07e842 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -270,10 +270,14 @@ Menu::Menu() { // Settings > Audio... action = addActionToQMenuAndActionHash(settingsMenu, "Audio..."); connect(action, &QAction::triggered, [] { - static const QUrl widgetUrl("hifi/dialogs/Audio.qml"); static const QUrl tabletUrl("hifi/audio/Audio.qml"); - static const QString name("AudioDialog"); - qApp->showDialog(widgetUrl, tabletUrl, name); + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + auto hmd = DependencyManager::get(); + tablet->pushOntoStack(tabletUrl); + + if (!hmd->getShouldShowTablet()) { + hmd->toggleShouldShowTablet(); + } }); // Settings > Graphics... diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 9828a8beda..f55f5919f5 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -109,7 +109,7 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() { return Parent::internalActivate(); } -void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { +void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPointer& compositeFramebuffer) { #if defined(Q_OS_ANDROID) auto& virtualPadManager = VirtualPad::Manager::instance(); if(virtualPadManager.getLeftVirtualPad()->isShown()) { @@ -121,7 +121,7 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { render([&](gpu::Batch& batch) { batch.enableStereo(false); - batch.setFramebuffer(_compositeFramebuffer); + batch.setFramebuffer(compositeFramebuffer); batch.resetViewTransform(); batch.setProjectionTransform(mat4()); batch.setPipeline(_cursorPipeline); @@ -140,7 +140,7 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { }); } #endif - Parent::compositeExtra(); + Parent::compositeExtra(compositeFramebuffer); } static const uint32_t MIN_THROTTLE_CHECK_FRAMES = 60; diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index cc304c19c2..d4c321a571 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -33,7 +33,7 @@ public: virtual bool isThrottled() const override; - virtual void compositeExtra() override; + virtual void compositeExtra(const gpu::FramebufferPointer&) override; virtual void pluginUpdate() override {}; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index c536e6b6e2..5bc84acc6a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -379,14 +379,6 @@ void OpenGLDisplayPlugin::customizeContext() { scissorState->setDepthTest(gpu::State::DepthTest(false)); scissorState->setScissorEnable(true); - { -#ifdef Q_OS_ANDROID - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB); -#else - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); -#endif - _simplePipeline = gpu::Pipeline::create(program, scissorState); - } { #ifdef Q_OS_ANDROID gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB); @@ -396,29 +388,59 @@ void OpenGLDisplayPlugin::customizeContext() { _presentPipeline = gpu::Pipeline::create(program, scissorState); } + + // HUD operator { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); - _hudPipeline = gpu::Pipeline::create(program, blendState); - } - { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX); - _mirrorHUDPipeline = gpu::Pipeline::create(program, blendState); + gpu::PipelinePointer hudPipeline; + { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); + hudPipeline = gpu::Pipeline::create(program, blendState); + } + + gpu::PipelinePointer hudMirrorPipeline; + { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX); + hudMirrorPipeline = gpu::Pipeline::create(program, blendState); + } + + + _hudOperator = [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, const gpu::FramebufferPointer& compositeFramebuffer, bool mirror) { + auto hudStereo = isStereo(); + auto hudCompositeFramebufferSize = compositeFramebuffer->getSize(); + std::array hudEyeViewports; + for_each_eye([&](Eye eye) { + hudEyeViewports[eye] = eyeViewport(eye); + }); + if (hudPipeline && hudTexture) { + batch.enableStereo(false); + batch.setPipeline(mirror ? hudMirrorPipeline : hudPipeline); + batch.setResourceTexture(0, hudTexture); + if (hudStereo) { + for_each_eye([&](Eye eye) { + batch.setViewportTransform(hudEyeViewports[eye]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + } else { + batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } + } + }; + } + { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTransformedTexture); _cursorPipeline = gpu::Pipeline::create(program, blendState); } } - updateCompositeFramebuffer(); } void OpenGLDisplayPlugin::uncustomizeContext() { _presentPipeline.reset(); _cursorPipeline.reset(); - _hudPipeline.reset(); - _mirrorHUDPipeline.reset(); - _compositeFramebuffer.reset(); + _hudOperator = DEFAULT_HUD_OPERATOR; withPresentThreadLock([&] { _currentFrame.reset(); _lastFrame = nullptr; @@ -510,24 +532,16 @@ void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const { }); } -void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor) { - renderFromTexture(batch, texture, viewport, scissor, nullptr); -} -void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) { - auto fbo = gpu::FramebufferPointer(); +void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& destFbo, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) { batch.enableStereo(false); batch.resetViewTransform(); - batch.setFramebuffer(fbo); + batch.setFramebuffer(destFbo); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); batch.setStateScissorRect(scissor); batch.setViewportTransform(viewport); batch.setResourceTexture(0, texture); -#ifndef USE_GLES batch.setPipeline(_presentPipeline); -#else - batch.setPipeline(_simplePipeline); -#endif batch.draw(gpu::TRIANGLE_STRIP, 4); if (copyFbo) { gpu::Vec4i copyFboRect(0, 0, copyFbo->getWidth(), copyFbo->getHeight()); @@ -553,7 +567,7 @@ void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::Textur batch.setViewportTransform(copyFboRect); batch.setStateScissorRect(copyFboRect); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, {0.0f, 0.0f, 0.0f, 1.0f}); - batch.blit(fbo, sourceRect, copyFbo, copyRect); + batch.blit(destFbo, sourceRect, copyFbo, copyRect); } } @@ -581,41 +595,14 @@ void OpenGLDisplayPlugin::updateFrameData() { }); } -std::function OpenGLDisplayPlugin::getHUDOperator() { - auto hudPipeline = _hudPipeline; - auto hudMirrorPipeline = _mirrorHUDPipeline; - auto hudStereo = isStereo(); - auto hudCompositeFramebufferSize = _compositeFramebuffer->getSize(); - std::array hudEyeViewports; - for_each_eye([&](Eye eye) { - hudEyeViewports[eye] = eyeViewport(eye); - }); - return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, bool mirror) { - if (hudPipeline && hudTexture) { - batch.enableStereo(false); - batch.setPipeline(mirror ? hudMirrorPipeline : hudPipeline); - batch.setResourceTexture(0, hudTexture); - if (hudStereo) { - for_each_eye([&](Eye eye) { - batch.setViewportTransform(hudEyeViewports[eye]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); - } else { - batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize)); - batch.draw(gpu::TRIANGLE_STRIP, 4); - } - } - }; -} - -void OpenGLDisplayPlugin::compositePointer() { +void OpenGLDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compositeFramebuffer) { auto& cursorManager = Cursor::Manager::instance(); const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; auto cursorTransform = DependencyManager::get()->getReticleTransform(glm::mat4()); render([&](gpu::Batch& batch) { batch.enableStereo(false); batch.setProjectionTransform(mat4()); - batch.setFramebuffer(_compositeFramebuffer); + batch.setFramebuffer(compositeFramebuffer); batch.setPipeline(_cursorPipeline); batch.setResourceTexture(0, cursorData.texture); batch.resetViewTransform(); @@ -626,34 +613,13 @@ void OpenGLDisplayPlugin::compositePointer() { batch.draw(gpu::TRIANGLE_STRIP, 4); }); } else { - batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize())); + batch.setViewportTransform(ivec4(uvec2(0), compositeFramebuffer->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() { - updateCompositeFramebuffer(); - - { - PROFILE_RANGE_EX(render_detail, "compositeScene", 0xff0077ff, (uint64_t)presentCount()) - compositeScene(); - } - +void OpenGLDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& compositeFramebuffer) { #ifdef HIFI_ENABLE_NSIGHT_DEBUG if (false) // do not draw the HUD if running nsight debug #endif @@ -667,23 +633,35 @@ void OpenGLDisplayPlugin::compositeLayers() { { PROFILE_RANGE_EX(render_detail, "compositeExtra", 0xff0077ff, (uint64_t)presentCount()) - compositeExtra(); + compositeExtra(compositeFramebuffer); } // Draw the pointer last so it's on top of everything auto compositorHelper = DependencyManager::get(); if (compositorHelper->getReticleVisible()) { PROFILE_RANGE_EX(render_detail, "compositePointer", 0xff0077ff, (uint64_t)presentCount()) - compositePointer(); + compositePointer(compositeFramebuffer); } } -void OpenGLDisplayPlugin::internalPresent() { +void OpenGLDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) { render([&](gpu::Batch& batch) { // Note: _displayTexture must currently be the same size as the display. uvec2 dims = _displayTexture ? uvec2(_displayTexture->getDimensions()) : getSurfacePixels(); auto viewport = ivec4(uvec2(0), dims); - renderFromTexture(batch, _displayTexture ? _displayTexture : _compositeFramebuffer->getRenderBuffer(0), viewport, viewport); + + gpu::TexturePointer finalTexture; + if (_displayTexture) { + finalTexture = _displayTexture; + } else if (compositeFramebuffer) { + finalTexture = compositeFramebuffer->getRenderBuffer(0); + } else { + qCWarning(displayPlugins) << "No valid texture for output"; + } + + if (finalTexture) { + renderFromTexture(batch, finalTexture, viewport, viewport); + } }); swapBuffers(); _presentRate.increment(); @@ -700,7 +678,7 @@ void OpenGLDisplayPlugin::present() { } incrementPresentCount(); - if (_currentFrame) { + if (_currentFrame && _currentFrame->framebuffer) { auto correction = getViewCorrection(); getGLBackend()->setCameraCorrection(correction, _prevRenderView); _prevRenderView = correction * _currentFrame->view; @@ -720,18 +698,18 @@ void OpenGLDisplayPlugin::present() { // Write all layers to a local framebuffer { PROFILE_RANGE_EX(render, "composite", 0xff00ffff, frameId) - compositeLayers(); + compositeLayers(_currentFrame->framebuffer); } // Take the composite framebuffer and send it to the output device { PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId) - internalPresent(); + internalPresent(_currentFrame->framebuffer); } gpu::Backend::freeGPUMemSize.set(gpu::gl::getFreeDedicatedMemory()); } else if (alwaysPresent()) { - internalPresent(); + internalPresent(nullptr); } _movingAveragePresent.addSample((float)(usecTimestampNow() - startPresent)); } @@ -788,7 +766,12 @@ bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) { } QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { - auto size = _compositeFramebuffer->getSize(); + if (!_currentFrame || !_currentFrame->framebuffer) { + return QImage(); + } + + auto compositeFramebuffer = _currentFrame->framebuffer; + auto size = compositeFramebuffer->getSize(); if (isHmd()) { size.x /= 2; } @@ -806,7 +789,7 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { auto glBackend = const_cast(*this).getGLBackend(); QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32); withOtherThreadContext([&] { - glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot); + glBackend->downloadFramebuffer(compositeFramebuffer, ivec4(corner, bestSize), screenshot); }); return screenshot.mirrored(false, true); } @@ -858,7 +841,7 @@ bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) { } ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const { - uvec2 vpSize = _compositeFramebuffer->getSize(); + auto vpSize = glm::uvec2(getRecommendedRenderSize()); vpSize.x /= 2; uvec2 vpPos; if (eye == Eye::Right) { @@ -891,14 +874,6 @@ void OpenGLDisplayPlugin::render(std::function f) { OpenGLDisplayPlugin::~OpenGLDisplayPlugin() { } -void OpenGLDisplayPlugin::updateCompositeFramebuffer() { - auto renderSize = glm::uvec2(getRecommendedRenderSize()); - if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) { - _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y)); - // _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_SRGBA_32, renderSize.x, renderSize.y)); - } -} - void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) { #if !defined(USE_GLES) auto glBackend = const_cast(*this).getGLBackend(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 49a38ecb4c..3c48e8fc48 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -94,14 +94,10 @@ protected: // is not populated virtual bool alwaysPresent() const { return false; } - void updateCompositeFramebuffer(); - virtual QThread::Priority getPresentPriority() { return QThread::HighPriority; } - virtual void compositeLayers(); - virtual void compositeScene(); - virtual std::function getHUDOperator(); - virtual void compositePointer(); - virtual void compositeExtra() {}; + virtual void compositeLayers(const gpu::FramebufferPointer&); + virtual void compositePointer(const gpu::FramebufferPointer&); + virtual void compositeExtra(const gpu::FramebufferPointer&) {}; // These functions must only be called on the presentation thread virtual void customizeContext(); @@ -116,10 +112,10 @@ protected: virtual void deactivateSession() {} // Plugin specific functionality to send the composed scene to the output window or device - virtual void internalPresent(); + virtual void internalPresent(const gpu::FramebufferPointer&); - void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& fbo); - void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor); + + void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& destFbo = nullptr, const gpu::FramebufferPointer& copyFbo = nullptr); virtual void updateFrameData(); virtual glm::mat4 getViewCorrection() { return glm::mat4(); } @@ -142,14 +138,8 @@ protected: gpu::FramePointer _currentFrame; gpu::Frame* _lastFrame { nullptr }; mat4 _prevRenderView; - gpu::FramebufferPointer _compositeFramebuffer; - gpu::PipelinePointer _hudPipeline; - gpu::PipelinePointer _mirrorHUDPipeline; - gpu::ShaderPointer _mirrorHUDPS; - gpu::PipelinePointer _simplePipeline; - gpu::PipelinePointer _presentPipeline; gpu::PipelinePointer _cursorPipeline; - gpu::TexturePointer _displayTexture{}; + gpu::TexturePointer _displayTexture; float _compositeHUDAlpha { 1.0f }; struct CursorData { @@ -185,5 +175,9 @@ protected: // be serialized through this mutex mutable Mutex _presentMutex; float _hudAlpha{ 1.0f }; + +private: + gpu::PipelinePointer _presentPipeline; + }; diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h index f2b1f36419..95592cc490 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h @@ -24,7 +24,7 @@ public: protected: void updatePresentPose() override; - void hmdPresent() override {} + void hmdPresent(const gpu::FramebufferPointer&) override {} bool isHmdMounted() const override { return true; } bool internalActivate() override; private: diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 321bcc3fd2..a515232b3f 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -114,20 +114,23 @@ void HmdDisplayPlugin::internalDeactivate() { void HmdDisplayPlugin::customizeContext() { Parent::customizeContext(); - _hudRenderer.build(); + _hudOperator = _hudRenderer.build(); } void HmdDisplayPlugin::uncustomizeContext() { // 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)); - }); - _hudRenderer = HUDRenderer(); + if (_currentFrame && _currentFrame->framebuffer) { + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(_currentFrame->framebuffer); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + }); + + } + _hudRenderer = {}; _previewTexture.reset(); Parent::uncustomizeContext(); } @@ -174,11 +177,11 @@ float HmdDisplayPlugin::getLeftCenterPixel() const { return leftCenterPixel; } -void HmdDisplayPlugin::internalPresent() { +void HmdDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) { PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) // Composite together the scene, hud and mouse cursor - hmdPresent(); + hmdPresent(compositeFramebuffer); if (_displayTexture) { // Note: _displayTexture must currently be the same size as the display. @@ -260,7 +263,7 @@ void HmdDisplayPlugin::internalPresent() { viewport.z *= 2; } - renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor, fbo); + renderFromTexture(batch, compositeFramebuffer->getRenderBuffer(0), viewport, scissor, nullptr, fbo); }); swapBuffers(); @@ -345,7 +348,7 @@ glm::mat4 HmdDisplayPlugin::getViewCorrection() { } } -void HmdDisplayPlugin::HUDRenderer::build() { +DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() { vertices = std::make_shared(); indices = std::make_shared(); @@ -380,7 +383,7 @@ void HmdDisplayPlugin::HUDRenderer::build() { indexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; // Compute indices order - std::vector indices; + std::vector indexData; for (int i = 0; i < stacks - 1; i++) { for (int j = 0; j < slices - 1; j++) { GLushort bottomLeftIndex = i * slices + j; @@ -388,24 +391,21 @@ void HmdDisplayPlugin::HUDRenderer::build() { 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); + indexData.push_back(topLeftIndex); + indexData.push_back(bottomLeftIndex); + indexData.push_back(topRightIndex); - indices.push_back(topRightIndex); - indices.push_back(bottomLeftIndex); - indices.push_back(bottomRightIndex); + indexData.push_back(topRightIndex); + indexData.push_back(bottomLeftIndex); + indexData.push_back(bottomRightIndex); } } - this->indices->append(indices); + indices->append(indexData); format = std::make_shared(); // 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)); uniformsBuffer = std::make_shared(sizeof(Uniforms), nullptr); - updatePipeline(); -} -void HmdDisplayPlugin::HUDRenderer::updatePipeline() { if (!pipeline) { auto program = gpu::Shader::createProgram(shader::render_utils::program::hmd_ui); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -416,10 +416,6 @@ void HmdDisplayPlugin::HUDRenderer::updatePipeline() { pipeline = gpu::Pipeline::create(program, state); } -} - -std::function HmdDisplayPlugin::HUDRenderer::render(HmdDisplayPlugin& plugin) { - updatePipeline(); auto hudPipeline = pipeline; auto hudFormat = format; @@ -428,9 +424,9 @@ std::function HmdDis auto hudUniformBuffer = uniformsBuffer; auto hudUniforms = uniforms; auto hudIndexCount = indexCount; - return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, bool mirror) { - if (hudPipeline && hudTexture) { - batch.setPipeline(hudPipeline); + return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, const gpu::FramebufferPointer&, const bool mirror) { + if (pipeline && hudTexture) { + batch.setPipeline(pipeline); batch.setInputFormat(hudFormat); gpu::BufferView posView(hudVertices, VERTEX_OFFSET, hudVertices->getSize(), VERTEX_STRIDE, hudFormat->getAttributes().at(gpu::Stream::POSITION)._element); @@ -454,7 +450,7 @@ std::function HmdDis }; } -void HmdDisplayPlugin::compositePointer() { +void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compositeFramebuffer) { auto& cursorManager = Cursor::Manager::instance(); const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; auto compositorHelper = DependencyManager::get(); @@ -463,7 +459,7 @@ void HmdDisplayPlugin::compositePointer() { render([&](gpu::Batch& batch) { // FIXME use standard gpu stereo rendering for this. batch.enableStereo(false); - batch.setFramebuffer(_compositeFramebuffer); + batch.setFramebuffer(compositeFramebuffer); batch.setPipeline(_cursorPipeline); batch.setResourceTexture(0, cursorData.texture); batch.resetViewTransform(); @@ -478,10 +474,6 @@ void HmdDisplayPlugin::compositePointer() { }); } -std::function HmdDisplayPlugin::getHUDOperator() { - return _hudRenderer.render(*this); -} - HmdDisplayPlugin::~HmdDisplayPlugin() { } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index d8c0ce8e1d..6755c5b7e0 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -53,16 +53,15 @@ signals: void hmdVisibleChanged(bool visible); protected: - virtual void hmdPresent() = 0; + virtual void hmdPresent(const gpu::FramebufferPointer&) = 0; virtual bool isHmdMounted() const = 0; virtual void postPreview() {}; virtual void updatePresentPose(); bool internalActivate() override; void internalDeactivate() override; - std::function getHUDOperator() override; - void compositePointer() override; - void internalPresent() override; + void compositePointer(const gpu::FramebufferPointer&) override; + void internalPresent(const gpu::FramebufferPointer&) override; void customizeContext() override; void uncustomizeContext() override; void updateFrameData() override; @@ -120,8 +119,6 @@ private: static const size_t TEXTURE_OFFSET { offsetof(Vertex, uv) }; static const int VERTEX_STRIDE { sizeof(Vertex) }; - void build(); - void updatePipeline(); - std::function render(HmdDisplayPlugin& plugin); + HUDOperator build(); } _hudRenderer; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index 0ae0f9b1b6..69aa7fc344 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -37,13 +37,13 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const { return result; } -void InterleavedStereoDisplayPlugin::internalPresent() { +void InterleavedStereoDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) { 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.setResourceTexture(0, compositeFramebuffer->getRenderBuffer(0)); batch.setPipeline(_interleavedPresentPipeline); batch.draw(gpu::TRIANGLE_STRIP, 4); }); diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h index debd340f24..52dfa8f402 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h @@ -21,7 +21,7 @@ protected: // initialize OpenGL context settings needed by the plugin void customizeContext() override; void uncustomizeContext() override; - void internalPresent() override; + void internalPresent(const gpu::FramebufferPointer&) override; private: static const QString NAME; diff --git a/libraries/oculusMobile/src/ovr/Framebuffer.cpp b/libraries/oculusMobile/src/ovr/Framebuffer.cpp index 0f59eef614..a1dc1841de 100644 --- a/libraries/oculusMobile/src/ovr/Framebuffer.cpp +++ b/libraries/oculusMobile/src/ovr/Framebuffer.cpp @@ -7,59 +7,44 @@ // #include "Framebuffer.h" +#include + #include -#include #include #include #include +#include "Helpers.h" + using namespace ovr; void Framebuffer::updateLayer(int eye, ovrLayerProjection2& layer, const ovrMatrix4f* projectionMatrix ) const { auto& layerTexture = layer.Textures[eye]; - layerTexture.ColorSwapChain = _swapChain; - layerTexture.SwapChainIndex = _index; + layerTexture.ColorSwapChain = _swapChainInfos[eye].swapChain; + layerTexture.SwapChainIndex = _swapChainInfos[eye].index; if (projectionMatrix) { layerTexture.TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection( projectionMatrix ); } layerTexture.TextureRect = { 0, 0, 1, 1 }; } +void Framebuffer::SwapChainInfo::destroy() { + if (swapChain != nullptr) { + vrapi_DestroyTextureSwapChain(swapChain); + swapChain = nullptr; + } + index = -1; + length = -1; +} + void Framebuffer::create(const glm::uvec2& size) { _size = size; - _index = 0; - _validTexture = false; - - // Depth renderbuffer - /* glGenRenderbuffers(1, &_depth); - glBindRenderbuffer(GL_RENDERBUFFER, _depth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, _size.x, _size.y); - glBindRenderbuffer(GL_RENDERBUFFER, 0); -*/ - // Framebuffer - glGenFramebuffers(1, &_fbo); - // glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); - // glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depth); - // glBindFramebuffer(GL_FRAMEBUFFER, 0); - - _swapChain = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D, GL_RGBA8, _size.x, _size.y, 1, 3); - - _length = vrapi_GetTextureSwapChainLength(_swapChain); - if (!_length) { - __android_log_write(ANDROID_LOG_WARN, "QQQ_OVR", "Unable to count swap chain textures"); - return; - } - - for (int i = 0; i < _length; ++i) { - GLuint chainTexId = vrapi_GetTextureSwapChainHandle(_swapChain, i); - glBindTexture(GL_TEXTURE_2D, chainTexId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } + ovr::for_each_eye([&](ovrEye eye) { + _swapChainInfos[eye].create(size); + }); glBindTexture(GL_TEXTURE_2D, 0); + glGenFramebuffers(1, &_fbo); } void Framebuffer::destroy() { @@ -67,28 +52,82 @@ void Framebuffer::destroy() { glDeleteFramebuffers(1, &_fbo); _fbo = 0; } - if (0 != _depth) { - glDeleteRenderbuffers(1, &_depth); - _depth = 0; - } - if (_swapChain != nullptr) { - vrapi_DestroyTextureSwapChain(_swapChain); - _swapChain = nullptr; - } - _index = -1; - _length = -1; + + ovr::for_each_eye([&](ovrEye eye) { + _swapChainInfos[eye].destroy(); + }); } void Framebuffer::advance() { - _index = (_index + 1) % _length; - _validTexture = false; + ovr::for_each_eye([&](ovrEye eye) { + _swapChainInfos[eye].advance(); + }); } -void Framebuffer::bind() { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); - if (!_validTexture) { - GLuint chainTexId = vrapi_GetTextureSwapChainHandle(_swapChain, _index); - glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, chainTexId, 0); - _validTexture = true; +void Framebuffer::bind(GLenum target) { + glBindFramebuffer(target, _fbo); + _swapChainInfos[0].bind(target, GL_COLOR_ATTACHMENT0); + _swapChainInfos[1].bind(target, GL_COLOR_ATTACHMENT1); +} + +void Framebuffer::invalidate(GLenum target) { + static const std::array INVALIDATE_ATTACHMENTS {{ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }}; + glInvalidateFramebuffer(target, static_cast(INVALIDATE_ATTACHMENTS.size()), INVALIDATE_ATTACHMENTS.data()); +} + + +void Framebuffer::drawBuffers(ovrEye eye) const { + static const std::array, 3> EYE_DRAW_BUFFERS { { + {GL_COLOR_ATTACHMENT0, GL_NONE}, + {GL_NONE, GL_COLOR_ATTACHMENT1}, + {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1} + } }; + + switch(eye) { + case VRAPI_EYE_LEFT: + case VRAPI_EYE_RIGHT: + case VRAPI_EYE_COUNT: { + const auto& eyeDrawBuffers = EYE_DRAW_BUFFERS[eye]; + glDrawBuffers(static_cast(eyeDrawBuffers.size()), eyeDrawBuffers.data()); + } + break; + + default: + throw std::runtime_error("Invalid eye for drawBuffers"); + } +} + +void Framebuffer::SwapChainInfo::create(const glm::uvec2 &size) { + index = 0; + validTexture = false; + // GL_SRGB8_ALPHA8 and GL_RGBA8 appear to behave the same here. The only thing that changes the + // output gamma behavior is VRAPI_MODE_FLAG_FRONT_BUFFER_SRGB passed to vrapi_EnterVrMode + swapChain = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D, GL_SRGB8_ALPHA8, size.x, size.y, 1, 3); + length = vrapi_GetTextureSwapChainLength(swapChain); + if (!length) { + __android_log_write(ANDROID_LOG_WARN, "QQQ_OVR", "Unable to count swap chain textures"); + throw std::runtime_error("Unable to create Oculus texture swap chain"); + } + + for (int i = 0; i < length; ++i) { + GLuint chainTexId = vrapi_GetTextureSwapChainHandle(swapChain, i); + glBindTexture(GL_TEXTURE_2D, chainTexId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } +} + +void Framebuffer::SwapChainInfo::advance() { + index = (index + 1) % length; + validTexture = false; +} + +void Framebuffer::SwapChainInfo::bind(uint32_t target, uint32_t attachment) { + if (!validTexture) { + GLuint chainTexId = vrapi_GetTextureSwapChainHandle(swapChain, index); + glFramebufferTexture(target, attachment, chainTexId, 0); + validTexture = true; } } diff --git a/libraries/oculusMobile/src/ovr/Framebuffer.h b/libraries/oculusMobile/src/ovr/Framebuffer.h index 5127574462..4600d91534 100644 --- a/libraries/oculusMobile/src/ovr/Framebuffer.h +++ b/libraries/oculusMobile/src/ovr/Framebuffer.h @@ -9,6 +9,7 @@ #include #include +#include #include @@ -20,15 +21,28 @@ public: void create(const glm::uvec2& size); void advance(); void destroy(); - void bind(); + void bind(GLenum target = GL_DRAW_FRAMEBUFFER); + void invalidate(GLenum target = GL_DRAW_FRAMEBUFFER); + void drawBuffers(ovrEye eye) const; - uint32_t _depth { 0 }; + const glm::uvec2& size() const { return _size; } + +private: uint32_t _fbo{ 0 }; - int _length{ -1 }; - int _index{ -1 }; - bool _validTexture{ false }; glm::uvec2 _size; - ovrTextureSwapChain* _swapChain{ nullptr }; + struct SwapChainInfo { + int length{ -1 }; + int index{ -1 }; + bool validTexture{ false }; + ovrTextureSwapChain* swapChain{ nullptr }; + + void create(const glm::uvec2& size); + void destroy(); + void advance(); + void bind(GLenum target, GLenum attachment); + }; + + SwapChainInfo _swapChainInfos[VRAPI_FRAME_LAYER_EYE_MAX]; }; } // namespace ovr \ No newline at end of file diff --git a/libraries/oculusMobile/src/ovr/VrHandler.cpp b/libraries/oculusMobile/src/ovr/VrHandler.cpp index 6cc2ec9526..a7b0f9f8ee 100644 --- a/libraries/oculusMobile/src/ovr/VrHandler.cpp +++ b/libraries/oculusMobile/src/ovr/VrHandler.cpp @@ -9,37 +9,186 @@ #include #include +#include +#include #include +#include +#include #include #include #include //#include + #include "GLContext.h" #include "Helpers.h" #include "Framebuffer.h" +static AAssetManager* ASSET_MANAGER = nullptr; +#define USE_BLIT_PRESENT 0 + +#if !USE_BLIT_PRESENT + + + +static std::string getTextAsset(const char* assetPath) { + if (!ASSET_MANAGER || !assetPath) { + return nullptr; + } + AAsset* asset = AAssetManager_open(ASSET_MANAGER, assetPath, AASSET_MODE_BUFFER); + if (!asset) { + return {}; + } + + auto length = AAsset_getLength(asset); + if (0 == length) { + AAsset_close(asset); + return {}; + } + + auto buffer = AAsset_getBuffer(asset); + if (!buffer) { + AAsset_close(asset); + return {}; + } + + std::string result { static_cast(buffer), static_cast(length) }; + AAsset_close(asset); + return result; +} + +static std::string getShaderInfoLog(GLuint glshader) { + std::string result; + GLint infoLength = 0; + glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength); + if (infoLength > 0) { + char* temp = new char[infoLength]; + glGetShaderInfoLog(glshader, infoLength, NULL, temp); + result = std::string(temp); + delete[] temp; + } + return result; +} + +static GLuint buildShader(GLenum shaderDomain, const char* shader) { + GLuint glshader = glCreateShader(shaderDomain); + if (!glshader) { + throw std::runtime_error("Bad shader"); + } + + glShaderSource(glshader, 1, &shader, NULL); + glCompileShader(glshader); + + GLint compiled = 0; + glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled); + + // if compilation fails + if (!compiled) { + std::string compileError = getShaderInfoLog(glshader); + glDeleteShader(glshader); + __android_log_print(ANDROID_LOG_WARN, "QQQ_OVR", "Shader compile error: %s", compileError.c_str()); + return 0; + } + + return glshader; +} + +static std::string getProgramInfoLog(GLuint glprogram) { + std::string result; + GLint infoLength = 0; + glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); + if (infoLength > 0) { + char* temp = new char[infoLength]; + glGetProgramInfoLog(glprogram, infoLength, NULL, temp); + result = std::string(temp); + delete[] temp; + } + return result; +} + +static GLuint buildProgram(const char* vertex, const char* fragment) { + // A brand new program: + GLuint glprogram { 0 }, glvertex { 0 }, glfragment { 0 }; + + try { + glprogram = glCreateProgram(); + if (0 == glprogram) { + throw std::runtime_error("Failed to create program, is GL context current?"); + } + + glvertex = buildShader(GL_VERTEX_SHADER, vertex); + if (0 == glvertex) { + throw std::runtime_error("Failed to create or compile vertex shader"); + } + glAttachShader(glprogram, glvertex); + + glfragment = buildShader(GL_FRAGMENT_SHADER, fragment); + if (0 == glfragment) { + throw std::runtime_error("Failed to create or compile fragment shader"); + } + glAttachShader(glprogram, glfragment); + + GLint linked { 0 }; + glLinkProgram(glprogram); + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + + if (!linked) { + std::string linkErrorLog = getProgramInfoLog(glprogram); + __android_log_print(ANDROID_LOG_WARN, "QQQ_OVR", "Program link error: %s", linkErrorLog.c_str()); + throw std::runtime_error("Failed to link program, is the interface between the fragment and vertex shaders correct?"); + } + + + } catch(const std::runtime_error& error) { + if (0 != glprogram) { + glDeleteProgram(glprogram); + glprogram = 0; + } + } + + if (0 != glvertex) { + glDeleteShader(glvertex); + } + + if (0 != glfragment) { + glDeleteShader(glfragment); + } + + if (0 == glprogram) { + throw std::runtime_error("Failed to build program"); + } + + return glprogram; +} + +#endif using namespace ovr; static thread_local bool isRenderThread { false }; struct VrSurface : public TaskQueue { - using HandlerTask = VrHandler::HandlerTask; + using HandlerTask = ovr::VrHandler::HandlerTask; JavaVM* vm{nullptr}; jobject oculusActivity{ nullptr }; ANativeWindow* nativeWindow{ nullptr }; - VrHandler* handler{nullptr}; + ovr::VrHandler* handler{nullptr}; ovrMobile* session{nullptr}; bool resumed { false }; - GLContext vrglContext; - Framebuffer eyeFbos[2]; - uint32_t readFbo{0}; + ovr::GLContext vrglContext; + ovr::Framebuffer eyesFbo; + +#if USE_BLIT_PRESENT + GLuint readFbo { 0 }; +#else + GLuint renderProgram { 0 }; + GLuint renderVao { 0 }; +#endif std::atomic presentIndex{1}; double displayTime{0}; // Not currently set by anything @@ -76,6 +225,16 @@ struct VrSurface : public TaskQueue { vrglContext.create(currentDisplay, currentContext, noErrorContext); vrglContext.makeCurrent(); +#if USE_BLIT_PRESENT + glGenFramebuffers(1, &readFbo); +#else + glGenVertexArrays(1, &renderVao); + const char* vertex = nullptr; + auto vertexShader = getTextAsset("shaders/present.vert"); + auto fragmentShader = getTextAsset("shaders/present.frag"); + renderProgram = buildProgram(vertexShader.c_str(), fragmentShader.c_str()); +#endif + glm::uvec2 eyeTargetSize; withEnv([&](JNIEnv* env){ ovrJava java{ vm, env, oculusActivity }; @@ -85,10 +244,7 @@ struct VrSurface : public TaskQueue { }; }); __android_log_print(ANDROID_LOG_WARN, "QQQ_OVR", "QQQ Eye Size %d, %d", eyeTargetSize.x, eyeTargetSize.y); - ovr::for_each_eye([&](ovrEye eye) { - eyeFbos[eye].create(eyeTargetSize); - }); - glGenFramebuffers(1, &readFbo); + eyesFbo.create(eyeTargetSize); vrglContext.doneCurrent(); } @@ -157,6 +313,7 @@ struct VrSurface : public TaskQueue { ovrJava java{ vm, env, oculusActivity }; ovrModeParms modeParms = vrapi_DefaultModeParms(&java); modeParms.Flags |= VRAPI_MODE_FLAG_NATIVE_WINDOW; + modeParms.Flags |= VRAPI_MODE_FLAG_FRONT_BUFFER_SRGB; if (noErrorContext) { modeParms.Flags |= VRAPI_MODE_FLAG_CREATE_CONTEXT_NO_ERROR; } @@ -178,38 +335,51 @@ struct VrSurface : public TaskQueue { void presentFrame(uint32_t sourceTexture, const glm::uvec2 &sourceSize, const ovrTracking2& tracking) { ovrLayerProjection2 layer = vrapi_DefaultLayerProjection2(); layer.HeadPose = tracking.HeadPose; + + eyesFbo.bind(); if (sourceTexture) { + eyesFbo.invalidate(); +#if USE_BLIT_PRESENT glBindFramebuffer(GL_READ_FRAMEBUFFER, readFbo); glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, sourceTexture, 0); - GLenum framebufferStatus = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != framebufferStatus) { - __android_log_print(ANDROID_LOG_WARN, "QQQ_OVR", "incomplete framebuffer"); - } - } - GLenum invalidateAttachment = GL_COLOR_ATTACHMENT0; - - ovr::for_each_eye([&](ovrEye eye) { - const auto &eyeTracking = tracking.Eye[eye]; - auto &eyeFbo = eyeFbos[eye]; - const auto &destSize = eyeFbo._size; - eyeFbo.bind(); - glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &invalidateAttachment); - if (sourceTexture) { + const auto &destSize = eyesFbo.size(); + ovr::for_each_eye([&](ovrEye eye) { auto sourceWidth = sourceSize.x / 2; auto sourceX = (eye == VRAPI_EYE_LEFT) ? 0 : sourceWidth; + // Each eye blit uses a different draw buffer + eyesFbo.drawBuffers(eye); glBlitFramebuffer( sourceX, 0, sourceX + sourceWidth, sourceSize.y, 0, 0, destSize.x, destSize.y, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - eyeFbo.updateLayer(eye, layer, &eyeTracking.ProjectionMatrix); - eyeFbo.advance(); - }); - if (sourceTexture) { - glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, &invalidateAttachment); + }); + static const std::array READ_INVALIDATE_ATTACHMENTS {{ GL_COLOR_ATTACHMENT0 }}; + glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, (GLuint)READ_INVALIDATE_ATTACHMENTS.size(), READ_INVALIDATE_ATTACHMENTS.data()); glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0); +#else + eyesFbo.drawBuffers(VRAPI_EYE_COUNT); + const auto &destSize = eyesFbo.size(); + glViewport(0, 0, destSize.x, destSize.y); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, sourceTexture); + glBindVertexArray(renderVao); + glUseProgram(renderProgram); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glUseProgram(0); + glBindVertexArray(0); +#endif + } else { + eyesFbo.drawBuffers(VRAPI_EYE_COUNT); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); } - glFlush(); + + ovr::for_each_eye([&](ovrEye eye) { + const auto &eyeTracking = tracking.Eye[eye]; + eyesFbo.updateLayer(eye, layer, &eyeTracking.ProjectionMatrix); + }); + + eyesFbo.advance(); ovrLayerHeader2 *layerHeader = &layer.Header; ovrSubmitFrameDescription2 frameDesc = {}; @@ -321,8 +491,9 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *, void *) { return JNI_VERSION_1_6; } -JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_nativeOnCreate(JNIEnv* env, jobject obj) { +JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_nativeOnCreate(JNIEnv* env, jobject obj, jobject assetManager) { __android_log_write(ANDROID_LOG_WARN, "QQQ_JNI", __FUNCTION__); + ASSET_MANAGER = AAssetManager_fromJava(env, assetManager); SURFACE.onCreate(env, obj); } diff --git a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp index 9809d02866..12a9b12adc 100644 --- a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp +++ b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp @@ -245,7 +245,7 @@ void OculusMobileDisplayPlugin::updatePresentPose() { }); } -void OculusMobileDisplayPlugin::internalPresent() { +void OculusMobileDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compsiteFramebuffer) { VrHandler::pollTask(); if (!vrActive()) { @@ -253,8 +253,12 @@ void OculusMobileDisplayPlugin::internalPresent() { return; } - auto sourceTexture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); - glm::uvec2 sourceSize{ _compositeFramebuffer->getWidth(), _compositeFramebuffer->getHeight() }; + GLuint sourceTexture = 0; + glm::uvec2 sourceSize; + if (compsiteFramebuffer) { + sourceTexture = getGLBackend()->getTextureID(compsiteFramebuffer->getRenderBuffer(0)); + sourceSize = { compsiteFramebuffer->getWidth(), compsiteFramebuffer->getHeight() }; + } VrHandler::presentFrame(sourceTexture, sourceSize, presentTracking); _presentRate.increment(); } diff --git a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h index a98989655e..b5f7aa57b0 100644 --- a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h +++ b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h @@ -54,8 +54,8 @@ protected: void uncustomizeContext() override; void updatePresentPose() override; - void internalPresent() override; - void hmdPresent() override { throw std::runtime_error("Unused"); } + void internalPresent(const gpu::FramebufferPointer&) override; + void hmdPresent(const gpu::FramebufferPointer&) override { throw std::runtime_error("Unused"); } bool isHmdMounted() const override; bool alwaysPresent() const override { return true; } diff --git a/libraries/plugins/src/plugins/DisplayPlugin.cpp b/libraries/plugins/src/plugins/DisplayPlugin.cpp index 47503e8f85..71db87557c 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.cpp +++ b/libraries/plugins/src/plugins/DisplayPlugin.cpp @@ -2,6 +2,12 @@ #include + +const DisplayPlugin::HUDOperator DisplayPlugin::DEFAULT_HUD_OPERATOR{ std::function() }; + +DisplayPlugin::DisplayPlugin() : _hudOperator{ DEFAULT_HUD_OPERATOR } { +} + int64_t DisplayPlugin::getPaintDelayUsecs() const { std::lock_guard lock(_paintDelayMutex); return _paintDelayTimer.isValid() ? _paintDelayTimer.nsecsElapsed() / NSECS_PER_USEC : 0; @@ -35,8 +41,8 @@ void DisplayPlugin::waitForPresent() { } } -std::function DisplayPlugin::getHUDOperator() { - std::function hudOperator; +std::function DisplayPlugin::getHUDOperator() { + HUDOperator hudOperator; { QMutexLocker locker(&_presentMutex); hudOperator = _hudOperator; @@ -48,3 +54,5 @@ glm::mat4 HmdDisplay::getEyeToHeadTransform(Eye eye) const { static const glm::mat4 xform; return xform; } + + diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index aa52e57c3f..9194fde3ac 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -121,6 +121,8 @@ class DisplayPlugin : public Plugin, public HmdDisplay { Q_OBJECT using Parent = Plugin; public: + DisplayPlugin(); + virtual int getRequiredThreadCount() const { return 0; } virtual bool isHmd() const { return false; } virtual int getHmdScreen() const { return -1; } @@ -214,7 +216,8 @@ public: void waitForPresent(); float getAveragePresentTime() { return _movingAveragePresent.average / (float)USECS_PER_MSEC; } // in msec - std::function getHUDOperator(); + using HUDOperator = std::function; + virtual HUDOperator getHUDOperator() final; static const QString& MENU_PATH(); @@ -231,7 +234,8 @@ protected: gpu::ContextPointer _gpuContext; - std::function _hudOperator { std::function() }; + static const HUDOperator DEFAULT_HUD_OPERATOR; + HUDOperator _hudOperator; MovingAverage _movingAveragePresent; diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index 385e384efe..e77ccb74a5 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -122,8 +122,8 @@ void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::Fra if (inputs) { batch.setFramebuffer(inputs); } - if (renderContext->args->_hudOperator) { - renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); + if (renderContext->args->_hudOperator && renderContext->args->_blitFramebuffer) { + renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_blitFramebuffer, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); } }); #endif diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index b5c98e3428..8b2fff68c6 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -131,7 +131,7 @@ namespace render { render::ScenePointer _scene; int8_t _cameraMode { -1 }; - std::function _hudOperator; + std::function _hudOperator; gpu::TexturePointer _hudTexture; }; diff --git a/plugins/oculus/src/OculusDebugDisplayPlugin.h b/plugins/oculus/src/OculusDebugDisplayPlugin.h index ec05cd92e2..690a488b34 100644 --- a/plugins/oculus/src/OculusDebugDisplayPlugin.h +++ b/plugins/oculus/src/OculusDebugDisplayPlugin.h @@ -16,7 +16,7 @@ public: bool isSupported() const override; protected: - void hmdPresent() override {} + void hmdPresent(const gpu::FramebufferPointer&) override {} bool isHmdMounted() const override { return true; } private: diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index df01591639..48440ac80f 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -108,13 +108,16 @@ void OculusDisplayPlugin::customizeContext() { } void OculusDisplayPlugin::uncustomizeContext() { + #if 0 - // Present a final black frame to the HMD - _compositeFramebuffer->Bound(FramebufferTarget::Draw, [] { - Context::ClearColor(0, 0, 0, 1); - Context::Clear().ColorBuffer(); - }); - hmdPresent(); + if (_currentFrame && _currentFrame->framebuffer) { + // Present a final black frame to the HMD + _currentFrame->framebuffer->Bound(FramebufferTarget::Draw, [] { + Context::ClearColor(0, 0, 0, 1); + Context::Clear().ColorBuffer(); + }); + hmdPresent(); + } #endif ovr_DestroyTextureSwapChain(_session, _textureSwapChain); @@ -127,7 +130,7 @@ void OculusDisplayPlugin::uncustomizeContext() { static const uint64_t FRAME_BUDGET = (11 * USECS_PER_MSEC); static const uint64_t FRAME_OVER_BUDGET = (15 * USECS_PER_MSEC); -void OculusDisplayPlugin::hmdPresent() { +void OculusDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) { static uint64_t lastSubmitEnd = 0; if (!_customized) { @@ -157,15 +160,8 @@ void OculusDisplayPlugin::hmdPresent() { auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer); glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0); render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.setFramebuffer(_outputFramebuffer); - batch.setViewportTransform(ivec4(uvec2(), _outputFramebuffer->getSize())); - batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize())); - batch.resetViewTransform(); - batch.setProjectionTransform(mat4()); - batch.setPipeline(_presentPipeline); - batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); + auto viewport = ivec4(uvec2(), _outputFramebuffer->getSize()); + renderFromTexture(batch, compositeFramebuffer->getRenderBuffer(0), viewport, viewport, _outputFramebuffer); }); glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, 0, 0); } diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index 9209fd373e..a0126d2e58 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -28,7 +28,7 @@ protected: QThread::Priority getPresentPriority() override { return QThread::TimeCriticalPriority; } bool internalActivate() override; - void hmdPresent() override; + void hmdPresent(const gpu::FramebufferPointer&) override; bool isHmdMounted() const override; void customizeContext() override; void uncustomizeContext() override; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index e6b555443f..a928887866 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -237,7 +237,7 @@ void OculusLegacyDisplayPlugin::uncustomizeContext() { Parent::uncustomizeContext(); } -void OculusLegacyDisplayPlugin::hmdPresent() { +void OculusLegacyDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) { if (!_hswDismissed) { ovrHSWDisplayState hswState; ovrHmd_GetHSWDisplayState(_hmd, &hswState); @@ -252,7 +252,7 @@ void OculusLegacyDisplayPlugin::hmdPresent() { memset(eyePoses, 0, sizeof(ovrPosef) * 2); eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation; - GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); + GLint texture = getGLBackend()->getTextureID(compositeFramebuffer->getRenderBuffer(0)); auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); if (_hmdWindow->makeCurrent()) { diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 36bdd1c792..241d626f0c 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -39,7 +39,7 @@ protected: void customizeContext() override; void uncustomizeContext() override; - void hmdPresent() override; + void hmdPresent(const gpu::FramebufferPointer&) override; bool isHmdMounted() const override { return true; } private: diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 11d941dcd0..3d22268472 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -511,13 +511,13 @@ void OpenVrDisplayPlugin::customizeContext() { Parent::customizeContext(); if (_threadedSubmit) { - _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); +// _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) { - if (0 != i) { +// if (0 != i) { _compositeInfos[i].texture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT)); - } +// } _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture); } _submitThread->_canvas = _submitCanvas; @@ -613,17 +613,17 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { return Parent::beginFrameRender(frameIndex); } -void OpenVrDisplayPlugin::compositeLayers() { +void OpenVrDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& compositeFramebuffer) { if (_threadedSubmit) { ++_renderingIndex; _renderingIndex %= COMPOSITING_BUFFER_SIZE; auto& newComposite = _compositeInfos[_renderingIndex]; newComposite.pose = _currentPresentFrameInfo.presentPose; - _compositeFramebuffer->setRenderBuffer(0, newComposite.texture); + compositeFramebuffer->setRenderBuffer(0, newComposite.texture); } - Parent::compositeLayers(); + Parent::compositeLayers(compositeFramebuffer); if (_threadedSubmit) { auto& newComposite = _compositeInfos[_renderingIndex]; @@ -645,13 +645,13 @@ void OpenVrDisplayPlugin::compositeLayers() { } } -void OpenVrDisplayPlugin::hmdPresent() { +void OpenVrDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) { PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex) if (_threadedSubmit) { _submitThread->waitForPresent(); } else { - GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); + GLuint glTexId = getGLBackend()->getTextureID(compositeFramebuffer->getRenderBuffer(0)); vr::Texture_t vrTexture{ (void*)(uintptr_t)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT); diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 265f328920..923a0f7a8f 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -72,8 +72,8 @@ protected: void internalDeactivate() override; void updatePresentPose() override; - void compositeLayers() override; - void hmdPresent() override; + void compositeLayers(const gpu::FramebufferPointer&) override; + void hmdPresent(const gpu::FramebufferPointer&) override; bool isHmdMounted() const override; void postPreview() override;