Added Vulkan PC OpenXR Support

This commit is contained in:
iChris4 committed 2026-09-21 00:31:46 +02:00
1 parent fbc9ce7217
commit e7b670b2cc
23 files changed
+2174 -22

No files matched your search

+20
View File
@@ -286,6 +286,15 @@ if(MKW_PROJECT_COMPILE_DEFINITIONS)
add_compile_definitions(${MKW_PROJECT_COMPILE_DEFINITIONS})
endif()
if(MKW_PLATFORM_WINDOWS AND MKW_ENABLE_OPENXR)
include(FetchContent)
FetchContent_Declare(vulkan_headers
URL "https://github.com/KhronosGroup/Vulkan-Headers/archive/015e25c3c91b70eb1a754d36fb14c4ba6ad9b0b9.tar.gz"
DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
FetchContent_MakeAvailable(vulkan_headers)
include_directories("${vulkan_headers_SOURCE_DIR}/include")
endif()
set(MKW_OPENXR_TARGET "")
if(MKW_ENABLE_OPENXR)
if(NOT MKW_PLATFORM_WINDOWS AND NOT MKW_PLATFORM_LINUX AND NOT MKW_PLATFORM_ANDROID)
@@ -340,6 +349,7 @@ endif()
# a registration file is silently never compiled and never errors. The stale-glob
# failure mode is worth far more than the milliseconds.
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "src/*.cpp")
set_source_files_properties(src/vr/openxr_vulkan_win32.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
if(MKW_PLATFORM_MACOS)
list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_LIST_DIR}/src/guest_flat_memory.cpp")
# HostContext's Apple Silicon backend is implemented in a small assembly
@@ -453,6 +463,16 @@ if(MKW_ENABLE_OPENXR AND MKW_PLATFORM_WINDOWS)
target_compile_features(mkw_openxr_replay_tests PRIVATE cxx_std_17)
set_target_properties(mkw_openxr_replay_tests PROPERTIES UNITY_BUILD OFF)
add_test(NAME mkw_openxr_replay_tests COMMAND mkw_openxr_replay_tests)
add_executable(mkw_openxr_vulkan_replay_tests
tests/openxr_d3d12_replay_tests.cpp src/vr/openxr_vulkan_win32.cpp src/vr/openxr_diagnostics.cpp)
target_include_directories(mkw_openxr_vulkan_replay_tests PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/include"
"${CMAKE_CURRENT_LIST_DIR}/../aurora-main/include"
"$<TARGET_PROPERTY:${MKW_OPENXR_TARGET},INTERFACE_INCLUDE_DIRECTORIES>")
target_compile_definitions(mkw_openxr_vulkan_replay_tests PRIVATE MKW_ENABLE_OPENXR=1 TEST_WINDOWS_VULKAN=1)
target_compile_features(mkw_openxr_vulkan_replay_tests PRIVATE cxx_std_17)
set_target_properties(mkw_openxr_vulkan_replay_tests PROPERTIES UNITY_BUILD OFF)
add_test(NAME mkw_openxr_vulkan_replay_tests COMMAND mkw_openxr_vulkan_replay_tests)
endif()
add_executable(mkw_nand_save_tests "${CMAKE_CURRENT_LIST_DIR}/tests/nand_save_tests.cpp")
+19
View File
@@ -106,6 +106,23 @@ public:
OpenXRRuntime(OpenXRRuntime&&) = delete;
OpenXRRuntime& operator=(OpenXRRuntime&&) = delete;
// Same-device Vulkan runtimes may touch Dawn's queue in frame/swapchain
// calls. Share Dawn's device guard; never hold it across xrWaitFrame.
struct GraphicsQueueGuard {
GraphicsQueueGuard(void* value, void (*release)(void*)) : token(value), unlock(release) {}
GraphicsQueueGuard(const GraphicsQueueGuard&) = delete;
GraphicsQueueGuard& operator=(const GraphicsQueueGuard&) = delete;
void* token;
void (*unlock)(void*);
~GraphicsQueueGuard() { if (token && unlock) unlock(token); }
};
void SetGraphicsQueueGuard(void* (*lock)(), void (*unlock)(void*)) {
m_queue_lock = lock; m_queue_unlock = unlock;
}
GraphicsQueueGuard LockGraphicsQueue() const {
return {m_queue_lock ? m_queue_lock() : nullptr, m_queue_unlock};
}
// Creates the instance, resolves the HMD system, and enumerates the stereo
// view configuration. Returns false without terminating the application;
// the caller should continue in non-VR mode.
@@ -214,6 +231,8 @@ public:
const OpenXRError& LastError() const { return m_last_error; }
private:
void* (*m_queue_lock)() = nullptr;
void (*m_queue_unlock)(void*) = nullptr;
enum class FramePhase {
Idle,
Waited,
+109
View File
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#if defined(MKW_ENABLE_OPENXR) && defined(_WIN32)
#include "vr/openxr_backend.h"
#include "vr/openxr_runtime.h"
#include <array>
#include <cstdint>
#include <memory>
#include <string>
namespace mkw::vr {
// Shared frame vocabulary for the Windows pacing and replay paths.
using OpenXRWindowsVulkanFrameMode = OpenXRFrameMode;
using OpenXRWindowsVulkanBeginStatus = OpenXRBeginStatus;
using OpenXRWindowsVulkanSubmissionStatus = OpenXRSubmissionStatus;
using OpenXRWindowsVulkanPresentation = OpenXRPresentation;
using OpenXRWindowsVulkanFrame = OpenXRBackendFrame;
struct OpenXRWindowsVulkanGraphicsRequirements {
XrVersion min_api_version = 0;
XrVersion max_api_version = 0;
};
// Same-device Dawn/OpenXR Vulkan backend.
//
// Startup is deliberately split in two. QueryGraphicsRequirements() runs
// after OpenXRRuntime::Initialize() but before aurora_initialize(), allowing
// OpenXR to create Dawn's Vulkan instance/device and select the physical GPU.
// BindAurora() runs afterwards and checks the device before creating a session.
//
// All methods from BeginFrame() through FinishFrame(), plus PollEvents on the
// associated OpenXRRuntime, belong to one XR pacing thread. Aurora's frame
// worker never calls OpenXR: its post-submit callback only publishes a token
// that WaitForSubmission() consumes. This is the synchronization boundary
// required by the asynchronous sealed-frame renderer.
class OpenXRWindowsVulkanBackend final {
public:
explicit OpenXRWindowsVulkanBackend(OpenXRLogCallback logger = {});
~OpenXRWindowsVulkanBackend();
OpenXRWindowsVulkanBackend(const OpenXRWindowsVulkanBackend&) = delete;
OpenXRWindowsVulkanBackend& operator=(const OpenXRWindowsVulkanBackend&) = delete;
OpenXRWindowsVulkanBackend(OpenXRWindowsVulkanBackend&&) = delete;
OpenXRWindowsVulkanBackend& operator=(OpenXRWindowsVulkanBackend&&) = delete;
bool QueryGraphicsRequirements(OpenXRRuntime& runtime);
bool BindAurora(OpenXRRuntime& runtime);
OpenXRWindowsVulkanBeginStatus BeginFrame(const OpenXRWindowsVulkanPresentation& presentation,
OpenXRWindowsVulkanFrame& frame);
// timeout_ms == UINT32_MAX waits until Aurora publishes this token or
// Shutdown() interrupts the wait. A timeout does not release XR images;
// the caller may keep pacing with RepeatFrame while Aurora still owns them.
OpenXRWindowsVulkanSubmissionStatus WaitForSubmission(const OpenXRWindowsVulkanFrame& frame,
uint32_t timeout_ms = UINT32_MAX);
// Withdraws this token only if Aurora has not encoded it. On success no GPU
// command can reference the acquired images, and FinishFrame(frame, false)
// is required to release them and close the compositor frame.
bool TryCancelPendingFrame(OpenXRWindowsVulkanFrame& frame);
// Ends the current compositor cycle with the last completed layer and starts
// another, without releasing or changing Aurora's pending images/render token.
// The original render poses remain attached to the pending and retained images.
bool RepeatFrame(const OpenXRWindowsVulkanFrame& frame);
// Releases acquired images and calls xrEndFrame. submit_layer must only be
// true after WaitForSubmission returned Success. Immersive frames submit
// XrCompositionLayerProjection; virtual-screen frames submit an
// XrCompositionLayerQuad using the single mono target, placed as the
// presentation's quad_anchored/quad_pose describe. If no new usable layer is
// available, resubmits the retained layer using the current display time.
bool FinishFrame(OpenXRWindowsVulkanFrame& frame, bool submit_layer);
// Render-first path when interpolation is off. Aurora renders into acquired
// non-retained XR images while no compositor frame is open. BeginFrameForPacket
// accepts only a completed packet; CopyRenderedEyes verifies the already queued
// bridge copy. FinishFrame releases the images and submits their original poses.
OpenXRBeginStatus PreparePacket(const OpenXRPresentation& presentation, OpenXRBackendFrame& packet);
bool TryCancelPendingPacket(OpenXRBackendFrame& packet);
OpenXRBeginStatus BeginFrameForPacket(const OpenXRBackendFrame& packet, OpenXRBackendFrame& frame);
OpenXRSubmissionStatus CopyRenderedEyes(const OpenXRBackendFrame& frame);
OpenXRBeginStatus KeepAliveCycle();
// Call on the XR owner thread after Aurora's worker is idle and before
// aurora_shutdown(). Safe to repeat. False means a submitted Vulkan command
// could not be fenced; the caller must retain this backend and its runtime
// for the process lifetime instead of destroying possibly live resources.
bool Shutdown();
bool IsBound() const;
const OpenXRWindowsVulkanGraphicsRequirements& GraphicsRequirements() const;
int64_t SwapchainFormat() const;
const std::string& LastError() const;
private:
class Impl;
std::unique_ptr<Impl> m_impl;
};
} // namespace mkw::vr
#endif // defined(MKW_ENABLE_OPENXR) && defined(_WIN32)
+64
View File
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "vr/openxr_d3d12.h"
#include "vr/openxr_vulkan_win32.h"
#include <variant>
namespace mkw::vr {
// Runtime API selection; both backends expose the same pacing/ownership contract.
class OpenXRWindowsBackend {
std::variant<std::unique_ptr<OpenXRD3D12Backend>, std::unique_ptr<OpenXRWindowsVulkanBackend>> backend_;
public:
OpenXRWindowsBackend(OpenXRLogCallback logger, bool vulkan) {
if (vulkan) backend_ = std::make_unique<OpenXRWindowsVulkanBackend>(logger);
else backend_ = std::make_unique<OpenXRD3D12Backend>(logger);
}
const OpenXRD3D12GraphicsRequirements& GraphicsRequirements() {
return std::get<0>(backend_)->GraphicsRequirements();
}
bool QueryGraphicsRequirements(OpenXRRuntime& runtime) {
return std::visit([&](auto& backend) -> bool { return backend->QueryGraphicsRequirements(runtime); }, backend_);
}
bool BindAurora(OpenXRRuntime& runtime) {
return std::visit([&](auto& backend) -> bool { return backend->BindAurora(runtime); }, backend_);
}
OpenXRBeginStatus BeginFrame(const OpenXRPresentation& presentation, OpenXRBackendFrame& frame) {
return std::visit([&](auto& backend) -> OpenXRBeginStatus { return backend->BeginFrame(presentation, frame); }, backend_);
}
OpenXRSubmissionStatus WaitForSubmission(const OpenXRBackendFrame& frame, uint32_t timeout) {
return std::visit([&](auto& backend) -> OpenXRSubmissionStatus { return backend->WaitForSubmission(frame, timeout); }, backend_);
}
bool TryCancelPendingFrame(OpenXRBackendFrame& frame) {
return std::visit([&](auto& backend) -> bool { return backend->TryCancelPendingFrame(frame); }, backend_);
}
bool RepeatFrame(const OpenXRBackendFrame& frame) {
return std::visit([&](auto& backend) -> bool { return backend->RepeatFrame(frame); }, backend_);
}
bool FinishFrame(OpenXRBackendFrame& frame, bool submit) {
return std::visit([&](auto& backend) -> bool { return backend->FinishFrame(frame, submit); }, backend_);
}
OpenXRBeginStatus PreparePacket(const OpenXRPresentation& presentation, OpenXRBackendFrame& frame) {
return std::visit([&](auto& backend) -> OpenXRBeginStatus { return backend->PreparePacket(presentation, frame); }, backend_);
}
bool TryCancelPendingPacket(OpenXRBackendFrame& frame) {
return std::visit([&](auto& backend) -> bool { return backend->TryCancelPendingPacket(frame); }, backend_);
}
OpenXRBeginStatus BeginFrameForPacket(const OpenXRBackendFrame& packet, OpenXRBackendFrame& frame) {
return std::visit([&](auto& backend) -> OpenXRBeginStatus { return backend->BeginFrameForPacket(packet, frame); }, backend_);
}
OpenXRSubmissionStatus CopyRenderedEyes(const OpenXRBackendFrame& frame) {
return std::visit([&](auto& backend) -> OpenXRSubmissionStatus { return backend->CopyRenderedEyes(frame); }, backend_);
}
OpenXRBeginStatus KeepAliveCycle() {
return std::visit([&](auto& backend) -> OpenXRBeginStatus { return backend->KeepAliveCycle(); }, backend_);
}
bool Shutdown() {
return std::visit([&](auto& backend) -> bool { return backend->Shutdown(); }, backend_);
}
int64_t SwapchainFormat() {
return std::visit([&](auto& backend) -> int64_t { return backend->SwapchainFormat(); }, backend_);
}
const std::string& LastError() {
return std::visit([&](auto& backend) -> const std::string& { return backend->LastError(); }, backend_);
}
};
}
+15 -5
View File
@@ -34,7 +34,7 @@
#include "vr/openxr_input.h"
#include "vr/openxr_runtime.h"
#if defined(_WIN32)
#include "vr/openxr_d3d12.h"
#include "vr/openxr_windows.h"
#define MKW_OPENXR_GRAPHICS_BACKEND 1
#elif defined(__ANDROID__)
#include "vr/openxr_android.h"
@@ -78,8 +78,8 @@ void ConfigurePolicy(bool enabled) noexcept {
#if MKW_OPENXR_GRAPHICS_BACKEND
#if defined(_WIN32)
using GraphicsBackend = OpenXRD3D12Backend;
inline constexpr const char* kGraphicsBackendName = "D3D12";
using GraphicsBackend = OpenXRWindowsBackend;
#else
using GraphicsBackend = OpenXRVulkanBackend;
inline constexpr const char* kGraphicsBackendName = "Vulkan";
@@ -360,7 +360,11 @@ public:
}
#endif
runtime_ = std::make_unique<OpenXRRuntime>(logger_);
#if defined(_WIN32)
backend_ = std::make_unique<GraphicsBackend>(logger_, kRequiredAuroraBackend == BACKEND_VULKAN);
#else
backend_ = std::make_unique<GraphicsBackend>(logger_);
#endif
OpenXRConfig config{};
config.application_name = aurora_config.appName != nullptr ? aurora_config.appName
@@ -368,7 +372,7 @@ public:
config.engine_name = "Aurora";
config.resolution_scale = RuntimeConfigFile::VrRenderScale();
#if defined(_WIN32)
config.required_extensions = {"XR_KHR_D3D12_enable"};
config.required_extensions = {kRequiredAuroraBackend == BACKEND_VULKAN ? "XR_KHR_vulkan_enable2" : "XR_KHR_D3D12_enable"};
config.optional_extensions = {"XR_KHR_win32_convert_performance_counter_time",
"XR_FB_display_refresh_rate", "XR_EXT_performance_settings"};
#else
@@ -567,7 +571,8 @@ private:
};
#if defined(_WIN32)
static constexpr AuroraBackend kRequiredAuroraBackend = BACKEND_D3D12;
AuroraBackend kRequiredAuroraBackend = BACKEND_D3D12;
const char* kGraphicsBackendName = "D3D12";
#else
static constexpr AuroraBackend kRequiredAuroraBackend = BACKEND_VULKAN;
#endif
@@ -576,6 +581,10 @@ private:
static constexpr uint32_t kMaxConsecutiveSkips = 300;
bool BackendMatchesConfiguredGraphicsApi(const AuroraConfig& aurora_config) {
#if defined(_WIN32)
kRequiredAuroraBackend = aurora_config.desiredBackend == BACKEND_VULKAN ? BACKEND_VULKAN : BACKEND_D3D12;
kGraphicsBackendName = kRequiredAuroraBackend == BACKEND_VULKAN ? "Vulkan" : "D3D12";
#endif
if (aurora_config.desiredBackend == BACKEND_AUTO ||
aurora_config.desiredBackend == kRequiredAuroraBackend) {
return true;
@@ -589,6 +598,7 @@ private:
aurora_config.desiredBackend = kRequiredAuroraBackend;
aurora_config.xrInterop = true;
#if defined(_WIN32)
if (kRequiredAuroraBackend != BACKEND_D3D12) return;
const auto& requirements = backend_->GraphicsRequirements();
aurora_config.hasD3D12AdapterLuid = true;
aurora_config.d3d12AdapterLuidLow = requirements.adapter_luid_low;
+5 -1
View File
@@ -724,6 +724,7 @@ bool OpenXRRuntime::BeginFrame(const OpenXRFrame& frame) {
XrFrameBeginInfo begin_info{XR_TYPE_FRAME_BEGIN_INFO};
const XrResult result = diagnostics::Measure(diagnostics::Stage::BeginCall, [&] {
const auto queue_guard = LockGraphicsQueue();
return xrBeginFrame(m_session, &begin_info);
});
if (XR_FAILED(result)) {
@@ -816,7 +817,10 @@ bool OpenXRRuntime::EndFrame(
end_info.layerCount = layer_count;
end_info.layers = layers;
const diagnostics::Stopwatch end_timer;
const XrResult result = xrEndFrame(m_session, &end_info);
const XrResult result = [&] {
const auto queue_guard = LockGraphicsQueue();
return xrEndFrame(m_session, &end_info);
}();
diagnostics::OnEndFrame(end_timer);
m_frame_phase = FramePhase::Idle;
m_active_frame_serial = 0;
File diff suppressed because it is too large. Load diff
+80 -1
View File
@@ -1,6 +1,23 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Exercise the real backend against a deterministic compositor and Aurora sink.
// No headset, graphics driver, OpenXR loader, or translated game is required.
#if defined(TEST_WINDOWS_VULKAN)
#define XR_USE_GRAPHICS_API_VULKAN
#include <vulkan/vulkan.h>
#include <openxr/openxr_platform.h>
#include <aurora/vulkan_win32_interop.h>
#include "vr/openxr_vulkan_win32.h"
#define OpenXRD3D12Backend OpenXRWindowsVulkanBackend
#define OpenXRD3D12BeginStatus OpenXRWindowsVulkanBeginStatus
#define OpenXRD3D12SubmissionStatus OpenXRWindowsVulkanSubmissionStatus
#define OpenXRD3D12Frame OpenXRWindowsVulkanFrame
#define OpenXRD3D12Presentation OpenXRWindowsVulkanPresentation
#define OpenXRD3D12FrameMode OpenXRWindowsVulkanFrameMode
#define aurora_d3d12_enable_stereo_bridge aurora_vulkan_win32_enable
#define aurora_d3d12_set_stereo_targets aurora_vulkan_win32_set_targets
#define aurora_d3d12_cancel_stereo_targets aurora_vulkan_win32_cancel
#define aurora_d3d12_disable_stereo_bridge aurora_vulkan_win32_disable
#else
#define CINTERFACE
#define XR_USE_GRAPHICS_API_D3D12
#ifndef NOMINMAX
@@ -10,8 +27,10 @@
#include <openxr/openxr_platform.h>
#include <aurora/d3d12_interop.h>
#include "vr/openxr_d3d12.h"
#endif
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <vector>
@@ -65,6 +84,26 @@ void Complete(bool success = true) {
encoded = false;
}
#if defined(TEST_WINDOWS_VULKAN)
AuroraDawnVulkanHooks hooks{};
bool queue_locked = false;
XrResult XRAPI_CALL Requirements(XrInstance, XrSystemId, XrGraphicsRequirementsVulkanKHR* out) {
out->minApiVersionSupported = XR_MAKE_VERSION(1, 1, 0);
out->maxApiVersionSupported = XR_MAKE_VERSION(1, 3, 0);
return XR_SUCCESS;
}
XrResult XRAPI_CALL Physical(XrInstance, const XrVulkanGraphicsDeviceGetInfoKHR*, VkPhysicalDevice* out) {
*out = reinterpret_cast<VkPhysicalDevice>(2); return XR_SUCCESS;
}
XrResult XRAPI_CALL CreateInstance(XrInstance, const XrVulkanInstanceCreateInfoKHR* info, VkInstance* out, VkResult* result) {
Require(info->vulkanCreateInfo->pApplicationInfo->apiVersion >= VK_API_VERSION_1_1);
*out = reinterpret_cast<VkInstance>(1); *result = VK_SUCCESS; return XR_SUCCESS;
}
XrResult XRAPI_CALL CreateDevice(XrInstance, const XrVulkanDeviceCreateInfoKHR* info, VkDevice* out, VkResult* result) {
Require(info->vulkanPhysicalDevice == reinterpret_cast<VkPhysicalDevice>(2));
*out = reinterpret_cast<VkDevice>(3); *result = VK_SUCCESS; return XR_SUCCESS;
}
#else
HRESULT STDMETHODCALLTYPE FeatureSupport(ID3D12Device*, D3D12_FEATURE,
void* data, UINT) {
static_cast<D3D12_FEATURE_DATA_FEATURE_LEVELS*>(data)->MaxSupportedFeatureLevel =
@@ -77,10 +116,28 @@ XrResult XRAPI_CALL Requirements(XrInstance, XrSystemId,
requirements->minFeatureLevel = D3D_FEATURE_LEVEL_11_0;
return XR_SUCCESS;
}
#endif
}
// A COM vtable in its C representation supplies the single device operation
// used by BindAurora. The production backend is compiled normally as C++.
#if defined(TEST_WINDOWS_VULKAN)
bool aurora_vulkan_win32_configure(const AuroraDawnVulkanHooks* value) {
hooks = value ? *value : AuroraDawnVulkanHooks{}; return true;
}
bool aurora_vulkan_win32_get_handles(AuroraDawnVulkanHandles* handles, int64_t* format) {
VkApplicationInfo app{VK_STRUCTURE_TYPE_APPLICATION_INFO};
app.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo instance{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; instance.pApplicationInfo = &app;
Require(hooks.createInstance(hooks.userdata, nullptr, &instance, nullptr, &handles->instance) == VK_SUCCESS);
Require(hooks.getPhysicalDevice(hooks.userdata, handles->instance, &handles->physicalDevice) == VK_SUCCESS);
VkDeviceCreateInfo device{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
Require(hooks.createDevice(hooks.userdata, nullptr, handles->physicalDevice, &device, nullptr, &handles->device) == VK_SUCCESS);
*format = VK_FORMAT_R8G8B8A8_UNORM; return true;
}
void* aurora_vulkan_win32_lock_queue() { Require(!queue_locked); queue_locked = true; return &queue_locked; }
void aurora_vulkan_win32_unlock_queue(void*) { Require(queue_locked); queue_locked = false; }
#else
bool aurora_d3d12_get_native_handles(AuroraD3D12NativeHandles* handles) {
static ID3D12DeviceVtbl vtable{};
vtable.CheckFeatureSupport = FeatureSupport;
@@ -88,6 +145,7 @@ bool aurora_d3d12_get_native_handles(AuroraD3D12NativeHandles* handles) {
*handles = {&device, &device, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 0};
return true;
}
#endif
bool aurora_d3d12_enable_stereo_bridge(AuroraD3D12StereoSubmittedCallback cb, void* data) {
callback = cb;
callback_data = data;
@@ -119,13 +177,21 @@ XrResult XRAPI_CALL xrCreateSwapchain(XrSession, const XrSwapchainCreateInfo*, X
XrResult XRAPI_CALL xrEnumerateSwapchainImages(XrSwapchain handle, uint32_t capacity,
uint32_t* count, XrSwapchainImageBaseHeader* images) {
*count = 1; // Single-image swapchains also require a separate retained pair.
#if defined(TEST_WINDOWS_VULKAN)
if (capacity) reinterpret_cast<XrSwapchainImageVulkanKHR*>(images)->image =
reinterpret_cast<VkImage>(&reinterpret_cast<Swapchain*>(handle)->image);
#else
if (capacity) reinterpret_cast<XrSwapchainImageD3D12KHR*>(images)->texture =
reinterpret_cast<ID3D12Resource*>(&reinterpret_cast<Swapchain*>(handle)->image);
#endif
return XR_SUCCESS;
}
XrResult XRAPI_CALL xrAcquireSwapchainImage(XrSwapchain handle,
const XrSwapchainImageAcquireInfo*, uint32_t* index) {
auto& chain = *reinterpret_cast<Swapchain*>(handle);
#if defined(TEST_WINDOWS_VULKAN)
Require(queue_locked);
#endif
Require(!chain.acquired);
chain.acquired = true;
chain.waited = false;
@@ -141,6 +207,9 @@ XrResult XRAPI_CALL xrWaitSwapchainImage(XrSwapchain handle, const XrSwapchainIm
XrResult XRAPI_CALL xrReleaseSwapchainImage(XrSwapchain handle,
const XrSwapchainImageReleaseInfo*) {
auto& chain = *reinterpret_cast<Swapchain*>(handle);
#if defined(TEST_WINDOWS_VULKAN)
Require(queue_locked); // The runtime may touch Dawn's VkQueue here.
#endif
Require(chain.acquired && chain.waited);
chain.acquired = false;
chain.released = true;
@@ -158,14 +227,24 @@ XrResult XRAPI_CALL xrDestroySwapchain(XrSwapchain handle) {
namespace mkw::vr {
OpenXRRuntime::OpenXRRuntime(OpenXRLogCallback) {
m_instance = reinterpret_cast<XrInstance>(this);
#if defined(TEST_WINDOWS_VULKAN)
m_swapchain_formats = {VK_FORMAT_R8G8B8A8_SRGB};
#else
m_swapchain_formats = {DXGI_FORMAT_R8G8B8A8_UNORM_SRGB};
#endif
for (auto& view : m_view_configuration) {
view.render_width = 100;
view.render_height = 80;
}
}
OpenXRRuntime::~OpenXRRuntime() = default;
bool OpenXRRuntime::GetInstanceProcAddress(const char*, PFN_xrVoidFunction* out) {
bool OpenXRRuntime::GetInstanceProcAddress(const char* name, PFN_xrVoidFunction* out) {
#if defined(TEST_WINDOWS_VULKAN)
if (std::strcmp(name, "xrCreateVulkanInstanceKHR") == 0) *out = reinterpret_cast<PFN_xrVoidFunction>(::CreateInstance);
else if (std::strcmp(name, "xrCreateVulkanDeviceKHR") == 0) *out = reinterpret_cast<PFN_xrVoidFunction>(CreateDevice);
else if (std::strcmp(name, "xrGetVulkanGraphicsDevice2KHR") == 0) *out = reinterpret_cast<PFN_xrVoidFunction>(Physical);
else
#endif
*out = reinterpret_cast<PFN_xrVoidFunction>(Requirements);
return true;
}