Replace the QRhi rendering in OverlayController (private Qt API) with the render backends of the vrdashboard template (src/render/), which use public Qt API only (QQuickRenderTarget::fromOpenGLTexture / fromVulkanImage). The binary then runs on another Qt 6 patch release, e.g. the Steam Frame's system Qt. The Vulkan backend also does what SteamVR requires and the QRhi path lacked: the required instance/device extensions and the HMD's GPU (via openvr_init::renderInterop()), VK_FORMAT_R8G8B8A8_UNORM, and the transition to TRANSFER_SRC_OPTIMAL after each frame. - New option --graphics-api opengl|vulkan (default opengl, no fallback). - Shutdown() calls VR_Shutdown before the render target and Qt's device go (crash on the Steam Frame with Vulkan otherwise); it runs only once. - Mouse y is flipped on every platform: SteamVR reports it in GL convention. - Render delay 5 -> 16 ms (about 60 fps). - GuiPrivate is no longer needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
117 lines
4.2 KiB
C++
117 lines
4.2 KiB
C++
// Adapted from the vrdashboard template (FrameAssistant commit 4a5b090),
|
|
// verified on the Steam Frame. See docs/steamframe/DESIGN.md, section 3.
|
|
|
|
#pragma once
|
|
|
|
#include <openvr.h>
|
|
|
|
#include <QByteArrayList>
|
|
#include <QImage>
|
|
#include <QQuickRenderTarget>
|
|
#include <QSGRendererInterface>
|
|
#include <QSize>
|
|
#include <QString>
|
|
#include <functional>
|
|
#include <memory>
|
|
|
|
class QQuickWindow;
|
|
|
|
// Requirements the VR runtime imposes on the graphics device. All optional:
|
|
// without them (snapshot mode, tests) backends pick defaults.
|
|
struct RenderInterop
|
|
{
|
|
std::function<QByteArrayList()> vulkanInstanceExtensions;
|
|
std::function<QByteArrayList( VkPhysicalDevice_T* )> vulkanDeviceExtensions;
|
|
// The physical device driving the HMD, for a given VkInstance.
|
|
std::function<VkPhysicalDevice_T*( VkInstance_T* )> vulkanOutputDevice;
|
|
};
|
|
|
|
// Abstraction over the graphics API used to render the Qt Quick scene into a
|
|
// texture that SteamVR can consume. Everything API-specific (context/device
|
|
// setup, texture allocation, layout transitions, handing the texture to OpenVR,
|
|
// CPU readback) lives behind this interface; OverlayController and the
|
|
// VR/input code stay API-agnostic.
|
|
//
|
|
// Call order (driven by OverlayController):
|
|
// initialize → configureWindow → [QQuickRenderControl::initialize] →
|
|
// attachWindow → createRenderTarget → per frame: beginFrame … [Qt renders] …
|
|
// finishFrame → vrTexture → releaseRenderTarget → detachWindow → [Qt destroys
|
|
// its device/context]
|
|
class RenderBackend
|
|
{
|
|
public:
|
|
virtual ~RenderBackend() = default;
|
|
|
|
virtual QString name() const = 0;
|
|
|
|
// Must be applied via QQuickWindow::setGraphicsApi() before any
|
|
// QQuickWindow exists.
|
|
virtual QSGRendererInterface::GraphicsApi graphicsApi() const = 0;
|
|
|
|
// Creates API-level objects (context / instance). Requires a
|
|
// QGuiApplication.
|
|
virtual bool initialize( const RenderInterop& interop, QString* error ) = 0;
|
|
|
|
// Tells Qt Quick which device/context/instance to use. Before
|
|
// QQuickRenderControl::initialize().
|
|
virtual void configureWindow( QQuickWindow* window ) = 0;
|
|
|
|
// Picks up objects Qt created during QQuickRenderControl::initialize()
|
|
// (e.g. VkDevice).
|
|
virtual bool attachWindow( QQuickWindow* window, QString* error ) = 0;
|
|
// Releases everything created on Qt's device; called before Qt destroys
|
|
// it.
|
|
virtual void detachWindow() = 0;
|
|
|
|
// Human-readable GPU/driver description (valid after attachWindow).
|
|
virtual QString deviceName() const = 0;
|
|
|
|
// Bind the API context to the current thread (no-op for APIs without
|
|
// one).
|
|
virtual bool makeCurrent() = 0;
|
|
virtual void doneCurrent() = 0;
|
|
|
|
// (Re)allocates the shared color texture; returns the target for
|
|
// QQuickWindow::setRenderTarget.
|
|
virtual QQuickRenderTarget createRenderTarget( QSize size ) = 0;
|
|
// Frees the texture. Called before Qt's device/context goes away.
|
|
virtual void releaseRenderTarget() = 0;
|
|
|
|
// Before Qt renders a frame / after QQuickRenderControl::endFrame().
|
|
// Backends put the texture into the state Qt resp. SteamVR expects
|
|
// (layouts, flushes).
|
|
virtual void beginFrame() = 0;
|
|
virtual void finishFrame() = 0;
|
|
|
|
// Blocks until the GPU has finished all submitted work (benchmarks:
|
|
// makes the asynchronous OpenGL path comparable to the synchronous
|
|
// Vulkan one).
|
|
virtual void waitForGpu() = 0;
|
|
|
|
// Texture description for IVROverlay::SetOverlayTexture. Only valid
|
|
// while the render target exists; must be submitted with the context
|
|
// current (see makeCurrent()).
|
|
virtual vr::Texture_t vrTexture() = 0;
|
|
|
|
// UV bounds for IVROverlay::SetOverlayTextureBounds (compensates
|
|
// API-specific origin).
|
|
virtual vr::VRTextureBounds_t vrTextureBounds() const = 0;
|
|
|
|
// Reads the last finished frame back to the CPU (snapshots, tests).
|
|
// Slow.
|
|
virtual QImage readback() = 0;
|
|
};
|
|
|
|
enum class GraphicsApi
|
|
{
|
|
OpenGL,
|
|
Vulkan
|
|
};
|
|
|
|
// Returns nullptr if the API was not compiled in (Vulkan is optional, see
|
|
// OVRAS_HAS_VULKAN).
|
|
std::unique_ptr<RenderBackend> createRenderBackend( GraphicsApi api );
|
|
|
|
// "opengl" / "vulkan" (case-insensitive). Returns false for unknown names.
|
|
bool parseGraphicsApi( const QString& name, GraphicsApi* api );
|