The open source OpenXR runtime
1// Copyright 2020-2024, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief D3D12 backed image buffer allocator.
6 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
7 * @author Fernando Velazquez Innella <finnella@magicleap.com>
8 * @author Korcan Hussein <korcan.hussein@collabora.com>
9 * @ingroup aux_d3d
10 */
11
12#include "d3d_d3d12_allocator.h"
13#include "d3d_d3d12_allocator.hpp"
14
15#include "d3d_d3d12_bits.h"
16#include "d3d_dxgi_formats.h"
17
18#include "util/u_misc.h"
19#include "util/u_logging.h"
20#include "util/u_debug.h"
21#include "util/u_handles.h"
22
23#include "xrt/xrt_windows.h"
24
25#include <Unknwn.h>
26#include <d3d12.h>
27#include <wil/com.h>
28#include <wil/result.h>
29
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
50DEBUG_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
57namespace xrt::auxiliary::d3d::d3d12 {
58
59static wil::unique_handle
60createSharedHandle(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
73xrt_result_t
74allocateSharedImages(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 std::uint64_t &out_image_mem_size)
80try {
81 if (0 != (xsci.create & XRT_SWAPCHAIN_CREATE_PROTECTED_CONTENT)) {
82 return XRT_ERROR_SWAPCHAIN_FLAG_VALID_BUT_UNSUPPORTED;
83 }
84
85 if (0 != (xsci.create & XRT_SWAPCHAIN_CREATE_STATIC_IMAGE) && image_count > 1) {
86 D3DA_ERROR("Got XRT_SWAPCHAIN_CREATE_STATIC_IMAGE but an image count greater than 1!");
87 return XRT_ERROR_ALLOCATION;
88 }
89 if (xsci.array_size == 0) {
90 D3DA_ERROR("Array size must not be 0");
91 return XRT_ERROR_ALLOCATION;
92 }
93
94 // TODO: See if this is still necessary
95 DXGI_FORMAT typeless_format = d3d_dxgi_format_to_typeless_dxgi((DXGI_FORMAT)xsci.format);
96 if (typeless_format == 0) {
97 D3DA_ERROR("Invalid format %04" PRIx64 "!", (uint64_t)xsci.format);
98 return XRT_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED;
99 }
100
101 DXGI_SAMPLE_DESC sample_desc{
102 xsci.sample_count, // Count
103 0, // Quality
104 };
105
106 // Note:
107 // To use a cross-adapter heap the following flag must be passed:
108 // resource_flags |= D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
109 // Additionally, only copy operations are allowed with the resource.
110 D3D12_RESOURCE_FLAGS resource_flags = d3d_convert_usage_bits_to_d3d12_resource_flags(xsci.bits);
111
112 D3D12_RESOURCE_DESC desc{
113 D3D12_RESOURCE_DIMENSION_TEXTURE2D, // Dimension
114 0, // Alignment
115 xsci.width, // Width
116 xsci.height, // Height
117 (UINT16)xsci.array_size, // DepthOrArraySize
118 (UINT16)xsci.mip_count, // MipLevels
119 typeless_format, // Format
120 sample_desc, // SampleDesc
121 D3D12_TEXTURE_LAYOUT_UNKNOWN, // Layout
122 resource_flags // Flags;
123 };
124
125 // Cubemap
126 if (xsci.face_count == 6) {
127 desc.DepthOrArraySize *= 6;
128 }
129
130 // Create resources and let the driver manage memory
131 std::vector<wil::com_ptr<ID3D12Resource>> images;
132 D3D12_HEAP_PROPERTIES heap{};
133 heap.Type = D3D12_HEAP_TYPE_DEFAULT;
134 D3D12_HEAP_FLAGS heap_flags = D3D12_HEAP_FLAG_SHARED;
135 D3D12_RESOURCE_STATES initial_resource_state = d3d_convert_usage_bits_to_d3d12_app_resource_state(xsci.bits);
136
137 for (size_t i = 0; i < image_count; ++i) {
138 wil::com_ptr<ID3D12Resource> tex;
139 HRESULT res = device.CreateCommittedResource( //
140 &heap, // pHeapProperties
141 heap_flags, // HeapFlags
142 &desc, // pDesc
143 initial_resource_state, // InitialResourceState
144 nullptr, // pOptimizedClearValue
145 IID_PPV_ARGS(tex.put())); // riidResource, ppvResource
146
147 if (FAILED(LOG_IF_FAILED(res))) {
148 return XRT_ERROR_ALLOCATION;
149 }
150 images.emplace_back(std::move(tex));
151 }
152
153 std::vector<wil::unique_handle> handles;
154 handles.reserve(image_count);
155 for (const auto &tex : images) {
156 handles.emplace_back(createSharedHandle(device, tex));
157 }
158 out_images = std::move(images);
159 out_handles = std::move(handles);
160 const D3D12_RESOURCE_ALLOCATION_INFO alloc_info = device.GetResourceAllocationInfo(0, 1, &desc);
161 out_image_mem_size = alloc_info.SizeInBytes;
162 return XRT_SUCCESS;
163}
164DEFAULT_CATCH(XRT_ERROR_ALLOCATION)
165
166} // namespace xrt::auxiliary::d3d::d3d12
167
168struct d3d12_allocator
169{
170 struct xrt_image_native_allocator base;
171 wil::com_ptr<ID3D12Device> device;
172};
173
174
175static xrt_result_t
176d3d12_images_allocate(struct xrt_image_native_allocator *xina,
177 const struct xrt_swapchain_create_info *xsci,
178 size_t image_count,
179 struct xrt_image_native *out_images)
180{
181 try {
182 d3d12_allocator *d3da = reinterpret_cast<d3d12_allocator *>(xina);
183
184 std::uint64_t image_mem_size = 0;
185 std::vector<wil::com_ptr<ID3D12Resource>> images;
186 std::vector<wil::unique_handle> handles;
187 auto result = xrt::auxiliary::d3d::d3d12::allocateSharedImages( //
188 *(d3da->device), // device
189 *xsci, // xsci
190 image_count, // image_count
191 images, // out_images
192 handles, // out_handles
193 image_mem_size); // out_image_mem_size (in bytes)
194
195 if (result != XRT_SUCCESS) {
196 return result;
197 }
198
199 for (size_t i = 0; i < image_count; ++i) {
200 out_images[i].handle = handles[i].release();
201 out_images[i].is_dxgi_handle = false;
202 out_images[i].size = image_mem_size;
203 }
204
205 return XRT_SUCCESS;
206 }
207 DEFAULT_CATCH(XRT_ERROR_ALLOCATION)
208}
209
210static xrt_result_t
211d3d12_images_free(struct xrt_image_native_allocator *xina, size_t image_count, struct xrt_image_native *images)
212{
213 for (size_t i = 0; i < image_count; ++i) {
214 u_graphics_buffer_unref(&(images[i].handle));
215 }
216 return XRT_SUCCESS;
217}
218
219static void
220d3d12_destroy(struct xrt_image_native_allocator *xina)
221{
222 d3d12_allocator *d3da = reinterpret_cast<d3d12_allocator *>(xina);
223 delete d3da;
224}
225
226struct xrt_image_native_allocator *
227d3d12_allocator_create(ID3D11Device *device)
228try {
229 auto ret = std::make_unique<d3d12_allocator>();
230 U_ZERO(&(ret->base));
231 ret->base.images_allocate = d3d12_images_allocate;
232 ret->base.images_free = d3d12_images_free;
233 ret->base.destroy = d3d12_destroy;
234 return &(ret.release()->base);
235}
236DEFAULT_CATCH(nullptr)