From b9efba55e7bc2dc458901a4eb9b6602454d2d7db Mon Sep 17 00:00:00 2001
From: James Hogan <james@albanarts.com>
Date: Wed, 15 Jun 2022 23:57:33 +0100
Subject: [PATCH] src/Viewer/splash: VR splash screen

Use a quad composition layer object from osgXR 0.3.9 to make the splash
screen VR friendly. This allows the OpenXR compositor to redraw the quad
without any updates from flightgear, which is particularly helpful as
flightgear makes no attempt at a reasonable VR frame rate during
initialisation.

The quad is positioned 2 meters from the user, with an aspect ratio
matching the desktop window. The quad is blended using an
unpremultiplied alpha channel which is forced to a given alpha value by
osgXR.

The splash is effectively already rendered to an FBO as non-linear SRGB,
so this is made explicit in the internal texture format, with the
rendering to the desktop window using GL_FRAMEBUFFER_SRGB to ensure it
is properly re-encoded.
---
 CMakeLists.txt        |  2 +-
 src/Viewer/splash.cxx | 38 +++++++++++++++++++++++++++++++++++++-
 src/Viewer/splash.hxx | 14 ++++++++++++++
 3 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 99e8c3ab9..29ac80778 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -158,7 +158,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
 endif()
 
 if (ENABLE_VR_DEFAULT)
-    find_package(osgXR 0.3.3 QUIET)
+    find_package(osgXR 0.3.9 QUIET)
     if (osgXR_FOUND)
         set(SYSTEM_OSGXR_DEFAULT 1)
     endif()
diff --git a/src/Viewer/splash.cxx b/src/Viewer/splash.cxx
index 6a874559d..8fec18368 100644
--- a/src/Viewer/splash.cxx
+++ b/src/Viewer/splash.cxx
@@ -57,6 +57,7 @@
 #include <Main/util.hxx>
 #include "splash.hxx"
 #include "renderer.hxx"
+#include "VRManager.hxx"
 
 #include <sstream>
 
@@ -79,6 +80,19 @@ public:
 SplashScreen::SplashScreen() :
     _splashAlphaNode(fgGetNode("/sim/startup/splash-alpha", true))
 {
+#ifdef ENABLE_OSGXR
+    uint32_t splashW = 1920, splashH = 1080;
+    float aspect = (float)splashW / splashH;
+    _splashSwapchain = new osgXR::Swapchain(splashW, splashH);
+    _splashSwapchain->setAlphaBits(8);
+    _splashSwapchain->allowRGBEncoding(osgXR::Swapchain::Encoding::ENCODING_SRGB);
+    _splashLayer = new osgXR::CompositionLayerQuad(flightgear::VRManager::instance());
+    _splashLayer->setSubImage(_splashSwapchain);
+    _splashLayer->setSize(osg::Vec2f(aspect, 1.0f));
+    _splashLayer->setPosition(osg::Vec3f(0, 0, -2.0f));
+    _splashLayer->setAlphaMode(osgXR::CompositionLayer::BLEND_ALPHA_UNPREMULT);
+#endif
+
     setName("splashGroup");
     setUpdateCallback(new SplashScreenUpdateCallback);
 }
@@ -91,7 +105,7 @@ void SplashScreen::createNodes()
 {
     // setup the base geometry 
     _splashFBOTexture = new osg::Texture2D;
-    _splashFBOTexture->setInternalFormat(GL_RGB);
+    _splashFBOTexture->setInternalFormat(GL_SRGB8);
     _splashFBOTexture->setResizeNonPowerOfTwoHint(false);
     _splashFBOTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
     _splashFBOTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
@@ -226,6 +240,7 @@ void SplashScreen::createNodes()
     stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
     stateSet->setRenderBinDetails(1000, "RenderBin");
     stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
+    stateSet->setMode(GL_FRAMEBUFFER_SRGB, osg::StateAttribute::ON);
     
     geometry = osg::createTexturedQuadGeometry(osg::Vec3(0.0, 0.0, 0.0),
                                                osg::Vec3(1.0, 0.0, 0.0),
@@ -244,6 +259,9 @@ void SplashScreen::createNodes()
     geode = new osg::Geode;
     geode->addDrawable(geometry);
 
+#ifdef ENABLE_OSGXR
+    _splashSwapchain->attachToMirror(stateSet);
+#endif
     _splashQuadCamera->addChild(geode);
     addChild(_splashQuadCamera);
 }
@@ -325,6 +343,9 @@ osg::ref_ptr<osg::Camera> SplashScreen::createFBOCamera()
     c->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT );
     c->setRenderOrder(osg::Camera::PRE_RENDER);
     c->attach(osg::Camera::COLOR_BUFFER, _splashFBOTexture);
