From a9facfe6712984751e9566271db29c497e8ff226 Mon Sep 17 00:00:00 2001
From: Ryan Pavlik <ryan.pavlik@collabora.com>
Date: Mon, 11 Jul 2022 14:55:17 -0500
Subject: [PATCH] c/client: Extract some utilities usable by D3D11 and 12

---
 doc/changes/compositor/mr.943.md              |   1 +
 src/xrt/compositor/CMakeLists.txt             |  11 +-
 .../compositor/client/comp_d3d11_client.cpp   | 125 +++------------
 src/xrt/compositor/client/comp_d3d_common.cpp | 144 ++++++++++++++++++
 src/xrt/compositor/client/comp_d3d_common.hpp | 144 ++++++++++++++++++
 5 files changed, 319 insertions(+), 106 deletions(-)
 create mode 100644 src/xrt/compositor/client/comp_d3d_common.cpp
 create mode 100644 src/xrt/compositor/client/comp_d3d_common.hpp

diff --git a/doc/changes/compositor/mr.943.md b/doc/changes/compositor/mr.943.md
index 8d1725f18..7438007dc 100644
--- a/doc/changes/compositor/mr.943.md
+++ b/doc/changes/compositor/mr.943.md
@@ -5,6 +5,7 @@
 - mr.1326
 - mr.1302
 - mr.1337
+- mr.1340
 ---
 
 Full support for D3D11 client applications on Windows.
diff --git a/src/xrt/compositor/CMakeLists.txt b/src/xrt/compositor/CMakeLists.txt
index 97b65fbb5..840644f1e 100644
--- a/src/xrt/compositor/CMakeLists.txt
+++ b/src/xrt/compositor/CMakeLists.txt
@@ -58,6 +58,14 @@ if(XRT_HAVE_EGL)
 	target_sources(comp_client PRIVATE client/comp_egl_client.c client/comp_egl_client.h)
 endif()
 
+if(XRT_HAVE_D3D11 OR XRT_HAVE_D3D12)
+	target_link_libraries(comp_client PRIVATE aux_d3d)
+endif()
+
+if(XRT_HAVE_D3D11 OR XRT_HAVE_D3D12)
+	target_sources(comp_client PRIVATE client/comp_d3d_common.cpp client/comp_d3d_common.hpp)
+endif()
+
 if(XRT_HAVE_D3D11)
 	target_sources(
 		comp_client PRIVATE client/comp_d3d11_client.cpp client/comp_d3d11_client.h
@@ -65,6 +73,7 @@ if(XRT_HAVE_D3D11)
 		)
 	target_link_libraries(comp_client PRIVATE aux_d3d)
 endif()
+
 ##
 # Util library
 #
@@ -259,7 +268,6 @@ if(XRT_FEATURE_COMPOSITOR_MAIN)
 	endif()
 endif()
 
-
 ###
 # Null compositor
 #
@@ -268,7 +276,6 @@ if(XRT_FEATURE_COMPOSITOR_NULL)
 	add_subdirectory(null)
 endif()
 
-
 ###
 # Multi client compositor library
 #
diff --git a/src/xrt/compositor/client/comp_d3d11_client.cpp b/src/xrt/compositor/client/comp_d3d11_client.cpp
index c2772609b..77871bbb8 100644
--- a/src/xrt/compositor/client/comp_d3d11_client.cpp
+++ b/src/xrt/compositor/client/comp_d3d11_client.cpp
@@ -10,6 +10,8 @@
 
 #include "comp_d3d11_client.h"
 
+#include "comp_d3d_common.hpp"
+
 #include "xrt/xrt_compositor.h"
 #include "xrt/xrt_config_os.h"
 #include "xrt/xrt_handles.h"
@@ -45,6 +47,8 @@
 using namespace std::chrono_literals;
 using namespace std::chrono;
 
