mirror of
https://github.com/thejerf/baijiSteamVR.git
synced 2026-10-21 01:02:00 +02:00
277 lines
9.3 KiB
C++
277 lines
9.3 KiB
C++
// Copyright 2026 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "VideoCommon/VR/OpenXRD3D11Common.h"
|
|
|
|
#ifdef ENABLE_VR
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
#include "Common/Logging/Log.h"
|
|
|
|
namespace VR::D3D11OpenXR
|
|
{
|
|
namespace
|
|
{
|
|
using PFNCreateDXGIFactory1 = HRESULT(WINAPI*)(REFIID, void**);
|
|
using PFNCreateDXGIFactory = HRESULT(WINAPI*)(REFIID, void**);
|
|
using PFND3D11CreateDevice =
|
|
HRESULT(WINAPI*)(IDXGIAdapter*, D3D_DRIVER_TYPE, HMODULE, UINT, const D3D_FEATURE_LEVEL*,
|
|
UINT, UINT, ID3D11Device**, D3D_FEATURE_LEVEL*, ID3D11DeviceContext**);
|
|
|
|
constexpr std::array<D3D_FEATURE_LEVEL, 3> SUPPORTED_FEATURE_LEVELS = {
|
|
D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0};
|
|
|
|
bool CreateDXGIFactoryDynamic(Common::DynamicLibrary* dxgi_library, IDXGIFactory1** out_factory)
|
|
{
|
|
if (!dxgi_library->Open("dxgi.dll"))
|
|
{
|
|
ERROR_LOG_FMT(OPENXR, "OpenXR: Failed to load dxgi.dll.");
|
|
return false;
|
|
}
|
|
|
|
PFNCreateDXGIFactory1 create_factory1 = nullptr;
|
|
if (dxgi_library->GetSymbol("CreateDXGIFactory1", &create_factory1) && create_factory1)
|
|
{
|
|
const HRESULT hr = create_factory1(__uuidof(IDXGIFactory1),
|
|
reinterpret_cast<void**>(out_factory));
|
|
if (SUCCEEDED(hr))
|
|
return true;
|
|
}
|
|
|
|
PFNCreateDXGIFactory create_factory = nullptr;
|
|
if (dxgi_library->GetSymbol("CreateDXGIFactory", &create_factory) && create_factory)
|
|
{
|
|
const HRESULT hr = create_factory(__uuidof(IDXGIFactory1),
|
|
reinterpret_cast<void**>(out_factory));
|
|
if (SUCCEEDED(hr))
|
|
return true;
|
|
}
|
|
|
|
ERROR_LOG_FMT(OPENXR, "OpenXR: Failed to create a DXGI factory for utility session.");
|
|
return false;
|
|
}
|
|
|
|
bool LuidMatches(const LUID& lhs, const LUID& rhs)
|
|
{
|
|
return lhs.HighPart == rhs.HighPart && lhs.LowPart == rhs.LowPart;
|
|
}
|
|
} // namespace
|
|
|
|
const char* FormatToString(int64_t format)
|
|
{
|
|
switch (static_cast<DXGI_FORMAT>(format))
|
|
{
|
|
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
|
return "DXGI_FORMAT_R8G8B8A8_UNORM";
|
|
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
|
|
return "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB";
|
|
case DXGI_FORMAT_B8G8R8A8_UNORM:
|
|
return "DXGI_FORMAT_B8G8R8A8_UNORM";
|
|
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
|
|
return "DXGI_FORMAT_B8G8R8A8_UNORM_SRGB";
|
|
case DXGI_FORMAT_R16G16B16A16_FLOAT:
|
|
return "DXGI_FORMAT_R16G16B16A16_FLOAT";
|
|
default:
|
|
return "UNKNOWN_DXGI_FORMAT";
|
|
}
|
|
}
|
|
|
|
bool QueryGraphicsRequirements(XrInstance instance, XrSystemId system_id,
|
|
XrGraphicsRequirementsD3D11KHR* out_requirements)
|
|
{
|
|
PFN_xrGetD3D11GraphicsRequirementsKHR get_requirements = nullptr;
|
|
const XrResult addr_result = xrGetInstanceProcAddr(
|
|
instance, "xrGetD3D11GraphicsRequirementsKHR",
|
|
reinterpret_cast<PFN_xrVoidFunction*>(&get_requirements));
|
|
if (XR_FAILED(addr_result) || get_requirements == nullptr)
|
|
{
|
|
ERROR_LOG_FMT(OPENXR, "OpenXR: Could not load xrGetD3D11GraphicsRequirementsKHR.");
|
|
return false;
|
|
}
|
|
|
|
*out_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR};
|
|
const XrResult result = get_requirements(instance, system_id, out_requirements);
|
|
if (XR_FAILED(result))
|
|
{
|
|
ERROR_LOG_FMT(OPENXR, "OpenXR: xrGetD3D11GraphicsRequirementsKHR failed ({}).",
|
|
static_cast<int>(result));
|
|
return false;
|
|
}
|
|
|
|
INFO_LOG_FMT(OPENXR,
|
|
"OpenXR: D3D11 requirements adapter LUID {:#010x}{:08x}, min feature level {:#x}",
|
|
out_requirements->adapterLuid.HighPart, out_requirements->adapterLuid.LowPart,
|
|
static_cast<int>(out_requirements->minFeatureLevel));
|
|
return true;
|
|
}
|
|
|
|
bool CreateMatchingDevice(const XrGraphicsRequirementsD3D11KHR& requirements,
|
|
bool enable_debug_layer, DeviceBundle* out_bundle)
|
|
{
|
|
if (!out_bundle)
|
|
return false;
|
|
|
|
PFND3D11CreateDevice d3d11_create_device = nullptr;
|
|
if (!out_bundle->d3d11_library.Open("d3d11.dll") ||
|
|
!out_bundle->d3d11_library.GetSymbol("D3D11CreateDevice", &d3d11_create_device))
|
|
{
|
|
ERROR_LOG_FMT(OPENXR, "OpenXR: Failed to load D3D11CreateDevice.");
|
|
return false;
|
|
}
|
|
|
|
IDXGIFactory1* raw_factory = nullptr;
|
|
if (!CreateDXGIFactoryDynamic(&out_bundle->dxgi_library, &raw_factory))
|
|
return false;
|
|
out_bundle->dxgi_factory.Attach(raw_factory);
|
|
|
|
for (UINT index = 0;; ++index)
|
|
{
|
|
Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
|
|
if (out_bundle->dxgi_factory->EnumAdapters(index, &adapter) == DXGI_ERROR_NOT_FOUND)
|
|
break;
|
|
|
|
DXGI_ADAPTER_DESC desc{};
|
|
if (FAILED(adapter->GetDesc(&desc)))
|
|
continue;
|
|
|
|
if (LuidMatches(desc.AdapterLuid, requirements.adapterLuid))
|
|
{
|
|
out_bundle->adapter = adapter;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!out_bundle->adapter)
|
|
{
|
|
WARN_LOG_FMT(OPENXR,
|
|
"OpenXR: No adapter matched the runtime LUID; falling back to the default "
|
|
"adapter.");
|
|
}
|
|
|
|
const auto try_create_device = [&](UINT flags) -> HRESULT {
|
|
return d3d11_create_device(out_bundle->adapter.Get(),
|
|
out_bundle->adapter ? D3D_DRIVER_TYPE_UNKNOWN :
|
|
D3D_DRIVER_TYPE_HARDWARE,
|
|
nullptr, flags, SUPPORTED_FEATURE_LEVELS.data(),
|
|
static_cast<UINT>(SUPPORTED_FEATURE_LEVELS.size()),
|
|
D3D11_SDK_VERSION, &out_bundle->device, &out_bundle->feature_level,
|
|
&out_bundle->context);
|
|
};
|
|
|
|
HRESULT hr = E_FAIL;
|
|
if (enable_debug_layer)
|
|
hr = try_create_device(D3D11_CREATE_DEVICE_DEBUG);
|
|
if (!enable_debug_layer || FAILED(hr))
|
|
hr = try_create_device(0);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ERROR_LOG_FMT(OPENXR, "OpenXR: D3D11CreateDevice failed ({:#010x}).",
|
|
static_cast<unsigned>(hr));
|
|
return false;
|
|
}
|
|
|
|
if (out_bundle->feature_level < requirements.minFeatureLevel)
|
|
{
|
|
ERROR_LOG_FMT(OPENXR,
|
|
"OpenXR: D3D11 device feature level {:#x} is below runtime minimum {:#x}.",
|
|
static_cast<int>(out_bundle->feature_level),
|
|
static_cast<int>(requirements.minFeatureLevel));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CreateSessionFromRequirements(XrInstance instance, XrSystemId system_id,
|
|
const XrGraphicsRequirementsD3D11KHR& requirements,
|
|
ID3D11Device* device, XrSession* out_session)
|
|
{
|
|
if (!device || !out_session)
|
|
return false;
|
|
|
|
XrGraphicsBindingD3D11KHR d3d11_binding{XR_TYPE_GRAPHICS_BINDING_D3D11_KHR};
|
|
d3d11_binding.device = device;
|
|
|
|
XrSessionCreateInfo session_info{XR_TYPE_SESSION_CREATE_INFO};
|
|
session_info.next = &d3d11_binding;
|
|
session_info.systemId = system_id;
|
|
|
|
*out_session = XR_NULL_HANDLE;
|
|
const XrResult result = xrCreateSession(instance, &session_info, out_session);
|
|
if (XR_FAILED(result))
|
|
{
|
|
ERROR_LOG_FMT(OPENXR,
|
|
"OpenXR: xrCreateSession failed ({}), adapter LUID {:#010x}{:08x}, minimum "
|
|
"feature level {:#x}.",
|
|
static_cast<int>(result), requirements.adapterLuid.HighPart,
|
|
requirements.adapterLuid.LowPart, static_cast<int>(requirements.minFeatureLevel));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SelectSwapchainFormat(XrSession session, int64_t* out_format)
|
|
{
|
|
uint32_t format_count = 0;
|
|
XrResult result = xrEnumerateSwapchainFormats(session, 0, &format_count, nullptr);
|
|
if (XR_FAILED(result) || format_count == 0)
|
|
{
|
|
ERROR_LOG_FMT(OPENXR, "OpenXR: xrEnumerateSwapchainFormats (count) failed ({}).",
|
|
static_cast<int>(result));
|
|
return false;
|
|
}
|
|
|
|
std::vector<int64_t> runtime_formats(format_count);
|
|
result = xrEnumerateSwapchainFormats(session, format_count, &format_count, runtime_formats.data());
|
|
if (XR_FAILED(result))
|
|
{
|
|
ERROR_LOG_FMT(OPENXR, "OpenXR: xrEnumerateSwapchainFormats failed ({}).",
|
|
static_cast<int>(result));
|
|
return false;
|
|
}
|
|
|
|
for (const int64_t format : runtime_formats)
|
|
INFO_LOG_FMT(OPENXR, "OpenXR: Runtime swapchain format {} ({})",
|
|
static_cast<long long>(format), FormatToString(format));
|
|
|
|
// Prefer sRGB formats so the OpenXR compositor correctly decodes Dolphin's
|
|
// gamma-encoded XFB. With a UNORM swapchain the runtime treats our sRGB bytes
|
|
// as linear and the image appears ~2x too bright.
|
|
static constexpr std::array<DXGI_FORMAT, 5> preferred_formats = {
|
|
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
|
|
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM,
|
|
DXGI_FORMAT_R16G16B16A16_FLOAT};
|
|
|
|
for (const DXGI_FORMAT preferred : preferred_formats)
|
|
{
|
|
const int64_t wanted = static_cast<int64_t>(preferred);
|
|
if (std::find(runtime_formats.begin(), runtime_formats.end(), wanted) != runtime_formats.end())
|
|
{
|
|
*out_format = wanted;
|
|
INFO_LOG_FMT(OPENXR, "OpenXR: Selected swapchain format {} ({}).",
|
|
static_cast<long long>(*out_format), FormatToString(*out_format));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
*out_format = runtime_formats.front();
|
|
WARN_LOG_FMT(OPENXR,
|
|
"OpenXR: No preferred D3D11 swapchain format found; falling back to runtime "
|
|
"format {} ({}).",
|
|
static_cast<long long>(*out_format), FormatToString(*out_format));
|
|
return true;
|
|
}
|
|
} // namespace VR::D3D11OpenXR
|
|
|
|
#endif
|
|
|
|
#endif // ENABLE_VR
|