a/d3d: Add native d3d12 allocator and copy helpers.

Add a D3D12 allocator and helper code to copy from shadow images,
the copy is needed to work around a interop issue on NVIDIA hardware.

Co-authored-by: Ryan Pavlik <ryan.pavlik@collabora.com>
This commit is contained in:
Fernando Velazquez Innella 2023-08-01 13:49:40 -04:00 committed by Jakob Bornecrantz
parent 84ac7b14be
commit 6342c72665
6 changed files with 403 additions and 2 deletions

View file

@ -28,8 +28,9 @@ if(XRT_HAVE_D3D12)
aux_d3d
PRIVATE
d3d_d3d12_bits.h
# d3d_d3d12_allocator.cpp
# d3d_d3d12_allocator.hpp
d3d_d3d12_allocator.cpp
d3d_d3d12_allocator.h
d3d_d3d12_allocator.hpp
d3d_d3d12_fence.cpp
d3d_d3d12_fence.hpp
d3d_d3d12_helpers.cpp

View file

@ -0,0 +1,230 @@
// Copyright 2020-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief D3D12 backed image buffer allocator.
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
* @author Fernando Velazquez Innella <finnella@magicleap.com>
* @ingroup aux_d3d
*/
#include "d3d_d3d12_allocator.h"
#include "d3d_d3d12_allocator.hpp"
#include "d3d_d3d12_bits.h"
#include "d3d_dxgi_formats.h"
#include "util/u_misc.h"
#include "util/u_logging.h"
#include "util/u_debug.h"
#include "util/u_handles.h"
#include "xrt/xrt_windows.h"
#include <Unknwn.h>
#include <d3d12.h>
#include <wil/com.h>
#include <wil/result.h>
#include <inttypes.h>
#include <memory>
#define DEFAULT_CATCH(...) \
catch (wil::ResultException const &e) \
{ \
U_LOG_E("Caught exception: %s", e.what()); \
return __VA_ARGS__; \
} \
catch (std::exception const &e) \
{ \
U_LOG_E("Caught exception: %s", e.what()); \
return __VA_ARGS__; \
} \
catch (...) \
{ \
U_LOG_E("Caught exception"); \
return __VA_ARGS__; \
}
DEBUG_GET_ONCE_LOG_OPTION(d3d12_log, "D3D12_LOG", U_LOGGING_WARN)
#define D3DA_TRACE(...) U_LOG_IFL_T(debug_get_log_option_d3d12_log(), __VA_ARGS__)
#define D3DA_DEBUG(...) U_LOG_IFL_D(debug_get_log_option_d3d12_log(), __VA_ARGS__)
#define D3DA_INFO(...) U_LOG_IFL_I(debug_get_log_option_d3d12_log(), __VA_ARGS__)
#define D3DA_WARN(...) U_LOG_IFL_W(debug_get_log_option_d3d12_log(), __VA_ARGS__)
#define D3DA_ERROR(...) U_LOG_IFL_E(debug_get_log_option_d3d12_log(), __VA_ARGS__)
namespace xrt::auxiliary::d3d::d3d12 {
static wil::unique_handle
createSharedHandle(ID3D12Device &device, const wil::com_ptr<ID3D12Resource> &image)
{
wil::unique_handle h;
THROW_IF_FAILED(device.CreateSharedHandle( //
image.get(), // pObject
nullptr, // pAttributes
GENERIC_ALL, // Access
nullptr, // Name
h.put())); // pHandle
return h;
}
xrt_result_t
allocateSharedImages(ID3D12Device &device,
const xrt_swapchain_create_info &xsci,
size_t image_count,
std::vector<wil::com_ptr<ID3D12Resource>> &out_images,
std::vector<wil::unique_handle> &out_handles)
try {
if (0 != (xsci.create & XRT_SWAPCHAIN_CREATE_PROTECTED_CONTENT)) {
return XRT_ERROR_SWAPCHAIN_FLAG_VALID_BUT_UNSUPPORTED;
}
if (0 != (xsci.create & XRT_SWAPCHAIN_CREATE_STATIC_IMAGE) && image_count > 1) {
D3DA_ERROR("Got XRT_SWAPCHAIN_CREATE_STATIC_IMAGE but an image count greater than 1!");
return XRT_ERROR_ALLOCATION;
}
if (xsci.array_size == 0) {
D3DA_ERROR("Array size must not be 0");
return XRT_ERROR_ALLOCATION;
}
// TODO: See if this is still necessary
DXGI_FORMAT typeless_format = d3d_dxgi_format_to_typeless_dxgi((DXGI_FORMAT)xsci.format);
if (typeless_format == 0) {
D3DA_ERROR("Invalid format %04" PRIx64 "!", (uint64_t)xsci.format);
return XRT_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED;
}
DXGI_SAMPLE_DESC sample_desc{
xsci.sample_count, // Count
0, // Quality
};
// Note:
// To use a cross-adapter heap the following flag must be passed:
// resource_flags |= D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
// Additionally, only copy operations are allowed with the resource.
D3D12_RESOURCE_FLAGS resource_flags = d3d_convert_usage_bits_to_d3d12_resource_flags(xsci.bits);
D3D12_RESOURCE_DESC desc{
D3D12_RESOURCE_DIMENSION_TEXTURE2D, // Dimension
0, // Alignment
xsci.width, // Width
xsci.height, // Height
(UINT16)xsci.array_size, // DepthOrArraySize
(UINT16)xsci.mip_count, // MipLevels
typeless_format, // Format
sample_desc, // SampleDesc
D3D12_TEXTURE_LAYOUT_UNKNOWN, // Layout
resource_flags // Flags;
};
// Cubemap
if (xsci.face_count == 6) {
desc.DepthOrArraySize *= 6;
}
// Create resources and let the driver manage memory
std::vector<wil::com_ptr<ID3D12Resource>> images;
D3D12_HEAP_PROPERTIES heap{};
heap.Type = D3D12_HEAP_TYPE_DEFAULT;
D3D12_HEAP_FLAGS heap_flags = D3D12_HEAP_FLAG_SHARED;
D3D12_RESOURCE_STATES initial_resource_state = d3d_convert_usage_bits_to_d3d12_app_resource_state(xsci.bits);
for (size_t i = 0; i < image_count; ++i) {
wil::com_ptr<ID3D12Resource> tex;
HRESULT res = device.CreateCommittedResource( //
&heap, // pHeapProperties
heap_flags, // HeapFlags
&desc, // pDesc
initial_resource_state, // InitialResourceState
nullptr, // pOptimizedClearValue
IID_PPV_ARGS(tex.put())); // riidResource, ppvResource
if (FAILED(LOG_IF_FAILED(res))) {
return XRT_ERROR_ALLOCATION;
}
images.emplace_back(std::move(tex));
}
std::vector<wil::unique_handle> handles;
handles.reserve(image_count);
for (const auto &tex : images) {
handles.emplace_back(createSharedHandle(device, tex));
}
out_images = std::move(images);
out_handles = std::move(handles);
return XRT_SUCCESS;
}
DEFAULT_CATCH(XRT_ERROR_ALLOCATION)
} // namespace xrt::auxiliary::d3d::d3d12
struct d3d12_allocator
{
struct xrt_image_native_allocator base;
wil::com_ptr<ID3D12Device> device;
};
static xrt_result_t
d3d12_images_allocate(struct xrt_image_native_allocator *xina,
const struct xrt_swapchain_create_info *xsci,
size_t image_count,
struct xrt_image_native *out_images)
{
try {
d3d12_allocator *d3da = reinterpret_cast<d3d12_allocator *>(xina);
std::vector<wil::com_ptr<ID3D12Resource>> images;
std::vector<wil::unique_handle> handles;
auto result = xrt::auxiliary::d3d::d3d12::allocateSharedImages( //
*(d3da->device), // device
*xsci, // xsci
image_count, // image_count
images, // out_images
handles); // out_handles
if (result != XRT_SUCCESS) {
return result;
}
for (size_t i = 0; i < image_count; ++i) {
out_images[i].handle = handles[i].release();
out_images[i].is_dxgi_handle = false;
}
return XRT_SUCCESS;
}
DEFAULT_CATCH(XRT_ERROR_ALLOCATION)
}
static xrt_result_t
d3d12_images_free(struct xrt_image_native_allocator *xina, size_t image_count, struct xrt_image_native *images)
{
for (size_t i = 0; i < image_count; ++i) {
u_graphics_buffer_unref(&(images[i].handle));
}
return XRT_SUCCESS;
}
static void
d3d12_destroy(struct xrt_image_native_allocator *xina)
{
d3d12_allocator *d3da = reinterpret_cast<d3d12_allocator *>(xina);
delete d3da;
}
struct xrt_image_native_allocator *
d3d12_allocator_create(ID3D11Device *device)
try {
auto ret = std::make_unique<d3d12_allocator>();
U_ZERO(&(ret->base));
ret->base.images_allocate = d3d12_images_allocate;
ret->base.images_free = d3d12_images_free;
ret->base.destroy = d3d12_destroy;
return &(ret.release()->base);
}
DEFAULT_CATCH(nullptr)

