From 6342c72665e62a30fd53db489a6cb6ac369cee31 Mon Sep 17 00:00:00 2001 From: Fernando Velazquez Innella Date: Tue, 1 Aug 2023 13:49:40 -0400 Subject: [PATCH] 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 --- src/xrt/auxiliary/d3d/CMakeLists.txt | 5 +- src/xrt/auxiliary/d3d/d3d_d3d12_allocator.cpp | 230 ++++++++++++++++++ src/xrt/auxiliary/d3d/d3d_d3d12_allocator.h | 35 +++ src/xrt/auxiliary/d3d/d3d_d3d12_allocator.hpp | 50 ++++ src/xrt/auxiliary/d3d/d3d_d3d12_helpers.cpp | 64 +++++ src/xrt/auxiliary/d3d/d3d_d3d12_helpers.hpp | 21 ++ 6 files changed, 403 insertions(+), 2 deletions(-) create mode 100644 src/xrt/auxiliary/d3d/d3d_d3d12_allocator.cpp create mode 100644 src/xrt/auxiliary/d3d/d3d_d3d12_allocator.h create mode 100644 src/xrt/auxiliary/d3d/d3d_d3d12_allocator.hpp diff --git a/src/xrt/auxiliary/d3d/CMakeLists.txt b/src/xrt/auxiliary/d3d/CMakeLists.txt index 8acabe44c..7edfab020 100644 --- a/src/xrt/auxiliary/d3d/CMakeLists.txt +++ b/src/xrt/auxiliary/d3d/CMakeLists.txt @@ -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 diff --git a/src/xrt/auxiliary/d3d/d3d_d3d12_allocator.cpp b/src/xrt/auxiliary/d3d/d3d_d3d12_allocator.cpp new file mode 100644 index 000000000..c5b5a65c1 --- /dev/null +++ b/src/xrt/auxiliary/d3d/d3d_d3d12_allocator.cpp @@ -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 + * @author Fernando Velazquez Innella + * @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 +#include +#include +#include + +#include +#include + +#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 &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> &out_images, + std::vector &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> 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 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 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 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(xina); + + std::vector> images; + std::vector 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(xina); + delete d3da; +} + +struct xrt_image_native_allocator * +d3d12_allocator_create(ID3D11Device *device) +try { + auto ret = std::make_unique(); + 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) diff --git a/src/xrt/auxiliary/d3d/d3d_d3d12_allocator.h b/src/xrt/auxiliary/d3d/d3d_d3d12_allocator.h new file mode 100644 index 000000000..50bca0c94 --- /dev/null +++ b/src/xrt/auxiliary/d3d/d3d_d3d12_allocator.h @@ -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 + * @author Fernando Velazquez Innella + * @ingroup aux_d3d + */ + +#pragma once + +#include "xrt/xrt_compositor.h" + +#include + + +#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 diff --git a/src/xrt/auxiliary/d3d/d3d_d3d12_allocator.hpp b/src/xrt/auxiliary/d3d/d3d_d3d12_allocator.hpp new file mode 100644 index 000000000..4da82d96c --- /dev/null +++ b/src/xrt/auxiliary/d3d/d3d_d3d12_allocator.hpp @@ -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 + * @author Fernando Velazquez Innella + * @ingroup aux_d3d + */ + +#pragma once + +#include "xrt/xrt_compositor.h" + +#include +#include +#include +#include + +#include + + +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> &out_images, + std::vector &out_handles); + +}; // namespace xrt::auxiliary::d3d::d3d12 diff --git a/src/xrt/auxiliary/d3d/d3d_d3d12_helpers.cpp b/src/xrt/auxiliary/d3d/d3d_d3d12_helpers.cpp index 4628dc71a..39561202c 100644 --- a/src/xrt/auxiliary/d3d/d3d_d3d12_helpers.cpp +++ b/src/xrt/auxiliary/d3d/d3d_d3d12_helpers.cpp @@ -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 &out_copy_command_list) +{ + wil::com_ptr 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 importImage(ID3D12Device &device, HANDLE h) diff --git a/src/xrt/auxiliary/d3d/d3d_d3d12_helpers.hpp b/src/xrt/auxiliary/d3d/d3d_d3d12_helpers.hpp index 121ad8db9..1e499f9f5 100644 --- a/src/xrt/auxiliary/d3d/d3d_d3d12_helpers.hpp +++ b/src/xrt/auxiliary/d3d/d3d_d3d12_helpers.hpp @@ -57,6 +57,27 @@ createCommandLists(ID3D12Device &device, wil::com_ptr out_acquire_command_list, wil::com_ptr 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 &out_copy_command_list); + /** * Imports an image into D3D12 from a handle. *