From cdb8d54ca11b616581e800a8b8aab1863e5ef915 Mon Sep 17 00:00:00 2001
From: Ryan Pavlik <ryan.pavlik@collabora.com>
Date: Mon, 16 May 2022 10:44:47 -0500
Subject: [PATCH] st/oxr: Work on D3D11

---
 doc/changes/compositor/mr.943.md              |  2 +-
 src/xrt/state_trackers/oxr/CMakeLists.txt     |  6 ++
 src/xrt/state_trackers/oxr/oxr_api_funcs.h    |  8 ++
 .../state_trackers/oxr/oxr_api_negotiate.c    |  4 +
 src/xrt/state_trackers/oxr/oxr_api_system.c   | 27 ++++++
 src/xrt/state_trackers/oxr/oxr_api_verify.h   |  4 +
 src/xrt/state_trackers/oxr/oxr_d3d11.cpp      | 92 +++++++++++++++++++
 src/xrt/state_trackers/oxr/oxr_objects.h      | 41 +++++++++
 src/xrt/state_trackers/oxr/oxr_session.c      | 22 +++++
 .../oxr/oxr_session_gfx_d3d11.c               | 44 +++++++++
 .../state_trackers/oxr/oxr_swapchain_d3d11.c  | 78 ++++++++++++++++
 src/xrt/state_trackers/oxr/oxr_system.c       |  4 +
 src/xrt/state_trackers/oxr/oxr_verify.c       | 23 +++++
 13 files changed, 354 insertions(+), 1 deletion(-)
 create mode 100644 src/xrt/state_trackers/oxr/oxr_d3d11.cpp
 create mode 100644 src/xrt/state_trackers/oxr/oxr_session_gfx_d3d11.c
 create mode 100644 src/xrt/state_trackers/oxr/oxr_swapchain_d3d11.c

diff --git a/doc/changes/compositor/mr.943.md b/doc/changes/compositor/mr.943.md
index 1dde801d7..8668fa51f 100644
--- a/doc/changes/compositor/mr.943.md
+++ b/doc/changes/compositor/mr.943.md
@@ -2,4 +2,4 @@
 - mr.1263
 - mr.1295
 ---
-Work toward support for D3D11 client applications on Windows.
+Initial support for D3D11 client applications on Windows.
diff --git a/src/xrt/state_trackers/oxr/CMakeLists.txt b/src/xrt/state_trackers/oxr/CMakeLists.txt
index 7987e1d60..5d0bc54b8 100644
--- a/src/xrt/state_trackers/oxr/CMakeLists.txt
+++ b/src/xrt/state_trackers/oxr/CMakeLists.txt
@@ -69,6 +69,12 @@ if(XRT_HAVE_EGL)
 	target_sources(st_oxr PRIVATE oxr_session_gfx_egl.c)
 endif()
 
+if(XRT_HAVE_D3D11)
+	target_compile_definitions(st_oxr PRIVATE XR_USE_GRAPHICS_API_D3D11)
+	target_sources(st_oxr PRIVATE oxr_session_gfx_d3d11.c oxr_swapchain_d3d11.c oxr_d3d11.cpp)
+	target_link_libraries(st_oxr PRIVATE aux_d3d)
+endif()
+
 if(ANDROID)
 	target_compile_definitions(st_oxr PRIVATE XR_USE_PLATFORM_ANDROID)
 	target_sources(st_oxr PRIVATE oxr_session_gfx_gles_android.c)
diff --git a/src/xrt/state_trackers/oxr/oxr_api_funcs.h b/src/xrt/state_trackers/oxr/oxr_api_funcs.h
index b5f2e2bd5..881c3ecec 100644
--- a/src/xrt/state_trackers/oxr/oxr_api_funcs.h
+++ b/src/xrt/state_trackers/oxr/oxr_api_funcs.h
@@ -224,6 +224,14 @@ oxr_xrCreateVulkanDeviceKHR(XrInstance instance,
                             VkResult *vulkanResult);
 #endif
 
