The open source OpenXR runtime

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>

authored by

Fernando Velazquez Innella
Ryan Pavlik
and committed by
Jakob Bornecrantz
6342c726 84ac7b14

+403 -2
+3 -2
src/xrt/auxiliary/d3d/CMakeLists.txt
··· 28 28 aux_d3d 29 29 PRIVATE 30 30 d3d_d3d12_bits.h 31 - # d3d_d3d12_allocator.cpp 32 - # d3d_d3d12_allocator.hpp 31 + d3d_d3d12_allocator.cpp 32 + d3d_d3d12_allocator.h 33 + d3d_d3d12_allocator.hpp 33 34 d3d_d3d12_fence.cpp 34 35 d3d_d3d12_fence.hpp 35 36 d3d_d3d12_helpers.cpp
+230
src/xrt/auxiliary/d3d/d3d_d3d12_allocator.cpp
··· 1 + // Copyright 2020-2022, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief D3D12 backed image buffer allocator. 6 + * @author Ryan Pavlik <ryan.pavlik@collabora.com> 7 + * @author Fernando Velazquez Innella <finnella@magicleap.com> 8 + * @ingroup aux_d3d 9 + */ 10 + 11 + #include "d3d_d3d12_allocator.h" 12 + #include "d3d_d3d12_allocator.hpp" 13 + 14 + #include "d3d_d3d12_bits.h" 15 + #include "d3d_dxgi_formats.h" 16 + 17 + #include "util/u_misc.h" 18 + #include "util/u_logging.h" 19 + #include "util/u_debug.h" 20 + #include "util/u_handles.h" 21 + 22 + #include "xrt/xrt_windows.h" 23 + 24 + #include <Unknwn.h> 25 + #include <d3d12.h> 26 + #include <wil/com.h> 27 + #include <wil/result.h> 28 + 29 + #include <inttypes.h> 30 + #include <memory> 31 + 32 + #define DEFAULT_CATCH(...) \ 33 + catch (wil::ResultException const &e) \ 34 + { \ 35 + U_LOG_E("Caught exception: %s", e.what()); \ 36 + return __VA_ARGS__; \ 37 + } \ 38 + catch (std::exception const &e) \ 39 + { \ 40 + U_LOG_E("Caught exception: %s", e.what()); \ 41 + return __VA_ARGS__; \ 42 + } \ 43 + catch (...) \ 44 + { \ 45 + U_LOG_E("Caught exception"); \ 46 + return __VA_ARGS__; \ 47 + } 48 + 49 + 50 + DEBUG_GET_ONCE_LOG_OPTION(d3d12_log, "D3D12_LOG", U_LOGGING_WARN) 51 + #define D3DA_TRACE(...) U_LOG_IFL_T(debug_get_log_option_d3d12_log(), __VA_ARGS__) 52 + #define D3DA_DEBUG(...) U_LOG_IFL_D(debug_get_log_option_d3d12_log(), __VA_ARGS__) 53 + #define D3DA_INFO(...) U_LOG_IFL_I(debug_get_log_option_d3d12_log(), __VA_ARGS__) 54 + #define D3DA_WARN(...) U_LOG_IFL_W(debug_get_log_option_d3d12_log(), __VA_ARGS__) 55 + #define D3DA_ERROR(...) U_LOG_IFL_E(debug_get_log_option_d3d12_log(), __VA_ARGS__) 56 + 57 + namespace xrt::auxiliary::d3d::d3d12 { 58 + 59 + static wil::unique_handle 60 + createSharedHandle(ID3D12Device &device, const wil::com_ptr<ID3D12Resource> &image) 61 + { 62 + wil::unique_handle h; 63 + THROW_IF_FAILED(device.CreateSharedHandle( // 64 + image.get(), // pObject 65 + nullptr, // pAttributes 66 + GENERIC_ALL, // Access 67 + nullptr, // Name 68 + h.put())); // pHandle 69 + return h; 70 + } 71 + 72 + 73 + xrt_result_t 74 + allocateSharedImages(ID3D12Device &device, 75 + const xrt_swapchain_create_info &xsci, 76 + size_t image_count, 77 + std::vector<wil::com_ptr<ID3D12Resource>> &out_images, 78 + std::vector<wil::unique_handle> &out_handles) 79 + try { 80 + if (0 != (xsci.create & XRT_SWAPCHAIN_CREATE_PROTECTED_CONTENT)) { 81 + return XRT_ERROR_SWAPCHAIN_FLAG_VALID_BUT_UNSUPPORTED; 82 + } 83 + 84 + if (0 != (xsci.create & XRT_SWAPCHAIN_CREATE_STATIC_IMAGE) && image_count > 1) { 85 + D3DA_ERROR("Got XRT_SWAPCHAIN_CREATE_STATIC_IMAGE but an image count greater than 1!"); 86 + return XRT_ERROR_ALLOCATION; 87 + } 88 + if (xsci.array_size == 0) { 89 + D3DA_ERROR("Array size must not be 0"); 90 + return XRT_ERROR_ALLOCATION; 91 + } 92 + 93 + // TODO: See if this is still necessary 94 + DXGI_FORMAT typeless_format = d3d_dxgi_format_to_typeless_dxgi((DXGI_FORMAT)xsci.format); 95 + if (typeless_format == 0) { 96 + D3DA_ERROR("Invalid format %04" PRIx64 "!", (uint64_t)xsci.format); 97 + return XRT_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED; 98 + } 99 + 100 + DXGI_SAMPLE_DESC sample_desc{ 101 + xsci.sample_count, // Count 102 + 0, // Quality 103 + }; 104 + 105 + // Note: 106 + // To use a cross-adapter heap the following flag must be passed: 107 + // resource_flags |= D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER; 108 + // Additionally, only copy operations are allowed with the resource. 109 + D3D12_RESOURCE_FLAGS resource_flags = d3d_convert_usage_bits_to_d3d12_resource_flags(xsci.bits); 110 + 111 + D3D12_RESOURCE_DESC desc{ 112 + D3D12_RESOURCE_DIMENSION_TEXTURE2D, // Dimension 113 + 0, // Alignment 114 + xsci.width, // Width 115 + xsci.height, // Height 116 + (UINT16)xsci.array_size, // DepthOrArraySize 117 + (UINT16)xsci.mip_count, // MipLevels 118 + typeless_format, // Format 119 + sample_desc, // SampleDesc 120 + D3D12_TEXTURE_LAYOUT_UNKNOWN, // Layout 121 + resource_flags // Flags; 122 + }; 123 + 124 + // Cubemap 125 + if (xsci.face_count == 6) { 126 + desc.DepthOrArraySize *= 6; 127 + } 128 + 129 + // Create resources and let the driver manage memory 130 + std::vector<wil::com_ptr<ID3D12Resource>> images; 131 + D3D12_HEAP_PROPERTIES heap{}; 132 + heap.Type = D3D12_HEAP_TYPE_DEFAULT; 133 + D3D12_HEAP_FLAGS heap_flags = D3D12_HEAP_FLAG_SHARED; 134 + D3D12_RESOURCE_STATES initial_resource_state = d3d_convert_usage_bits_to_d3d12_app_resource_state(xsci.bits); 135 + 136 + for (size_t i = 0; i < image_count; ++i) { 137 + wil::com_ptr<ID3D12Resource> tex; 138 + HRESULT res = device.CreateCommittedResource( // 139 + &heap, // pHeapProperties 140 + heap_flags, // HeapFlags 141 + &desc, // pDesc 142 + initial_resource_state, // InitialResourceState 143 + nullptr, // pOptimizedClearValue 144 + IID_PPV_ARGS(tex.put())); // riidResource, ppvResource 145 + 146 + if (FAILED(LOG_IF_FAILED(res))) { 147 + return XRT_ERROR_ALLOCATION; 148 + } 149 + images.emplace_back(std::move(tex)); 150 + } 151 + 152 + std::vector<wil::unique_handle> handles; 153 + handles.reserve(image_count); 154 + for (const auto &tex : images) { 155 + handles.emplace_back(createSharedHandle(device, tex)); 156 + } 157 + out_images = std::move(images); 158 + out_handles = std::move(handles); 159 + return XRT_SUCCESS; 160 + } 161 + DEFAULT_CATCH(XRT_ERROR_ALLOCATION) 162 + 163 + } // namespace xrt::auxiliary::d3d::d3d12 164 + 165 + struct d3d12_allocator 166 + { 167 + struct xrt_image_native_allocator base; 168 + wil::com_ptr<ID3D12Device> device; 169 + }; 170 + 171 + 172 + static xrt_result_t 173 + d3d12_images_allocate(struct xrt_image_native_allocator *xina, 174 + const struct xrt_swapchain_create_info *xsci, 175 + size_t image_count, 176 + struct xrt_image_native *out_images) 177 + { 178 + try { 179 + d3d12_allocator *d3da = reinterpret_cast<d3d12_allocator *>(xina); 180 + 181 + std::vector<wil::com_ptr<ID3D12Resource>> images; 182 + std::vector<wil::unique_handle> handles; 183 + auto result = xrt::auxiliary::d3d::d3d12::allocateSharedImages( // 184 + *(d3da->device), // device 185 + *xsci, // xsci 186 + image_count, // image_count 187 + images, // out_images 188 + handles); // out_handles 189 + 190 + if (result != XRT_SUCCESS) { 191 + return result; 192 + } 193 + 194 + for (size_t i = 0; i < image_count; ++i) { 195 + out_images[i].handle = handles[i].release(); 196 + out_images[i].is_dxgi_handle = false; 197 + } 198 + 199 + return XRT_SUCCESS; 200 + } 201 + DEFAULT_CATCH(XRT_ERROR_ALLOCATION) 202 + } 203 + 204 + static xrt_result_t 205 + d3d12_images_free(struct xrt_image_native_allocator *xina, size_t image_count, struct xrt_image_native *images) 206 + { 207 + for (size_t i = 0; i < image_count; ++i) { 208 + u_graphics_buffer_unref(&(images[i].handle)); 209 + } 210 + return XRT_SUCCESS; 211 + } 212 + 213 + static void 214 + d3d12_destroy(struct xrt_image_native_allocator *xina) 215 + { 216 + d3d12_allocator *d3da = reinterpret_cast<d3d12_allocator *>(xina); 217 + delete d3da; 218 + } 219 + 220 + struct xrt_image_native_allocator * 221 + d3d12_allocator_create(ID3D11Device *device) 222 + try { 223 + auto ret = std::make_unique<d3d12_allocator>(); 224 + U_ZERO(&(ret->base)); 225 + ret->base.images_allocate = d3d12_images_allocate; 226 + ret->base.images_free = d3d12_images_free; 227 + ret->base.destroy = d3d12_destroy; 228 + return &(ret.release()->base); 229 + } 230 + DEFAULT_CATCH(nullptr)
+35
src/xrt/auxiliary/d3d/d3d_d3d12_allocator.h
··· 1 + // Copyright 2020-2023, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Header for D3D12-backed image buffer allocator factory function. 6 + * @author Ryan Pavlik <ryan.pavlik@collabora.com> 7 + * @author Fernando Velazquez Innella <finnella@magicleap.com> 8 + * @ingroup aux_d3d 9 + */ 10 + 11 + #pragma once 12 + 13 + #include "xrt/xrt_compositor.h" 14 + 15 + #include <d3d12.h> 16 + 17 + 18 + #ifdef __cplusplus 19 + extern "C" { 20 + #endif 21 + 22 + /** 23 + * @brief Create a XINA that allocates D3D12 textures. 24 + * 25 + * @param device A device to allocate the textures with. Be sure it will not be used from other threads while this 26 + * allocator allocates. 27 + * 28 + * @return struct xrt_image_native_allocator* 29 + */ 30 + struct xrt_image_native_allocator * 31 + d3d12_allocator_create(ID3D12Device *device); 32 + 33 + #ifdef __cplusplus 34 + } 35 + #endif
+50
src/xrt/auxiliary/d3d/d3d_d3d12_allocator.hpp
··· 1 + // Copyright 2020-2023, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Higher-level D3D12-backed image buffer allocation routine. 6 + * @author Ryan Pavlik <ryan.pavlik@collabora.com> 7 + * @author Fernando Velazquez Innella <finnella@magicleap.com> 8 + * @ingroup aux_d3d 9 + */ 10 + 11 + #pragma once 12 + 13 + #include "xrt/xrt_compositor.h" 14 + 15 + #include <Unknwn.h> 16 + #include <d3d12.h> 17 + #include <wil/com.h> 18 + #include <wil/resource.h> 19 + 20 + #include <vector> 21 + 22 + 23 + namespace xrt::auxiliary::d3d::d3d12 { 24 + 25 + /** 26 + * Allocate images (ID3D12Resource) that have a corresponding native handle. 27 + * 28 + * @param device A D3D12 device to allocate with. 29 + * @param xsci Swapchain create info: note that the format is assumed to be a DXGI_FORMAT (conversion to typeless is 30 + * automatic) 31 + * @param image_count The number of images to create. 32 + * @param keyed_mutex Whether to create images with a shared "keyed mutex" as well 33 + * @param[out] out_images A vector that will be cleared and populated with the images. 34 + * @param[out] out_handles A vector that will be cleared and populated with the corresponding native handles. 35 + * 36 + * @return xrt_result_t, one of: 37 + * - @ref XRT_SUCCESS 38 + * - @ref XRT_ERROR_ALLOCATION 39 + * - @ref XRT_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED 40 + * - @ref XRT_ERROR_SWAPCHAIN_FLAG_VALID_BUT_UNSUPPORTED 41 + * - @ref XRT_ERROR_D3D12 42 + */ 43 + xrt_result_t 44 + allocateSharedImages(ID3D12Device &device, 45 + const xrt_swapchain_create_info &xsci, 46 + size_t image_count, 47 + std::vector<wil::com_ptr<ID3D12Resource>> &out_images, 48 + std::vector<wil::unique_handle> &out_handles); 49 + 50 + }; // namespace xrt::auxiliary::d3d::d3d12
+64
src/xrt/auxiliary/d3d/d3d_d3d12_helpers.cpp
··· 79 79 return S_OK; 80 80 } 81 81 82 + HRESULT 83 + createCommandListImageCopy(ID3D12Device &device, 84 + ID3D12CommandAllocator &command_allocator, 85 + ID3D12Resource &resource_src, 86 + ID3D12Resource &resource_dst, 87 + D3D12_RESOURCE_STATES src_resource_state, 88 + D3D12_RESOURCE_STATES dst_resource_state, 89 + wil::com_ptr<ID3D12CommandList> &out_copy_command_list) 90 + { 91 + wil::com_ptr<ID3D12GraphicsCommandList> copyCommandList; 92 + RETURN_IF_FAILED(device.CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, &command_allocator, nullptr, 93 + IID_PPV_ARGS(copyCommandList.put()))); 94 + 95 + // Transition images into copy state 96 + D3D12_RESOURCE_BARRIER preCopyBarriers[2]{}; 97 + preCopyBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 98 + preCopyBarriers[0].Transition.pResource = &resource_src; 99 + preCopyBarriers[0].Transition.StateBefore = src_resource_state; 100 + preCopyBarriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; 101 + preCopyBarriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; 102 + 103 + preCopyBarriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 104 + preCopyBarriers[1].Transition.pResource = &resource_dst; 105 + preCopyBarriers[1].Transition.StateBefore = dst_resource_state; 106 + preCopyBarriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; 107 + preCopyBarriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; 108 + 109 + copyCommandList->ResourceBarrier(2, preCopyBarriers); 110 + 111 + // Insert texture copy command 112 + D3D12_TEXTURE_COPY_LOCATION srcCopyLocation; 113 + srcCopyLocation.pResource = &resource_src; 114 + srcCopyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; 115 + srcCopyLocation.SubresourceIndex = 0; 116 + 117 + D3D12_TEXTURE_COPY_LOCATION dstCopyLocation; 118 + dstCopyLocation.pResource = &resource_dst; 119 + dstCopyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; 120 + dstCopyLocation.SubresourceIndex = 0; 121 + 122 + copyCommandList->CopyTextureRegion(&dstCopyLocation, 0, 0, 0, &srcCopyLocation, nullptr); 123 + 124 + // Transition images back from copy state 125 + D3D12_RESOURCE_BARRIER postCopyBarriers[2]{}; 126 + postCopyBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 127 + postCopyBarriers[0].Transition.pResource = &resource_src; 128 + postCopyBarriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; 129 + postCopyBarriers[0].Transition.StateAfter = src_resource_state; 130 + postCopyBarriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; 131 + 132 + postCopyBarriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 133 + postCopyBarriers[1].Transition.pResource = &resource_dst; 134 + postCopyBarriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; 135 + postCopyBarriers[1].Transition.StateAfter = dst_resource_state; 136 + postCopyBarriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; 137 + 138 + copyCommandList->ResourceBarrier(2, postCopyBarriers); 139 + 140 + RETURN_IF_FAILED(copyCommandList->Close()); 141 + 142 + out_copy_command_list = std::move(copyCommandList); 143 + return S_OK; 144 + } 145 + 82 146 83 147 wil::com_ptr<ID3D12Resource> 84 148 importImage(ID3D12Device &device, HANDLE h)
+21
src/xrt/auxiliary/d3d/d3d_d3d12_helpers.hpp
··· 58 58 wil::com_ptr<ID3D12CommandList> out_release_command_list); 59 59 60 60 /** 61 + * @brief Create a command list for image resource copying 62 + * 63 + * @param device D3D12 device 64 + * @param command_allocator 65 + * @param resource_src Source image 66 + * @param resource_dst Destination image 67 + * @param src_resource_state 68 + * @param dst_resource_state 69 + * @param[out] out_copy_command_list Command list output 70 + * @return HRESULT 71 + */ 72 + HRESULT 73 + createCommandListImageCopy(ID3D12Device &device, 74 + ID3D12CommandAllocator &command_allocator, 75 + ID3D12Resource &resource_src, 76 + ID3D12Resource &resource_dst, 77 + D3D12_RESOURCE_STATES src_resource_state, 78 + D3D12_RESOURCE_STATES dst_resource_state, 79 + wil::com_ptr<ID3D12CommandList> &out_copy_command_list); 80 + 81 + /** 61 82 * Imports an image into D3D12 from a handle. 62 83 * 63 84 * @param device D3D12 device