+using xrt::compositor::client::unique_swapchain_ref;
+
 DEBUG_GET_ONCE_LOG_OPTION(log, "D3D_COMPOSITOR_LOG", U_LOGGING_INFO)
 
 /*!
@@ -86,10 +90,6 @@ using unique_compositor_semaphore_ref = std::unique_ptr<
     struct xrt_compositor_semaphore,
     xrt::deleters::reference_deleter<struct xrt_compositor_semaphore, xrt_compositor_semaphore_reference>>;
 
-using unique_swapchain_ref =
-    std::unique_ptr<struct xrt_swapchain,
-                    xrt::deleters::reference_deleter<struct xrt_swapchain, xrt_swapchain_reference>>;
-
 // 0 is special
 static constexpr uint64_t kKeyedMutexKey = 0;
 
@@ -182,6 +182,10 @@ convertTimeoutToWindowsMilliseconds(uint64_t timeout_ns)
  */
 struct client_d3d11_swapchain_data
 {
+	explicit client_d3d11_swapchain_data(enum u_logging_level log_level) : keyed_mutex_collection(log_level) {}
+
+	xrt::compositor::client::KeyedMutexCollection keyed_mutex_collection;
+
 	//! The shared handles for all our images
 	std::vector<wil::unique_handle> handles;
 
@@ -190,16 +194,6 @@ struct client_d3d11_swapchain_data
 
 	//! Images associated with client_d3d11_compositor::comp_device
 	std::vector<wil::com_ptr<ID3D11Texture2D1>> comp_images;
-
-	//! Keyed mutex per image associated with client_d3d11_compositor::app_device
-	std::vector<wil::com_ptr<IDXGIKeyedMutex>> app_keyed_mutex_collection;
-
-	std::vector<bool> keyed_mutex_acquired;
-
-	xrt_result_t
-	waitImage(client_d3d11_swapchain *sc, uint32_t index, uint64_t timeout_ns);
-	xrt_result_t
-	releaseImage(client_d3d11_swapchain *sc, uint32_t index);
 };
 
 /*!
@@ -265,63 +259,6 @@ formatMessage(DWORD err, char (&buf)[N])
 }
 
 
-/*
- *
- * Helpers for Swapchain
- *
- */
-
-inline xrt_result_t
-client_d3d11_swapchain_data::waitImage(client_d3d11_swapchain *sc, uint32_t index, uint64_t timeout_ns)
-{
-	if (keyed_mutex_acquired[index]) {
-
-		D3D_WARN(sc->c, "Will not acquire the keyed mutex for image %" PRId32 " - it was already acquired!",
-		         index);
-		return XRT_ERROR_NO_IMAGE_AVAILABLE;
-	}
-
-	auto hr = app_keyed_mutex_collection[index]->AcquireSync(kKeyedMutexKey,
-	                                                         convertTimeoutToWindowsMilliseconds(timeout_ns));
-	if (hr == WAIT_ABANDONED) {
-		D3D_ERROR(sc->c,
-		          "Could not acquire the keyed mutex for image %" PRId32
-		          " due to it being in an inconsistent state",
-		          index);
-		return XRT_ERROR_D3D11;
-	}
-	if (hr == WAIT_TIMEOUT) {
-		return XRT_TIMEOUT;
-	}
-	if (FAILED(hr)) {
-		D3D_ERROR(sc->c, "Could not acquire the keyed mutex for image %" PRId32, index);
-		return XRT_ERROR_D3D11;
-	}
-	keyed_mutex_acquired[index] = true;
-	sc->c->app_context->Flush();
-	//! @todo this causes an error in the debug layer
-	// Discard old contents
-	// sc->c->app_context->DiscardResource(wil::com_raw_ptr(app_images[index]));
-	return XRT_SUCCESS;
-}
-
-inline xrt_result_t
-client_d3d11_swapchain_data::releaseImage(client_d3d11_swapchain *sc, uint32_t index)
-{
-	if (!keyed_mutex_acquired[index]) {
-
-		D3D_WARN(sc->c, "Will not release the keyed mutex for image %" PRId32 " - it was not acquired!", index);
-		return XRT_ERROR_D3D11;
-	}
-	auto hr = LOG_IF_FAILED(app_keyed_mutex_collection[index]->ReleaseSync(kKeyedMutexKey));
-	if (FAILED(hr)) {
-		D3D_ERROR(sc->c, "Could not release the keyed mutex for image %" PRId32, index);
-		return XRT_ERROR_D3D11;
-	}
-	sc->data->keyed_mutex_acquired[index] = false;
-	return XRT_SUCCESS;
-}
-
 /*
  *
  * Swapchain functions.
@@ -347,8 +284,10 @@ client_d3d11_swapchain_wait_image(struct xrt_swapchain *xsc, uint64_t timeout_ns
 
 	if (xret == XRT_SUCCESS) {
 		// OK, we got the image in the native compositor, now need the keyed mutex in d3d11.
-		return sc->data->waitImage(sc, index, timeout_ns);
+		xret = sc->data->keyed_mutex_collection.waitKeyedMutex(index, timeout_ns);
 	}
+
+	//! @todo discard old contents?
 	return xret;
 }
 
@@ -361,7 +300,8 @@ client_d3d11_swapchain_release_image(struct xrt_swapchain *xsc, uint32_t index)
 	xrt_result_t xret = xrt_swapchain_release_image(sc->xsc.get(), index);
 
 	if (xret == XRT_SUCCESS) {
-		return sc->data->releaseImage(sc, index);
+		// Release the keyed mutex
+		xret = sc->data->keyed_mutex_collection.releaseKeyedMutex(index);
 	}
 	return xret;
 }
@@ -439,15 +379,13 @@ try {
 	vkinfo.format = vk_format;
 
 	std::unique_ptr<struct client_d3d11_swapchain> sc = std::make_unique<struct client_d3d11_swapchain>();
-	sc->data = std::make_unique<client_d3d11_swapchain_data>();
+	sc->data = std::make_unique<client_d3d11_swapchain_data>(c->log_level);
 	auto &data = sc->data;
 	xret = xrt::auxiliary::d3d::d3d11::allocateSharedImages(*(c->comp_device), xinfo, image_count, true,
 	                                                        data->comp_images, data->handles);
 	if (xret != XRT_SUCCESS) {
 		return xret;
 	}
-	data->keyed_mutex_acquired.resize(image_count);
-	sc->data->app_keyed_mutex_collection.reserve(image_count);
 	data->app_images.reserve(image_count);
 
 	// Import from the handle for the app.
@@ -458,45 +396,24 @@ try {
 		// Put the image where the OpenXR state tracker can get it
 		sc->base.images[i] = image.get();
 
-		// Cache the keyed mutex interface too
-		sc->data->app_keyed_mutex_collection.emplace_back(image.query<IDXGIKeyedMutex>());
-
 		// Store the owning pointer for lifetime management
 		data->app_images.emplace_back(std::move(image));
 	}
 
-	// Populate for import
-	std::vector<xrt_image_native> xins;
-	xins.reserve(data->handles.size());
-	// Keep this around until after successful import, then detach all.
-	std::vector<wil::unique_handle> handlesForImport;
-	handlesForImport.reserve(data->handles.size());
-
-	for (const wil::unique_handle &handle : data->handles) {
-		wil::unique_handle duped{u_graphics_buffer_ref(handle.get())};
-		xrt_image_native xin;
-		xin.handle = duped.get();
-		xin.size = 0;
-		xin.use_dedicated_allocation = false; //! @todo not sure
-
-		handlesForImport.emplace_back(std::move(duped));
-		xins.emplace_back(xin);
+	// Cache the keyed mutex interface
+	xret = data->keyed_mutex_collection.init(data->app_images);
+	if (xret != XRT_SUCCESS) {
+		D3D_ERROR(c, "Error retrieving keyex mutex interfaces");
+		return xret;
 	}
 
 	// Import into the native compositor, to create the corresponding swapchain which we wrap.
-	xrt_swapchain *xsc = nullptr;
-	xret = xrt_comp_import_swapchain(&(c->xcn->base), &vkinfo, xins.data(), image_count, &xsc);
+	xret = xrt::compositor::client::importFromHandleDuplicates(
+	    *(c->xcn), data->handles, vkinfo, false /** @todo not sure - dedicated allocation */, sc->xsc);
 	if (xret != XRT_SUCCESS) {
 		D3D_ERROR(c, "Error importing D3D11 swapchain into native compositor");
 		return xret;
 	}
