c/client: Extract some utilities usable by D3D11 and 12

This commit is contained in:
Ryan Pavlik 2022-07-11 14:55:17 -05:00
parent 576b158a83
commit a9facfe671
5 changed files with 319 additions and 106 deletions

View file

@ -5,6 +5,7 @@
- mr.1326
- mr.1302
- mr.1337
- mr.1340
---
Full support for D3D11 client applications on Windows.

View file

@ -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
#

View file

@ -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;

View file

@ -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

View file

@ -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