Files
OpenVR-AdvancedSettings-frame/src/render/RenderBackend.h
T
Matthias JanetschekandClaude Opus 5.5 4826686a4d Render through public Qt API: OpenGL and Vulkan backends, --graphics-api
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>
2026-10-07 13:41:13 +02:00

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 );