The open source OpenXR runtime
1// Copyright 2022, Collabora, Ltd.
2// Copyright 2025, Holo-Light GmbH
3// SPDX-License-Identifier: BSL-1.0
4/*!
5 * @file
6 * @brief Direct3D 12 tests.
7 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
8 * @author Krzysztof Lesiak <c-k.lesiak@holo-light.com>
9 */
10
11#include "catch_amalgamated.hpp"
12
13#include <util/u_win32_com_guard.hpp>
14#include <d3d/d3d_dxgi_helpers.hpp>
15#include <d3d/d3d_d3d12_helpers.hpp>
16#include <d3d/d3d_d3d12_allocator.hpp>
17#include "aux_d3d_dxgi_formats.hpp"
18
19#ifdef XRT_HAVE_VULKAN
20#include "vktest_init_bundle.hpp"
21#include <vk/vk_image_allocator.h>
22#include <util/u_handles.h>
23#include <d3d/d3d_dxgi_formats.h>
24#endif
25
26using namespace xrt::auxiliary::util;
27
28TEST_CASE("d3d12_device", "[.][needgpu]")
29{
30 ComGuard comGuard;
31
32 wil::com_ptr<IDXGIAdapter> adapter;
33 CHECK_NOTHROW(adapter = xrt::auxiliary::d3d::getAdapterByIndex(0, U_LOGGING_TRACE));
34 CHECK(adapter);
35
36 wil::com_ptr<ID3D12Device> device;
37 CHECK_NOTHROW(device = xrt::auxiliary::d3d::d3d12::createDevice(adapter, U_LOGGING_TRACE));
38 CHECK(device);
39}
40
41#ifdef XRT_HAVE_VULKAN
42
43static inline bool
44tryImport(struct vk_bundle *vk,
45 std::vector<wil::unique_handle> &handles,
46 const struct xrt_swapchain_create_info &xsci,
47 size_t image_mem_size)
48{
49 // in d3d11 tryImport handles is const..here we do away with it so we can call release on handles passed in?
50 // I need to read more about wil and figure out lifetime semantics of all this.
51
52 INFO("Testing import into Vulkan");
53
54 static constexpr bool use_dedicated_allocation = false;
55 xrt_swapchain_create_info vk_info = xsci;
56 vk_info.format = d3d_dxgi_format_to_vk((DXGI_FORMAT)xsci.format);
57 const auto free_vk_ic = [&](struct vk_image_collection *vkic) {
58 vk_ic_destroy(vk, vkic);
59 delete vkic;
60 };
61
62 std::shared_ptr<vk_image_collection> vkic{new vk_image_collection, free_vk_ic};
63
64 uint32_t image_count = static_cast<uint32_t>(handles.size());
65
66 // Populate for import
67 std::vector<xrt_image_native> xins;
68 xins.reserve(image_count);
69
70 for (auto &handle : handles) {
71 xrt_image_native xin;
72 xin.handle = handle.get();
73 xin.size = image_mem_size;
74 xin.use_dedicated_allocation = use_dedicated_allocation;
75 xin.is_dxgi_handle = false;
76
77 xins.emplace_back(xin);
78 }
79
80 // Import into a vulkan image collection
81 bool result = VK_SUCCESS == vk_ic_from_natives(vk, &vk_info, xins.data(), (uint32_t)xins.size(), vkic.get());
82
83 if (result) {
84 // The imported swapchain took ownership of them now, release them from ownership here.
85 for (wil::unique_handle &h : handles) {
86 h.release();
87 }
88 }
89 return result;
90}
91#else
92
93static inline bool
94tryImport(struct vk_bundle * /* vk */,
95 std::vector<wil::unique_handle> const & /* handles */,
96 const struct xrt_swapchain_create_info & /* xsci */)
97{
98 return true;
99}
100
101#endif
102
103TEST_CASE("d3d12_allocate", "[.][needgpu]")
104{
105 unique_vk_bundle vk = makeVkBundle();
106
107#ifdef XRT_HAVE_VULKAN
108 REQUIRE(vktest_init_bundle(vk.get()));
109#endif
110
111 ComGuard comGuard;
112
113 wil::com_ptr<IDXGIAdapter> adapter = xrt::auxiliary::d3d::getAdapterByIndex(0, U_LOGGING_TRACE);
114 wil::com_ptr<ID3D12Device> device = xrt::auxiliary::d3d::d3d12::createDevice(adapter, U_LOGGING_TRACE);
115 std::vector<wil::com_ptr<ID3D12Resource>> images;
116 std::vector<wil::unique_handle> handles;
117 size_t out_image_mem_size = 0;
118
119 size_t image_count = 3;
120
121 xrt_swapchain_create_info xsci{};
122 CAPTURE(xsci.sample_count = 1);
123 CAPTURE(xsci.width = 800);
124 CAPTURE(xsci.height = 600);
125
126 CAPTURE(xsci.mip_count = 1);
127 xsci.face_count = 1;
128 xsci.array_size = 1;
129
130 SECTION("create images")
131 {
132 auto nameAndFormat = GENERATE(values(namesAndFormats));
133 DYNAMIC_SECTION("Texture format " << nameAndFormat.first)
134 {
135 auto format = nameAndFormat.second;
136 CAPTURE(isDepthStencilFormat(format));
137 xsci.format = format;
138 if (isDepthStencilFormat(format)) {
139 xsci.bits = XRT_SWAPCHAIN_USAGE_DEPTH_STENCIL;
140 } else {
141 xsci.bits = XRT_SWAPCHAIN_USAGE_COLOR;
142 }
143 xsci.bits = (xrt_swapchain_usage_bits)(xsci.bits | XRT_SWAPCHAIN_USAGE_SAMPLED);
144 images.clear();
145 handles.clear();
146
147 SECTION("invalid array size 0")
148 {
149 CAPTURE(xsci.array_size = 0);
150 REQUIRE(XRT_SUCCESS !=
151 xrt::auxiliary::d3d::d3d12::allocateSharedImages(
152 *device.get(), xsci, image_count, images, handles, out_image_mem_size));
153 CHECK(images.empty());
154 CHECK(handles.empty());
155 }
156 SECTION("not array")
157 {
158 CAPTURE(xsci.array_size);
159 REQUIRE(XRT_SUCCESS ==
160 xrt::auxiliary::d3d::d3d12::allocateSharedImages(
161 *device.get(), xsci, image_count, images, handles, out_image_mem_size));
162 CHECK(images.size() == image_count);
163 CHECK(handles.size() == image_count);
164 CHECK(tryImport(vk.get(), handles, xsci, out_image_mem_size));
165 }
166 SECTION("array of 2")
167 {
168 CAPTURE(xsci.array_size = 2);
169 REQUIRE(XRT_SUCCESS ==
170 xrt::auxiliary::d3d::d3d12::allocateSharedImages(
171 *device.get(), xsci, image_count, images, handles, out_image_mem_size));
172 CHECK(images.size() == image_count);
173 CHECK(handles.size() == image_count);
174 CHECK(tryImport(vk.get(), handles, xsci, out_image_mem_size));
175 }
176 // this does not return an error...so i guess allocating cubemaps with d3d12 is fine?
177 // SECTION("cubemaps not implemented")
178 // {
179 // CAPTURE(xsci.array_size);
180 // CAPTURE(xsci.face_count = 6);
181 // REQUIRE(XRT_ERROR_ALLOCATION ==
182 // xrt::auxiliary::d3d::d3d12::allocateSharedImages(*device.get(), xsci,
183 // imageCount,
184 // images, handles,
185 // outImageMemSize));
186 // CHECK(images.empty());
187 // CHECK(handles.empty());
188 // }
189 SECTION("protected content not implemented")
190 {
191 CAPTURE(xsci.array_size);
192 CAPTURE(xsci.create = XRT_SWAPCHAIN_CREATE_PROTECTED_CONTENT);
193 REQUIRE(XRT_ERROR_SWAPCHAIN_FLAG_VALID_BUT_UNSUPPORTED ==
194 xrt::auxiliary::d3d::d3d12::allocateSharedImages(
195 *device.get(), xsci, image_count, images, handles, out_image_mem_size));
196 CHECK(images.empty());
197 CHECK(handles.empty());
198 }
199 }
200 }
201}