+#ifdef XR_USE_GRAPHICS_API_D3D11
+
+//! OpenXR API function @ep{xrGetD3D11GraphicsRequirementsKHR}
+XRAPI_ATTR XrResult XRAPI_CALL
+oxr_xrGetD3D11GraphicsRequirementsKHR(XrInstance instance,
+                                      XrSystemId systemId,
+                                      XrGraphicsRequirementsD3D11KHR *graphicsRequirements);
+#endif // XR_USE_GRAPHICS_API_D3D11
 
 /*
  *
diff --git a/src/xrt/state_trackers/oxr/oxr_api_negotiate.c b/src/xrt/state_trackers/oxr/oxr_api_negotiate.c
index b18663e0c..9c4ea45d5 100644
--- a/src/xrt/state_trackers/oxr/oxr_api_negotiate.c
+++ b/src/xrt/state_trackers/oxr/oxr_api_negotiate.c
@@ -265,6 +265,10 @@ handle_non_null(struct oxr_instance *inst, struct oxr_logger *log, const char *n
 	ENTRY_IF_EXT(xrCreateVulkanInstanceKHR, KHR_vulkan_enable2);
 #endif // OXR_HAVE_KHR_vulkan_enable2
 
+#ifdef OXR_HAVE_KHR_D3D11_enable
+	ENTRY_IF_EXT(xrGetD3D11GraphicsRequirementsKHR, KHR_D3D11_enable);
+#endif // OXR_HAVE_KHR_D3D11_enable
+
 	/*
 	 * Not logging here because there's no need to loudly advertise
 	 * which extensions the loader knows about (it calls this on
diff --git a/src/xrt/state_trackers/oxr/oxr_api_system.c b/src/xrt/state_trackers/oxr/oxr_api_system.c
index e7f712a72..70b43845d 100644
--- a/src/xrt/state_trackers/oxr/oxr_api_system.c
+++ b/src/xrt/state_trackers/oxr/oxr_api_system.c
@@ -417,3 +417,30 @@ oxr_xrCreateVulkanDeviceKHR(XrInstance instance,
 	return oxr_vk_create_vulkan_device(&log, sys, createInfo, vulkanDevice, vulkanResult);
 }
 #endif
+
+
+/*
+ *
+ * D3D11
+ *
+ */
+
+#ifdef XR_USE_GRAPHICS_API_D3D11
+
+XrResult
+oxr_xrGetD3D11GraphicsRequirementsKHR(XrInstance instance,
+                                      XrSystemId systemId,
+                                      XrGraphicsRequirementsD3D11KHR *graphicsRequirements)
+{
+
+	struct oxr_instance *inst;
+	struct oxr_logger log;
+	OXR_VERIFY_INSTANCE_AND_INIT_LOG(&log, instance, inst, "xrGetD3D11GraphicsRequirementsKHR");
+	OXR_VERIFY_ARG_TYPE_AND_NOT_NULL(&log, graphicsRequirements, XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR);
+	OXR_VERIFY_SYSTEM_AND_GET(&log, inst, systemId, sys);
+
+	sys->gotten_requirements = true;
+
+	return oxr_d3d11_get_requirements(&log, sys, graphicsRequirements);
+}
+#endif
diff --git a/src/xrt/state_trackers/oxr/oxr_api_verify.h b/src/xrt/state_trackers/oxr/oxr_api_verify.h
index ab7a1cc24..bd6cc5da3 100644
--- a/src/xrt/state_trackers/oxr/oxr_api_verify.h
+++ b/src/xrt/state_trackers/oxr/oxr_api_verify.h
@@ -276,6 +276,10 @@ oxr_verify_XrGraphicsBindingOpenGLESAndroidKHR(struct oxr_logger *, const XrGrap
 #endif // defined(XR_USE_PLATFORM_ANDROID) &&
        // defined(XR_USE_GRAPHICS_API_OPENGL_ES)
 
+#if defined(XR_USE_GRAPHICS_API_D3D11)
+XrResult
+oxr_verify_XrGraphicsBindingD3D11KHR(struct oxr_logger *, const XrGraphicsBindingD3D11KHR *);
+#endif // defined(XR_USE_GRAPHICS_API_D3D11)
 /*!
  * @}
  */
diff --git a/src/xrt/state_trackers/oxr/oxr_d3d11.cpp b/src/xrt/state_trackers/oxr/oxr_d3d11.cpp
new file mode 100644
index 000000000..32cb37e35
--- /dev/null
+++ b/src/xrt/state_trackers/oxr/oxr_d3d11.cpp
@@ -0,0 +1,92 @@
+// Copyright 2021, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Holds D3D11 related functions that didn't fit somewhere else.
+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
+ * @ingroup oxr_main
+ */
+
+#include "xrt/xrt_gfx_d3d11.h"
+
+#include "util/u_misc.h"
+#include "util/u_debug.h"
+#include "d3d/d3d_helpers.hpp"
+
+#include "oxr_objects.h"
+#include "oxr_logger.h"
+
+#include <dxgi1_6.h>
+#include <d3d11_4.h>
+#include <wil/com.h>
+#include <wil/result.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define DEFAULT_CATCH(MSG)                                                                                             \
+	catch (wil::ResultException const &e)                                                                          \
+	{                                                                                                              \
+		return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, MSG ": %s", e.what());                                 \
+	}                                                                                                              \
+	catch (std::exception const &e)                                                                                \
+	{                                                                                                              \
+		return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, MSG ": %s", e.what());                                 \
+	}                                                                                                              \
+	catch (...)                                                                                                    \
+	{                                                                                                              \
+		return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, MSG);                                                  \
+	}
+
+using namespace xrt::auxiliary::d3d;
+
+XrResult
+oxr_d3d11_get_requirements(struct oxr_logger *log,
+                           struct oxr_system *sys,
+                           XrGraphicsRequirementsD3D11KHR *graphicsRequirements)
+{
+	try {
+
+		if (sys->xsysc->info.client_d3d_deviceLUID_valid) {
+			sys->suggested_d3d_luid =
+			    reinterpret_cast<const LUID &>(sys->xsysc->info.client_d3d_deviceLUID);
+		} else {
+			auto adapter = getAdapterByIndex(0, U_LOGGING_INFO);
+			if (adapter == nullptr) {
+				return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, " failure enumerating adapter LUIDs.");
+			}
+			DXGI_ADAPTER_DESC desc{};
+			THROW_IF_FAILED(adapter->GetDesc(&desc));
+			sys->suggested_d3d_luid = desc.AdapterLuid;
+		}
+		sys->suggested_d3d_luid_valid = true;
+		graphicsRequirements->adapterLuid = sys->suggested_d3d_luid;
+		//! @todo implement better?
+		graphicsRequirements->minFeatureLevel = D3D_FEATURE_LEVEL_11_0;
+
+		return XR_SUCCESS;
+	}
+	DEFAULT_CATCH(" failure determining adapter LUID")
+}
+
+XrResult
+oxr_d3d11_check_device(struct oxr_logger *log, struct oxr_system *sys, ID3D11Device *device)
+{
+	try {
+		wil::com_ptr<IDXGIDevice> dxgiDevice;
+		THROW_IF_FAILED(device->QueryInterface(dxgiDevice.put()));
+		wil::com_ptr<IDXGIAdapter> adapter;
+		THROW_IF_FAILED(dxgiDevice->GetAdapter(adapter.put()));
+		DXGI_ADAPTER_DESC desc{};
+		adapter->GetDesc(&desc);
+		if (desc.AdapterLuid.HighPart != sys->suggested_d3d_luid.HighPart ||
+		    desc.AdapterLuid.LowPart != sys->suggested_d3d_luid.LowPart) {
+
+			return oxr_error(log, XR_ERROR_GRAPHICS_DEVICE_INVALID,
+			                 " supplied device does not match required LUID.");
+		}
+		return XR_SUCCESS;
+	}
+	DEFAULT_CATCH(" failure checking adapter LUID")
+}
diff --git a/src/xrt/state_trackers/oxr/oxr_objects.h b/src/xrt/state_trackers/oxr/oxr_objects.h
index 14fd65347..e5fc0d391 100644
--- a/src/xrt/state_trackers/oxr/oxr_objects.h
+++ b/src/xrt/state_trackers/oxr/oxr_objects.h
@@ -1110,6 +1110,42 @@ oxr_session_populate_egl(struct oxr_logger *log,
 #endif
 
 
+/*
+ *
+ * D3D11, located in various files.
+ *
+ */
+
+#ifdef XR_USE_GRAPHICS_API_D3D11
+
+XrResult
+oxr_d3d11_get_requirements(struct oxr_logger *log,
+                           struct oxr_system *sys,
+                           XrGraphicsRequirementsD3D11KHR *graphicsRequirements);
+
+/**
+ * @brief Check to ensure the device provided at session create matches the LUID we returned earlier.
+ *
+ * @return XR_SUCCESS if the device matches the LUID
+ */
+XrResult
+oxr_d3d11_check_device(struct oxr_logger *log, struct oxr_system *sys, ID3D11Device *device);
+
+
+XrResult
+oxr_session_populate_d3d11(struct oxr_logger *log,
+                           struct oxr_system *sys,
+                           XrGraphicsBindingD3D11KHR const *next,
+                           struct oxr_session *sess);
+
+XrResult
+oxr_swapchain_d3d11_create(struct oxr_logger *,
+                           struct oxr_session *sess,
+                           const XrSwapchainCreateInfo *,
+                           struct oxr_swapchain **out_swapchain);
+
+#endif
+
 /*
  *
  * Structs
@@ -1205,6 +1241,10 @@ struct oxr_system
 	} vk;
 
 #endif
+#ifdef XR_USE_GRAPHICS_API_D3D11
+	LUID suggested_d3d_luid;
+	bool suggested_d3d_luid_valid;
+#endif
 };
 
 #define GET_XDEV_BY_ROLE(SYS, ROLE) ((SYS)->xsysd->roles.ROLE)
@@ -1219,6 +1259,7 @@ struct oxr_extension_status
 {
 	OXR_EXTENSION_SUPPORT_GENERATE(MAKE_EXT_STATUS)
 };
+#undef MAKE_EXT_STATUS
 
 /*!
  * Main object that ties everything together.
diff --git a/src/xrt/state_trackers/oxr/oxr_session.c b/src/xrt/state_trackers/oxr/oxr_session.c
index 4128fd45e..a3dce932c 100644
--- a/src/xrt/state_trackers/oxr/oxr_session.c
+++ b/src/xrt/state_trackers/oxr/oxr_session.c
@@ -734,6 +734,28 @@ oxr_session_create_impl(struct oxr_logger *log,
 	}
 #endif
 
+#ifdef XR_USE_GRAPHICS_API_D3D11
+	XrGraphicsBindingD3D11KHR const *d3d11 =
+	    OXR_GET_INPUT_FROM_CHAIN(createInfo, XR_TYPE_GRAPHICS_BINDING_D3D11_KHR, XrGraphicsBindingD3D11KHR);
+	if (d3d11 != NULL) {
+		OXR_VERIFY_ARG_NOT_NULL(log, d3d11->device);
+
+		if (!sys->gotten_requirements) {
+			return oxr_error(log, XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING,
+			                 "Has not called xrGetD3D11GraphicsRequirementsKHR");
+		}
+		XrResult result = oxr_d3d11_check_device(log, sys, d3d11->device);
+
+		if (!XR_SUCCEEDED(result)) {
+			return result;
+		}
+
+
+		OXR_SESSION_ALLOCATE(log, sys, *out_session);
+		OXR_ALLOCATE_NATIVE_COMPOSITOR(log, xsi, *out_session);
+		return oxr_session_populate_d3d11(log, sys, d3d11, *out_session);
+	}
+#endif
 	/*
 	 * Add any new graphics binding structs here - before the headless
 	 * check. (order for non-headless checks not specified in standard.)
diff --git a/src/xrt/state_trackers/oxr/oxr_session_gfx_d3d11.c b/src/xrt/state_trackers/oxr/oxr_session_gfx_d3d11.c
new file mode 100644
index 000000000..0b070a901
--- /dev/null
+++ b/src/xrt/state_trackers/oxr/oxr_session_gfx_d3d11.c
@@ -0,0 +1,44 @@
+// Copyright 2018-2021, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Holds D3D11 specific session functions.
+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup oxr_main
+ * @ingroup comp_client
+ */
+
+#include <stdlib.h>
+
+#include "util/u_misc.h"
+
+#include "xrt/xrt_instance.h"
+#include "xrt/xrt_gfx_d3d11.h"
+
+#include "oxr_objects.h"
+#include "oxr_logger.h"
+#include "oxr_two_call.h"
+#include "oxr_handle.h"
+
+
+XrResult
+oxr_session_populate_d3d11(struct oxr_logger *log,
+                           struct oxr_system *sys,
+                           XrGraphicsBindingD3D11KHR const *next,
+                           struct oxr_session *sess)
+{
+	struct xrt_compositor_native *xcn = sess->xcn;
+	struct xrt_compositor_d3d11 *xcd3d = xrt_gfx_d3d11_provider_create( //
+	    xcn,                                                            //
+	    next->device);                                                  //
+
+	if (xcd3d == NULL) {
+		return oxr_error(log, XR_ERROR_INITIALIZATION_FAILED, "Failed to create a d3d11 client compositor");
+	}
+
+	sess->compositor = &xcd3d->base;
+	sess->create_swapchain = oxr_swapchain_d3d11_create;
+
+	return XR_SUCCESS;
+}
diff --git a/src/xrt/state_trackers/oxr/oxr_swapchain_d3d11.c b/src/xrt/state_trackers/oxr/oxr_swapchain_d3d11.c
new file mode 100644
index 000000000..9a68881d2
--- /dev/null
+++ b/src/xrt/state_trackers/oxr/oxr_swapchain_d3d11.c
@@ -0,0 +1,78 @@
+// Copyright 2019-2021, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Holds D3D11 swapchain related functions.
+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup oxr_main
+ * @ingroup comp_client
+ */
+
+#include <stdlib.h>
+
+#include "xrt/xrt_gfx_d3d11.h"
+#include "util/u_debug.h"
+
+#include "oxr_objects.h"
+#include "oxr_logger.h"
+
+
+static XrResult
+oxr_swapchain_d3d11_destroy(struct oxr_logger *log, struct oxr_swapchain *sc)
+{
+	// Release any waited image.
+	if (sc->waited.yes) {
+		sc->release_image(log, sc, NULL);
+	}
+
+	// Release any acquired images.
+	XrSwapchainImageWaitInfo waitInfo = {0};
+	while (!u_index_fifo_is_empty(&sc->acquired.fifo)) {
+		sc->wait_image(log, sc, &waitInfo);
+		sc->release_image(log, sc, NULL);
+	}
+
+	// Drop our reference, does NULL checking.
+	xrt_swapchain_reference(&sc->swapchain, NULL);
+
+	return XR_SUCCESS;
+}
+
+static XrResult
+oxr_swapchain_d3d11_enumerate_images(struct oxr_logger *log,
+                                     struct oxr_swapchain *sc,
+                                     uint32_t count,
+                                     XrSwapchainImageBaseHeader *images)
+{
+	struct xrt_swapchain_d3d11 *xscd3d = (struct xrt_swapchain_d3d11 *)sc->swapchain;
+	XrSwapchainImageD3D11KHR *d3d_imgs = (XrSwapchainImageD3D11KHR *)images;
+
+	for (uint32_t i = 0; i < count; i++) {
+		d3d_imgs[i].texture = xscd3d->images[i];
+	}
+
+	return oxr_session_success_result(sc->sess);
+}
+
+XrResult
+oxr_swapchain_d3d11_create(struct oxr_logger *log,
+                           struct oxr_session *sess,
+                           const XrSwapchainCreateInfo *createInfo,
+                           struct oxr_swapchain **out_swapchain)
+{
+	struct oxr_swapchain *sc;
+	XrResult ret;
+
+	ret = oxr_create_swapchain(log, sess, createInfo, &sc);
+	if (ret != XR_SUCCESS) {
+		return ret;
+	}
+
+	sc->destroy = oxr_swapchain_d3d11_destroy;
+	sc->enumerate_images = oxr_swapchain_d3d11_enumerate_images;
+
+	*out_swapchain = sc;
+
+	return XR_SUCCESS;
+}
diff --git a/src/xrt/state_trackers/oxr/oxr_system.c b/src/xrt/state_trackers/oxr/oxr_system.c
index 1b72b9327..1a77d8c0f 100644
--- a/src/xrt/state_trackers/oxr/oxr_system.c
+++ b/src/xrt/state_trackers/oxr/oxr_system.c
@@ -103,6 +103,10 @@ oxr_system_fill_in(struct oxr_logger *log, struct oxr_instance *inst, XrSystemId
 	sys->vulkan_enable2_instance = VK_NULL_HANDLE;
 	sys->suggested_vulkan_physical_device = VK_NULL_HANDLE;
 #endif
+#ifdef XR_USE_GRAPHICS_API_D3D11
+	U_ZERO(&(sys->suggested_d3d_luid));
+	sys->suggested_d3d_luid_valid = false;
+#endif
 
 	// Headless.
 	if (sys->xsysc == NULL) {
diff --git a/src/xrt/state_trackers/oxr/oxr_verify.c b/src/xrt/state_trackers/oxr/oxr_verify.c
index 4a026e7d9..689210dd2 100644
--- a/src/xrt/state_trackers/oxr/oxr_verify.c
+++ b/src/xrt/state_trackers/oxr/oxr_verify.c
@@ -494,6 +494,15 @@ oxr_verify_XrSessionCreateInfo(struct oxr_logger *log,
 	}
 #endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_USE_GRAPHICS_API_OPENGL_ES)
 
+#if defined(XR_USE_GRAPHICS_API_D3D11)
+	XrGraphicsBindingD3D11KHR const *d3d11 =
+	    OXR_GET_INPUT_FROM_CHAIN(createInfo, XR_TYPE_GRAPHICS_BINDING_D3D11_KHR, XrGraphicsBindingD3D11KHR);
+	if (d3d11 != NULL) {
+		OXR_VERIFY_EXTENSION(log, inst, KHR_D3D11_enable);
+		return oxr_verify_XrGraphicsBindingD3D11KHR(log, d3d11);
+	}
+#endif // XR_USE_GRAPHICS_API_D3D11
+
 	/*
 	 * Add any new graphics binding structs here - before the headless
 	 * check. (order for non-headless checks not specified in standard.)
@@ -603,3 +612,17 @@ oxr_verify_XrGraphicsBindingOpenGLESAndroidKHR(struct oxr_logger *log, const XrG
 }
 #endif // defined(XR_USE_PLATFORM_ANDROID) &&
        // defined(XR_USE_GRAPHICS_API_OPENGL_ES)
+
+#if defined(XR_USE_GRAPHICS_API_D3D11)
+XrResult
+oxr_verify_XrGraphicsBindingD3D11KHR(struct oxr_logger *log, const XrGraphicsBindingD3D11KHR *next)
+{
+	if (next->type != XR_TYPE_GRAPHICS_BINDING_D3D11_KHR) {
+		return oxr_error(log, XR_ERROR_VALIDATION_FAILURE, "Graphics binding has invalid type");
+	}
+	if (next->device == NULL) {
+		return oxr_error(log, XR_ERROR_VALIDATION_FAILURE, "XrGraphicsBindingD3D11KHR::device cannot be NULL");
+	}
+	return XR_SUCCESS;
+}
+#endif // defined(XR_USE_GRAPHICS_API_D3D11)