mirror of
https://github.com/thejerf/baijiSteamVR.git
synced 2026-10-19 06:02:49 +02:00
- Add IOpenXRSwapchain::GetEyeSwapchainHandle() and SubmitStereoFlatFrame(), with per-backend overrides for Vulkan, D3D11, D3D12, and OGL. - Add OpenXRManager::SubmitStereoQuadFrame() which builds two world-locked XrCompositionLayerQuad layers with XR_EYE_VISIBILITY_LEFT/RIGHT and publishes them through the pacing thread (or EndFrame on the legacy path). - Extend PublishedXRFrame and FrameThreadLoop to submit the stereo quad pair. - Add UI checkbox for Stereo Screen in VRPane and VRConfigWidget. The game renders with classic SBS stereoscopy; left/right XFB layers are blit into the corresponding OpenXR eye swapchains and shown as a 3D floating screen.
131 lines
4.5 KiB
C++
131 lines
4.5 KiB
C++
// Copyright 2026 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#ifdef ENABLE_VR
|
|
|
|
#include <array>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include <openxr/openxr.h>
|
|
|
|
#include "VideoCommon/VR/OpenXRManager.h"
|
|
|
|
class GLContext;
|
|
|
|
namespace OGL
|
|
{
|
|
class OGLTexture;
|
|
class OGLFramebuffer;
|
|
|
|
// Holds the swapchain images for one eye.
|
|
// The GL texture names are owned by the OpenXR runtime; OGLTexture adopts them
|
|
// (without taking ownership) and OGLFramebuffer provides the FBO for rendering.
|
|
// Special members are out-of-line so the unique_ptr deleters instantiate in the
|
|
// .cpp where OGLTexture/OGLFramebuffer are complete types.
|
|
struct XREyeSwapchainGL
|
|
{
|
|
XREyeSwapchainGL();
|
|
~XREyeSwapchainGL();
|
|
XREyeSwapchainGL(XREyeSwapchainGL&&) noexcept;
|
|
XREyeSwapchainGL& operator=(XREyeSwapchainGL&&) noexcept;
|
|
|
|
XREyeSwapchainGL(const XREyeSwapchainGL&) = delete;
|
|
XREyeSwapchainGL& operator=(const XREyeSwapchainGL&) = delete;
|
|
|
|
XrSwapchain swapchain = XR_NULL_HANDLE;
|
|
uint32_t width = 0;
|
|
uint32_t height = 0;
|
|
|
|
// One entry per swapchain image.
|
|
std::vector<std::unique_ptr<OGLTexture>> textures;
|
|
std::vector<std::unique_ptr<OGLFramebuffer>> framebuffers;
|
|
};
|
|
|
|
// OpenGL/GLES-specific OpenXR backend. Implements VR::IOpenXRSwapchain so that
|
|
// Presenter::RenderXFBToScreen() can acquire/release eye images and submit
|
|
// frames using only VideoCommon-visible types (AbstractFramebuffer*).
|
|
//
|
|
// Unlike Vulkan, the GL context is single-threaded on the GPU thread (like D3D11),
|
|
// so no queue locking or asynchronous frame finalization is needed. All methods
|
|
// (including Initialize/Shutdown) must be called with the main GL context current.
|
|
class OGLOpenXR : public VR::IOpenXRSwapchain
|
|
{
|
|
public:
|
|
OGLOpenXR();
|
|
~OGLOpenXR() override;
|
|
|
|
OGLOpenXR(const OGLOpenXR&) = delete;
|
|
OGLOpenXR& operator=(const OGLOpenXR&) = delete;
|
|
|
|
// GL frame calls must stay on the thread owning the GL context; keep the legacy
|
|
// inline Wait/Begin/EndFrame flow instead of the XR pacing thread.
|
|
bool SupportsDetachedFrameLoop() const override { return false; }
|
|
|
|
// Full initialization: creates XrInstance + system, GL-bound XrSession,
|
|
// reference space, and per-eye swapchains.
|
|
// context must be the main GL context, current on the calling (GPU) thread.
|
|
bool Initialize(GLContext* context);
|
|
|
|
// Tears down swapchains and resets g_openxr.
|
|
void Shutdown();
|
|
|
|
// ---- IOpenXRSwapchain ----
|
|
|
|
// Acquire the next swapchain image for the given eye.
|
|
// Returns AbstractFramebuffer* (actually OGLFramebuffer*) to render into.
|
|
AbstractFramebuffer* AcquireEyeFramebuffer(uint32_t eye_index) override;
|
|
|
|
// Release the current swapchain image back to the runtime.
|
|
void ReleaseEyeTexture(uint32_t eye_index) override;
|
|
|
|
// Build the XrCompositionLayerProjection and call xrEndFrame.
|
|
bool SubmitFrame() override;
|
|
|
|
uint32_t GetEyeWidth() const override { return m_eye_swapchains[0].width; }
|
|
uint32_t GetEyeHeight() const override { return m_eye_swapchains[0].height; }
|
|
|
|
// Flat mono panel path reuses eye swapchain #0; the base class handles acquire/release/submit.
|
|
XrSwapchain GetFlatSwapchain() const override { return m_eye_swapchains[0].swapchain; }
|
|
XrSwapchain GetEyeSwapchainHandle(uint32_t eye) const override
|
|
{
|
|
return m_eye_swapchains[eye].swapchain;
|
|
}
|
|
|
|
private:
|
|
// Creates XrSession with the platform-specific GL graphics binding
|
|
// (Win32/WGL, Xlib/GLX or Android/EGL).
|
|
bool CreateSessionGL();
|
|
|
|
// Picks a swapchain format from xrEnumerateSwapchainFormats (GL internalformat enums).
|
|
bool SelectSwapchainFormat(int64_t* out_format) const;
|
|
|
|
// Allocates m_eye_swapchains and wraps images as OGLTexture / OGLFramebuffer.
|
|
bool CreateSwapchains();
|
|
|
|
void DestroySwapchains();
|
|
|
|
// Non-owning; the context is owned by OGLGfx and outlives this object.
|
|
GLContext* m_context = nullptr;
|
|
bool m_gles = false;
|
|
|
|
std::array<XREyeSwapchainGL, 2> m_eye_swapchains{};
|
|
|
|
// Image index selected by xrAcquireSwapchainImage for the current frame.
|
|
std::array<uint32_t, 2> m_acquired_image_index{0, 0};
|
|
std::array<bool, 2> m_image_acquired{false, false};
|
|
|
|
// Reused per-frame composition data (avoids per-frame heap allocation).
|
|
std::array<XrCompositionLayerProjectionView, 2> m_projection_views{};
|
|
XrCompositionLayerProjection m_projection_layer{XR_TYPE_COMPOSITION_LAYER_PROJECTION};
|
|
};
|
|
|
|
// Global OpenGL OpenXR instance — valid between VideoBackend::Initialize() and Shutdown().
|
|
extern std::unique_ptr<OGLOpenXR> g_openxr_gl;
|
|
|
|
} // namespace OGL
|
|
|
|
#endif // ENABLE_VR
|