+#ifdef ENABLE_OSGXR
+    _splashSwapchain->attachToCamera(c);
+#endif
 
     osg::StateSet* stateSet = c->getOrCreateStateSet();
     stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
@@ -570,11 +591,18 @@ void SplashScreen::doUpdate()
         removeChild(0, getNumChildren());
         _splashFBOCamera = nullptr;
         _splashQuadCamera = nullptr;
+#ifdef ENABLE_OSGXR
+        _splashLayer->setVisible(false);
+#endif
     } else if (getNumChildren() == 0) {
         createNodes();
         _splashStartTime.stamp();
         resize(fgGetInt("/sim/startup/xsize"),
                fgGetInt("/sim/startup/ysize"));
+#ifdef ENABLE_OSGXR
+        _splashLayer->setVisible(true);
+        _splashSwapchain->setForcedAlpha(alpha);
+#endif
     } else {
         (*_splashFSQuadColor)[0] = osg::Vec4(1.0, 1.0, 1.0, _splashAlphaNode->getFloatValue());
         _splashFSQuadColor->dirty();
@@ -603,6 +631,9 @@ void SplashScreen::doUpdate()
         }
         updateSplashSpinner();
         updateTipText();
+#ifdef ENABLE_OSGXR
+        _splashSwapchain->setForcedAlpha(alpha);
+#endif
     }
 }
 
@@ -686,6 +717,11 @@ void SplashScreen::resize( int width, int height )
     _splashFBOCamera->setViewport(0, 0, width, height);
     _splashFBOCamera->setProjectionMatrixAsOrtho2D(-width * 0.5, width * 0.5,
                                                    -height * 0.5, height * 0.5);
+#ifdef ENABLE_OSGXR
+    float aspect = (float)width / height;
+    _splashSwapchain->setSize(width, height);
+    _splashLayer->setSize(osg::Vec2f(aspect, 1.0f));
+#endif
 
     const double screenAspectRatio = static_cast<double>(width) / height;
  
diff --git a/src/Viewer/splash.hxx b/src/Viewer/splash.hxx
index 9350c8225..5b50059a4 100644
--- a/src/Viewer/splash.hxx
+++ b/src/Viewer/splash.hxx
@@ -25,9 +25,16 @@
 #ifndef _SPLASH_HXX
 #define _SPLASH_HXX
 
+#include <config.h>
+
 #include <osg/Group>
 #include <osgText/Text>
 
+#ifdef ENABLE_OSGXR
+#include <osgXR/CompositionLayerQuad>
+#include <osgXR/Swapchain>
+#endif
+
 #include <simgear/props/props.hxx>
 #include <simgear/timing/timestamp.hxx>
 
@@ -117,6 +124,13 @@ private:
     osg::ref_ptr<osg::Camera> _splashQuadCamera;
 
      std::vector<ImageItem> _imageItems;
+
+#ifdef ENABLE_OSGXR
+    // Splash as OpenXR composition layer
+    osg::ref_ptr<osgXR::Swapchain> _splashSwapchain;
+    osg::ref_ptr<osgXR::CompositionLayerQuad> _splashLayer;
+#endif
+
     std::vector<TextItem> _items;
     
     SGTimeStamp _splashStartTime;