The open source OpenXR runtime
1// Copyright 2020-2022, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief D3D11 backed image buffer allocator.
6 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
7 * @author Fernando Velazquez Innella <finnella@magicleap.com>
8 * @ingroup aux_d3d
9 */
10
11#include "d3d_d3d11_allocator.h"
12#include "d3d_d3d11_allocator.hpp"
13
14#include "d3d_d3d11_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 <d3d11_3.h>
26#include <wil/com.h>
27#include <wil/result.h>
28
29#include <memory>
30
31#define DEFAULT_CATCH(...) \
32 catch (wil::ResultException const &e) \
33 { \
34 U_LOG_E("Caught exception: %s", e.what()); \
35 return __VA_ARGS__; \
36 } \
37 catch (std::exception const &e) \
38 { \
39 U_LOG_E("Caught exception: %s", e.what()); \
40 return __VA_ARGS__; \
41 } \
42 catch (...) \
43 { \
44 U_LOG_E("Caught exception"); \
45 return __VA_ARGS__; \
46 }
47
48
49DEBUG_GET_ONCE_LOG_OPTION(d3d11_log, "DXGI_LOG", U_LOGGING_WARN)
50#define D3DA_TRACE(...) U_LOG_IFL_T(debug_get_log_option_d3d11_log(), __VA_ARGS__)
51#define D3DA_DEBUG(...) U_LOG_IFL_D(debug_get_log_option_d3d11_log(), __VA_ARGS__)
52#define D3DA_INFO(...) U_LOG_IFL_I(debug_get_log_option_d3d11_log(), __VA_ARGS__)
53#define D3DA_WARN(...) U_LOG_IFL_W(debug_get_log_option_d3d11_log(), __VA_ARGS__)
54#define D3DA_ERROR(...) U_LOG_IFL_E(debug_get_log_option_d3d11_log(), __VA_ARGS__)
55
56namespace xrt::auxiliary::d3d::d3d11 {
57
58HANDLE
59getSharedHandle(const wil::com_ptr<ID3D11Texture2D1> &image)
60{
61 wil::com_ptr<IDXGIResource1> dxgiRes;
62 HANDLE h;
63 image.query_to(dxgiRes.put());
64 THROW_IF_FAILED(dxgiRes->GetSharedHandle(&h));
65 return h;
66}
67
68xrt_result_t
69allocateSharedImages(ID3D11Device5 &device,
70 const xrt_swapchain_create_info &xsci,
71 size_t image_count,
72 bool keyed_mutex,
73 std::vector<wil::com_ptr<ID3D11Texture2D1>> &out_images,
74 std::vector<HANDLE> &out_handles)
75try {
76 if (0 != (xsci.create & XRT_SWAPCHAIN_CREATE_PROTECTED_CONTENT)) {
77 return XRT_ERROR_SWAPCHAIN_FLAG_VALID_BUT_UNSUPPORTED;
78 }
79
80 if (0 != (xsci.create & XRT_SWAPCHAIN_CREATE_STATIC_IMAGE) && image_count > 1) {
81 D3DA_ERROR("Got XRT_SWAPCHAIN_CREATE_STATIC_IMAGE but an image count greater than 1!");
82 return XRT_ERROR_ALLOCATION;
83 }
84 if (xsci.array_size == 0) {
85 D3DA_ERROR("Array size must not be 0");
86 return XRT_ERROR_ALLOCATION;
87 }
88 CD3D11_TEXTURE2D_DESC1 desc{d3d_dxgi_format_to_typeless_dxgi((DXGI_FORMAT)xsci.format),
89 xsci.width,
90 xsci.height,
91 xsci.array_size,
92 xsci.mip_count,
93 d3d_convert_usage_bits_to_d3d11_bind_flags(xsci.bits)};
94 desc.SampleDesc.Count = xsci.sample_count;
95
96 // Using NT handles for sharing textures has a lot of limitations and issues.
97 // Depth formats are not supported and Vulkan may fail at vkAllocateMemory
98 desc.MiscFlags = keyed_mutex ? D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX : D3D11_RESOURCE_MISC_SHARED;
99
100 if (desc.Format == 0) {
101 D3DA_ERROR("Invalid format %04" PRIx64 "!", (uint64_t)xsci.format);
102 return XRT_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED;
103 }
104 if (xsci.face_count == 6) {
105 desc.ArraySize *= 6;
106 desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
107 }
108 // Create textures
109 std::vector<wil::com_ptr<ID3D11Texture2D1>> images;
110 for (size_t i = 0; i < image_count; ++i) {
111 wil::com_ptr<ID3D11Texture2D1> tex;
112 if (FAILED(LOG_IF_FAILED(device.CreateTexture2D1(&desc, nullptr, tex.put())))) {
113 return XRT_ERROR_ALLOCATION;
114 }
115 images.emplace_back(std::move(tex));
116 }
117
118 std::vector<HANDLE> handles;
119 handles.reserve(image_count);
120 for (const auto &tex : images) {
121 handles.emplace_back(getSharedHandle(tex));
122 }
123 out_images = std::move(images);
124 out_handles = std::move(handles);
125 return XRT_SUCCESS;
126}
127DEFAULT_CATCH(XRT_ERROR_ALLOCATION)
128
129} // namespace xrt::auxiliary::d3d::d3d11
130
131struct d3d11_allocator
132{
133 struct xrt_image_native_allocator base;
134 wil::com_ptr<ID3D11Device5> device;
135};
136
137
138static xrt_result_t
139d3d11_images_allocate(struct xrt_image_native_allocator *xina,
140 const struct xrt_swapchain_create_info *xsci,
141 size_t image_count,
142 struct xrt_image_native *out_images)
143{
144 try {
145 d3d11_allocator *d3da = reinterpret_cast<d3d11_allocator *>(xina);
146
147 std::vector<wil::com_ptr<ID3D11Texture2D1>> images;
148 std::vector<HANDLE> handles;
149 auto result = xrt::auxiliary::d3d::d3d11::allocateSharedImages(*(d3da->device), *xsci, image_count,
150 false, images, handles);
151
152 if (result != XRT_SUCCESS) {
153 return result;
154 }
155
156 for (size_t i = 0; i < image_count; ++i) {
157 out_images[i].handle = handles[i];
158 out_images[i].is_dxgi_handle = true;
159 }
160
161 return XRT_SUCCESS;
162 }
163 DEFAULT_CATCH(XRT_ERROR_ALLOCATION)
164}
165
166static xrt_result_t
167d3d11_images_free(struct xrt_image_native_allocator *xina, size_t image_count, struct xrt_image_native *images)
168{
169 for (size_t i = 0; i < image_count; ++i) {
170 if (!images[i].is_dxgi_handle) {
171 u_graphics_buffer_unref(&(images[i].handle));
172 }
173 }
174 return XRT_SUCCESS;
175}
176
177static void
178d3d11_destroy(struct xrt_image_native_allocator *xina)
179{
180 d3d11_allocator *d3da = reinterpret_cast<d3d11_allocator *>(xina);
181 delete d3da;
182}
183
184struct xrt_image_native_allocator *
185d3d11_allocator_create(ID3D11Device *device)
186try {
187 auto ret = std::make_unique<d3d11_allocator>();
188 U_ZERO(&(ret->base));
189 ret->base.images_allocate = d3d11_images_allocate;
190 ret->base.images_free = d3d11_images_free;
191 ret->base.destroy = d3d11_destroy;
192 return &(ret.release()->base);
193}
194DEFAULT_CATCH(nullptr)