View file

@ -0,0 +1,35 @@
// Copyright 2020-2023, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Header for D3D12-backed image buffer allocator factory function.
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
* @author Fernando Velazquez Innella <finnella@magicleap.com>
* @ingroup aux_d3d
*/
#pragma once
#include "xrt/xrt_compositor.h"
#include <d3d12.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Create a XINA that allocates D3D12 textures.
*
* @param device A device to allocate the textures with. Be sure it will not be used from other threads while this
* allocator allocates.
*
* @return struct xrt_image_native_allocator*
*/
struct xrt_image_native_allocator *
d3d12_allocator_create(ID3D12Device *device);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,50 @@
// Copyright 2020-2023, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Higher-level D3D12-backed image buffer allocation routine.
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
* @author Fernando Velazquez Innella <finnella@magicleap.com>
* @ingroup aux_d3d
*/
#pragma once
#include "xrt/xrt_compositor.h"
#include <Unknwn.h>
#include <d3d12.h>
#include <wil/com.h>
#include <wil/resource.h>
#include <vector>
namespace xrt::auxiliary::d3d::d3d12 {
/**
* Allocate images (ID3D12Resource) that have a corresponding native handle.
*
* @param device A D3D12 device to allocate with.
* @param xsci Swapchain create info: note that the format is assumed to be a DXGI_FORMAT (conversion to typeless is
* automatic)
* @param image_count The number of images to create.
* @param keyed_mutex Whether to create images with a shared "keyed mutex" as well
* @param[out] out_images A vector that will be cleared and populated with the images.
* @param[out] out_handles A vector that will be cleared and populated with the corresponding native handles.
*
* @return xrt_result_t, one of:
* - @ref XRT_SUCCESS
* - @ref XRT_ERROR_ALLOCATION
* - @ref XRT_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED
* - @ref XRT_ERROR_SWAPCHAIN_FLAG_VALID_BUT_UNSUPPORTED
* - @ref XRT_ERROR_D3D12
*/
xrt_result_t
allocateSharedImages(ID3D12Device &device,
const xrt_swapchain_create_info &xsci,
size_t image_count,
std::vector<wil::com_ptr<ID3D12Resource>> &out_images,
std::vector<wil::unique_handle> &out_handles);
}; // namespace xrt::auxiliary::d3d::d3d12