-	// The imported swapchain took ownership of them now, release them from ownership here.
-	for (auto &h : handlesForImport) {
-		h.release();
-	}
-
-	// Let unique_ptr manage the lifetime of xsc now
-	sc->xsc.reset(xsc);
 
 	sc->base.base.destroy = client_d3d11_swapchain_destroy;
 	sc->base.base.acquire_image = client_d3d11_swapchain_acquire_image;
diff --git a/src/xrt/compositor/client/comp_d3d_common.cpp b/src/xrt/compositor/client/comp_d3d_common.cpp
new file mode 100644
index 000000000..eab6904cb
--- /dev/null
+++ b/src/xrt/compositor/client/comp_d3d_common.cpp
@@ -0,0 +1,144 @@
+// Copyright 2019-2022, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  D3D12 client side glue to compositor implementation.
+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup comp_client
+ */
+
+#include "comp_d3d_common.hpp"
+
+#include "util/u_logging.h"
+#include "util/u_time.h"
+
+#include <inttypes.h>
+
+
+#define D3D_COMMON_SPEW(log_level, ...) U_LOG_IFL_T(log_level, __VA_ARGS__);
+
+#define D3D_COMMON_DEBUG(log_level, ...) U_LOG_IFL_D(log_level, __VA_ARGS__);
+
+#define D3D_COMMON_INFO(log_level, ...) U_LOG_IFL_I(log_level, __VA_ARGS__);
+
+#define D3D_COMMON_WARN(log_level, ...) U_LOG_IFL_W(log_level, __VA_ARGS__);
+
+#define D3D_COMMON_ERROR(log_level, ...) U_LOG_IFL_E(log_level, __VA_ARGS__);
+
+namespace xrt::compositor::client {
+
+// xrt_result_t
+// importFromHandleDuplicates(xrt_compositor_native &xcn,
+//                            std::vector<wil::unique_handle> const &handles,
+//                            const xrt_swapchain_create_info &vkinfo,
+//                            bool use_dedicated_allocation,
+//                            unique_swapchain_ref &out_xsc)
+
+
+static inline DWORD
+convertTimeoutToWindowsMilliseconds(uint64_t timeout_ns)
+{
+	return (timeout_ns == XRT_INFINITE_DURATION) ? INFINITE : (DWORD)(timeout_ns / (uint64_t)U_TIME_1MS_IN_NS);
+}
+
+KeyedMutexCollection::KeyedMutexCollection(u_logging_level log_level) noexcept : log_level(log_level) {}
+
+xrt_result_t
+KeyedMutexCollection::init(const std::vector<wil::com_ptr<ID3D11Texture2D1>> &images) noexcept
+try {
+	keyed_mutex_collection.clear();
+	keyed_mutex_collection.reserve(images.size());
+	for (const auto &image : images) {
+		keyed_mutex_collection.emplace_back(image.query<IDXGIKeyedMutex>());
+	}
+
+	keyed_mutex_acquired.clear();
+	keyed_mutex_acquired.resize(keyed_mutex_collection.size());
+	return XRT_SUCCESS;
+} catch (wil::ResultException const &e) {
+	U_LOG_E("Error getting keyed mutex collection for swapchain: %s", e.what());
+	return XRT_ERROR_D3D;
+} catch (std::exception const &e) {
+	U_LOG_E("Error getting keyed mutex collection for swapchain: %s", e.what());
+	return XRT_ERROR_D3D;
+} catch (...) {
+	U_LOG_E("Error getting keyed mutex collection for swapchain");
+	return XRT_ERROR_D3D;
+}
+
+xrt_result_t
+KeyedMutexCollection::waitKeyedMutex(uint32_t index, uint64_t timeout_ns) noexcept
+
+try {
+
+	if (keyed_mutex_acquired[index]) {
+
+		D3D_COMMON_WARN(log_level,
+		                "Will not acquire the keyed mutex for image %" PRId32 " - it was already acquired!",
+		                index);
+		return XRT_ERROR_NO_IMAGE_AVAILABLE;
+	}
+
+	auto hr =
+	    keyed_mutex_collection[index]->AcquireSync(kKeyedMutexKey, convertTimeoutToWindowsMilliseconds(timeout_ns));
+	if (hr == WAIT_ABANDONED) {
+		D3D_COMMON_ERROR(log_level,
+		                 "Could not acquire the keyed mutex for image %" PRId32
+		                 " due to it being in an inconsistent state",
+		                 index);
+		return XRT_ERROR_D3D;
+	}
+	if (hr == WAIT_TIMEOUT) {
+		return XRT_TIMEOUT;
+	}
+	if (FAILED(hr)) {
+		D3D_COMMON_ERROR(log_level, "Could not acquire the keyed mutex for image %" PRId32, index);
+		return XRT_ERROR_D3D;
+	}
+	keyed_mutex_acquired[index] = true;
+	return XRT_SUCCESS;
+} catch (wil::ResultException const &e) {
+	U_LOG_E("Error acquiring keyed mutex for image %" PRId32 ": %s", index, e.what());
+	return XRT_ERROR_D3D;
+} catch (std::exception const &e) {
+	U_LOG_E("Error acquiring keyed mutex for image %" PRId32 ": %s", index, e.what());
+	return XRT_ERROR_D3D;
+} catch (...) {
+	U_LOG_E("Error acquiring keyed mutex for image %" PRId32, index);
+	return XRT_ERROR_D3D;
+}
+
+
+xrt_result_t
+KeyedMutexCollection::releaseKeyedMutex(uint32_t index) noexcept
+
+try {
+
+	if (!keyed_mutex_acquired[index]) {
+
+		D3D_COMMON_WARN(log_level,
+		                "Will not release the keyed mutex for image %" PRId32 " - it was not acquired!", index);
+		return XRT_ERROR_D3D;
+	}
+	auto hr = LOG_IF_FAILED(keyed_mutex_collection[index]->ReleaseSync(kKeyedMutexKey));
+	if (FAILED(hr)) {
+		D3D_COMMON_ERROR(log_level, "Could not release the keyed mutex for image %" PRId32, index);
+		return XRT_ERROR_D3D;
+	}
+	keyed_mutex_acquired[index] = false;
+	return XRT_SUCCESS;
+
+} catch (wil::ResultException const &e) {
+	U_LOG_E("Error releasing keyed mutex %" PRId32 ": %s", index, e.what());
+	return XRT_ERROR_D3D;
+} catch (std::exception const &e) {
+	U_LOG_E("Error releasing keyed mutex %" PRId32 ": %s", index, e.what());
+	return XRT_ERROR_D3D;
+} catch (...) {
+	U_LOG_E("Error releasing keyed mutex %" PRId32, index);
+	return XRT_ERROR_D3D;
+}
+
+
+} // namespace xrt::compositor::client
diff --git a/src/xrt/compositor/client/comp_d3d_common.hpp b/src/xrt/compositor/client/comp_d3d_common.hpp
new file mode 100644
index 000000000..60168091e
--- /dev/null
+++ b/src/xrt/compositor/client/comp_d3d_common.hpp
@@ -0,0 +1,144 @@
+// Copyright 2019-2022, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Functionality common to D3D11 and D3D12 for client side compositor implementation.
+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup comp_client
+ */
+#pragma once
+
+#include "xrt/xrt_compositor.h"
+#include "xrt/xrt_deleters.hpp"
+#include "xrt/xrt_results.h"
+
+#include "util/u_handles.h"
+#include "util/u_logging.h"
+
+#include <wil/com.h>
+#include <wil/resource.h>
+#include <wil/result_macros.h>
+
+#include <d3d11_3.h>
+
+#include <memory>
+#include <vector>
+#include <stdint.h>
+
+
+namespace xrt::compositor::client {
+
+using unique_swapchain_ref =
+    std::unique_ptr<struct xrt_swapchain,
+                    xrt::deleters::reference_deleter<struct xrt_swapchain, xrt_swapchain_reference>>;
+
+/**
+ * Import the provided handles into a native compositor, without consuming them.
+ *
+ * @param c The native compositor
+ * @param handles A vector of uniquely-owned handles. These will be duplicated, not consumed, by this import.
+ * @param vkinfo The swapchain create info, with format as a Vulkan constant
+ * @param use_dedicated_allocation Passed through to @ref xrt_image_native
+ * @param[out] out_xsc The swapchain to populate
+ * @return XRT_SUCCESS if everything went well, otherwise whatever error a call internally returned.
+ */
+static inline xrt_result_t
+importFromHandleDuplicates(xrt_compositor_native &xcn,
+                           std::vector<wil::unique_handle> const &handles,
+                           const struct xrt_swapchain_create_info &vkinfo,
+                           bool use_dedicated_allocation,
+                           unique_swapchain_ref &out_xsc)
+{
+	uint32_t image_count = static_cast<uint32_t>(handles.size());
+	// Populate for import
+	std::vector<xrt_image_native> xins;
+	xins.reserve(image_count);
+
+	// Keep this around until after successful import, then detach all.
+	std::vector<wil::unique_handle> handlesForImport;
+	handlesForImport.reserve(image_count);
+
+	for (const wil::unique_handle &handle : handles) {
+		wil::unique_handle duped{u_graphics_buffer_ref(handle.get())};
+		xrt_image_native xin;
+		xin.handle = duped.get();
+		xin.size = 0;
+		xin.use_dedicated_allocation = use_dedicated_allocation;
+
+		handlesForImport.emplace_back(std::move(duped));
+		xins.emplace_back(xin);
+	}
+
+	// Import into the native compositor, to create the corresponding swapchain which we wrap.
+	xrt_swapchain *xsc = nullptr;
+	xrt_result_t xret = xrt_comp_import_swapchain(&(xcn.base), &vkinfo, xins.data(), image_count, &xsc);
+	if (xret != XRT_SUCCESS) {
+		return xret;
+	}
+	// Let unique_ptr manage the lifetime of xsc now
+	out_xsc.reset(xsc);
+
+	// The imported swapchain took ownership of them now, release them from ownership here.
+	for (auto &h : handlesForImport) {
+		h.release();
+	}
+	return XRT_SUCCESS;
+}
+
+/**
+ * A collection of DXGIKeyedMutex objects, one for each swapchain image in a swapchain.
+ *
+ */
+class KeyedMutexCollection
+{
+public:
+	// 0 is special
+	static constexpr uint64_t kKeyedMutexKey = 0;
+
+	/**
+	 * @brief Construct a new Keyed Mutex Collection object
+	 *
+	 * @param log_level The compositor log level to use
+	 */
+	explicit KeyedMutexCollection(u_logging_level log_level) noexcept;
+
+	/**
+	 * Take the keyed mutex vector before starting to use the images.
+	 *
+	 * @param keyedMutexVector Your vector of objects to move from
+	 */
+	xrt_result_t
+	init(const std::vector<wil::com_ptr<ID3D11Texture2D1>> &images) noexcept;
+
+	/**
+	 * Wait for acquisition of the keyed mutex.
+	 *
+	 * @param index Swapchain image index
+	 * @param timeout_ns Timeout in nanoseconds or XRT_INFINITE_DURATION
+	 * @return xrt_result_t: XRT_SUCCESS, XRT_TIMEOUT, or some error
+	 */
+	xrt_result_t
+	waitKeyedMutex(uint32_t index, uint64_t timeout_ns) noexcept;
+
+	/**
+	 * Release the keyed mutex
+	 *
+	 * @param index Swapchain image index
+	 * @return xrt_result_t
+	 */
+	xrt_result_t
+	releaseKeyedMutex(uint32_t index) noexcept;
+
+private:
+	//! Keyed mutex per image associated with client_d3d11_compositor::app_device
+	std::vector<wil::com_ptr<IDXGIKeyedMutex>> keyed_mutex_collection;
+
+	std::vector<bool> keyed_mutex_acquired;
+
+	//! Logging level.
+	u_logging_level log_level;
+};
+
+
+} // namespace xrt::compositor::client