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