View file

@ -79,6 +79,70 @@ createCommandLists(ID3D12Device &device,
return S_OK;
}
HRESULT
createCommandListImageCopy(ID3D12Device &device,
ID3D12CommandAllocator &command_allocator,
ID3D12Resource &resource_src,
ID3D12Resource &resource_dst,
D3D12_RESOURCE_STATES src_resource_state,
D3D12_RESOURCE_STATES dst_resource_state,
wil::com_ptr<ID3D12CommandList> &out_copy_command_list)
{
wil::com_ptr<ID3D12GraphicsCommandList> copyCommandList;
RETURN_IF_FAILED(device.CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, &command_allocator, nullptr,
IID_PPV_ARGS(copyCommandList.put())));
// Transition images into copy state
D3D12_RESOURCE_BARRIER preCopyBarriers[2]{};
preCopyBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
preCopyBarriers[0].Transition.pResource = &resource_src;
preCopyBarriers[0].Transition.StateBefore = src_resource_state;
preCopyBarriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
preCopyBarriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
preCopyBarriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
preCopyBarriers[1].Transition.pResource = &resource_dst;
preCopyBarriers[1].Transition.StateBefore = dst_resource_state;
preCopyBarriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
preCopyBarriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
copyCommandList->ResourceBarrier(2, preCopyBarriers);
// Insert texture copy command
D3D12_TEXTURE_COPY_LOCATION srcCopyLocation;
srcCopyLocation.pResource = &resource_src;
srcCopyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
srcCopyLocation.SubresourceIndex = 0;
D3D12_TEXTURE_COPY_LOCATION dstCopyLocation;
dstCopyLocation.pResource = &resource_dst;
dstCopyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstCopyLocation.SubresourceIndex = 0;
copyCommandList->CopyTextureRegion(&dstCopyLocation, 0, 0, 0, &srcCopyLocation, nullptr);
// Transition images back from copy state
D3D12_RESOURCE_BARRIER postCopyBarriers[2]{};
postCopyBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
postCopyBarriers[0].Transition.pResource = &resource_src;
postCopyBarriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
postCopyBarriers[0].Transition.StateAfter = src_resource_state;
postCopyBarriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
postCopyBarriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
postCopyBarriers[1].Transition.pResource = &resource_dst;
postCopyBarriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
postCopyBarriers[1].Transition.StateAfter = dst_resource_state;
postCopyBarriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
copyCommandList->ResourceBarrier(2, postCopyBarriers);
RETURN_IF_FAILED(copyCommandList->Close());
out_copy_command_list = std::move(copyCommandList);
return S_OK;
}
wil::com_ptr<ID3D12Resource>
importImage(ID3D12Device &device, HANDLE h)

View file

@ -57,6 +57,27 @@ createCommandLists(ID3D12Device &device,
wil::com_ptr<ID3D12CommandList> out_acquire_command_list,
wil::com_ptr<ID3D12CommandList> out_release_command_list);
/**
* @brief Create a command list for image resource copying
*
* @param device D3D12 device
* @param command_allocator
* @param resource_src Source image
* @param resource_dst Destination image
* @param src_resource_state
* @param dst_resource_state
* @param[out] out_copy_command_list Command list output
* @return HRESULT
*/
HRESULT
createCommandListImageCopy(ID3D12Device &device,
ID3D12CommandAllocator &command_allocator,
ID3D12Resource &resource_src,
ID3D12Resource &resource_dst,
D3D12_RESOURCE_STATES src_resource_state,
D3D12_RESOURCE_STATES dst_resource_state,
wil::com_ptr<ID3D12CommandList> &out_copy_command_list);
/**
* Imports an image into D3D12 from a handle.
*