The open source OpenXR runtime
1// Copyright 2019-2023, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief D3D11 client side glue to compositor implementation.
6 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
7 * @author Jakob Bornecrantz <jakob@collabora.com>
8 * @author Fernando Velazquez Innella <finnella@magicleap.com>
9 * @ingroup comp_client
10 */
11
12#include "comp_d3d11_client.h"
13
14#include "comp_d3d_common.hpp"
15
16#include "xrt/xrt_compositor.h"
17#include "xrt/xrt_config_os.h"
18#include "xrt/xrt_handles.h"
19#include "xrt/xrt_deleters.hpp"
20#include "xrt/xrt_results.h"
21#include "xrt/xrt_vulkan_includes.h"
22#include "d3d/d3d_dxgi_formats.h"
23#include "d3d/d3d_d3d11_helpers.hpp"
24#include "d3d/d3d_d3d11_allocator.hpp"
25#include "d3d/d3d_d3d11_fence.hpp"
26#include "util/u_misc.h"
27#include "util/u_pretty_print.h"
28#include "util/u_time.h"
29#include "util/u_logging.h"
30#include "util/u_debug.h"
31#include "util/u_handles.h"
32#include "util/u_win32_com_guard.hpp"
33
34#include <d3d11_1.h>
35#include <d3d11_3.h>
36#include <wil/resource.h>
37#include <wil/com.h>
38#include <wil/result_macros.h>
39
40#include <assert.h>
41#include <memory>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <chrono>
46
47using namespace std::chrono_literals;
48using namespace std::chrono;
49
50using xrt::compositor::client::unique_swapchain_ref;
51
52DEBUG_GET_ONCE_LOG_OPTION(log, "D3D_COMPOSITOR_LOG", U_LOGGING_INFO)
53
54/*!
55 * Spew level logging.
56 *
57 * @relates client_d3d11_compositor
58 */
59#define D3D_SPEW(c, ...) U_LOG_IFL_T(c->log_level, __VA_ARGS__);
60
61/*!
62 * Debug level logging.
63 *
64 * @relates client_d3d11_compositor
65 */
66#define D3D_DEBUG(c, ...) U_LOG_IFL_D(c->log_level, __VA_ARGS__);
67
68/*!
69 * Info level logging.
70 *
71 * @relates client_d3d11_compositor
72 */
73#define D3D_INFO(c, ...) U_LOG_IFL_I(c->log_level, __VA_ARGS__);
74
75/*!
76 * Warn level logging.
77 *
78 * @relates client_d3d11_compositor
79 */
80#define D3D_WARN(c, ...) U_LOG_IFL_W(c->log_level, __VA_ARGS__);
81
82/*!
83 * Error level logging.
84 *
85 * @relates client_d3d11_compositor
86 */
87#define D3D_ERROR(c, ...) U_LOG_IFL_E(c->log_level, __VA_ARGS__);
88
89using unique_compositor_semaphore_ref = std::unique_ptr<
90 struct xrt_compositor_semaphore,
91 xrt::deleters::reference_deleter<struct xrt_compositor_semaphore, xrt_compositor_semaphore_reference>>;
92
93// 0 is special
94static constexpr uint64_t kKeyedMutexKey = 0;
95
96// Timeout to wait for completion
97static constexpr auto kFenceTimeout = 500ms;
98
99/*!
100 * @class client_d3d11_compositor
101 *
102 * Wraps the real compositor providing a D3D11 based interface.
103 *
104 * @ingroup comp_client
105 * @implements xrt_compositor_d3d11
106 */
107struct client_d3d11_compositor
108{
109 struct xrt_compositor_d3d11 base = {};
110
111 //! Owning reference to the backing native compositor
112 struct xrt_compositor_native *xcn{nullptr};
113
114 //! Just keeps COM alive while we keep references to COM things.
115 xrt::auxiliary::util::ComGuard com_guard;
116
117 //! Logging level.
118 enum u_logging_level log_level = U_LOGGING_INFO;
119
120 //! Device we got from the app
121 wil::com_ptr<ID3D11Device5> app_device;
122
123 //! Immediate context for @ref app_device
124 wil::com_ptr<ID3D11DeviceContext3> app_context;
125
126 //! A similar device we created on the same adapter
127 wil::com_ptr<ID3D11Device5> comp_device;
128
129 //! Immediate context for @ref comp_device
130 wil::com_ptr<ID3D11DeviceContext4> comp_context;
131
132 //! Device used for the fence, currently the @ref app_device.
133 wil::com_ptr<ID3D11Device5> fence_device;
134
135 //! Immediate context for @ref fence_device
136 wil::com_ptr<ID3D11DeviceContext4> fence_context;
137
138 // wil::unique_handle timeline_semaphore_handle;
139
140 /*!
141 * A timeline semaphore made by the native compositor and imported by us.
142 *
143 * When this is valid, we should use xrt_compositor::layer_commit_with_semaphone:
144 * it means the native compositor knows about timeline semaphores, and we can import its semaphores, so we can
145 * pass @ref timeline_semaphore instead of blocking locally.
146 */
147 unique_compositor_semaphore_ref timeline_semaphore;
148
149 /*!
150 * A fence (timeline semaphore) object, owned by @ref fence_device.
151 *
152 * Signal using @ref fence_context if this is not null.
153 *
154 * Wait on it in `layer_commit` if @ref timeline_semaphore *is* null/invalid.
155 */
156 wil::com_ptr<ID3D11Fence> fence;
157
158 /*!
159 * Event used for blocking in `layer_commit` if required (if @ref timeline_semaphore *is* null/invalid)
160 */
161 wil::unique_event_nothrow local_wait_event;
162
163 /*!
164 * The value most recently signaled on the timeline semaphore
165 */
166 uint64_t timeline_semaphore_value = 0;
167};
168
169static_assert(std::is_standard_layout<client_d3d11_compositor>::value);
170
171struct client_d3d11_swapchain;
172
173static inline DWORD
174convertTimeoutToWindowsMilliseconds(int64_t timeout_ns)
175{
176 return (timeout_ns == XRT_INFINITE_DURATION) ? INFINITE : (DWORD)(timeout_ns / (int64_t)U_TIME_1MS_IN_NS);
177}
178
179/*!
180 * Split out from @ref client_d3d11_swapchain to ensure that it is standard
181 * layout, std::vector for instance is not standard layout.
182 */
183struct client_d3d11_swapchain_data
184{
185 explicit client_d3d11_swapchain_data(enum u_logging_level log_level) : keyed_mutex_collection(log_level) {}
186
187 xrt::compositor::client::KeyedMutexCollection keyed_mutex_collection;
188
189 //! The shared DXGI handles for our images
190 std::vector<HANDLE> dxgi_handles;
191
192 //! Images associated with client_d3d11_compositor::app_device
193 std::vector<wil::com_ptr<ID3D11Texture2D1>> app_images;
194
195 //! Images associated with client_d3d11_compositor::comp_device
196 std::vector<wil::com_ptr<ID3D11Texture2D1>> comp_images;
197};
198
199/*!
200 * Wraps the real compositor swapchain providing a D3D11 based interface.
201 *
202 * @ingroup comp_client
203 * @implements xrt_swapchain_d3d11
204 */
205struct client_d3d11_swapchain
206{
207 struct xrt_swapchain_d3d11 base;
208
209 //! Owning reference to the imported swapchain.
210 unique_swapchain_ref xsc;
211
212 //! Non-owning reference to our parent compositor.
213 struct client_d3d11_compositor *c;
214
215 //! implementation struct with things that aren't standard_layout
216 std::unique_ptr<client_d3d11_swapchain_data> data;
217};
218
219static_assert(std::is_standard_layout<client_d3d11_swapchain>::value);
220
221/*!
222 * Down-cast helper.
223 * @private @memberof client_d3d11_swapchain
224 */
225static inline struct client_d3d11_swapchain *
226as_client_d3d11_swapchain(struct xrt_swapchain *xsc)
227{
228 return reinterpret_cast<client_d3d11_swapchain *>(xsc);
229}
230
231/*!
232 * Down-cast helper.
233 * @private @memberof client_d3d11_compositor
234 */
235static inline struct client_d3d11_compositor *
236as_client_d3d11_compositor(struct xrt_compositor *xc)
237{
238 return (struct client_d3d11_compositor *)xc;
239}
240
241
242/*
243 *
244 * Logging helper.
245 *
246 */
247static constexpr size_t kErrorBufSize = 256;
248
249template <size_t N>
250static inline bool
251formatMessage(DWORD err, char (&buf)[N])
252{
253 if (0 != FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err,
254 LANG_SYSTEM_DEFAULT, buf, N - 1, NULL)) {
255 return true;
256 }
257 memset(buf, 0, N);
258 return false;
259}
260
261
262/*
263 *
264 * Swapchain functions.
265 *
266 */
267
268static xrt_result_t
269client_d3d11_swapchain_acquire_image(struct xrt_swapchain *xsc, uint32_t *out_index)
270{
271 struct client_d3d11_swapchain *sc = as_client_d3d11_swapchain(xsc);
272
273 // Pipe down call into imported swapchain in native compositor.
274 return xrt_swapchain_acquire_image(sc->xsc.get(), out_index);
275}
276
277static xrt_result_t
278client_d3d11_swapchain_wait_image(struct xrt_swapchain *xsc, int64_t timeout_ns, uint32_t index)
279{
280 struct client_d3d11_swapchain *sc = as_client_d3d11_swapchain(xsc);
281
282 // Pipe down call into imported swapchain in native compositor.
283 xrt_result_t xret = xrt_swapchain_wait_image(sc->xsc.get(), timeout_ns, index);
284
285 if (xret == XRT_SUCCESS) {
286 // OK, we got the image in the native compositor, now need the keyed mutex in d3d11.
287 xret = sc->data->keyed_mutex_collection.waitKeyedMutex(index, timeout_ns);
288 }
289
290 //! @todo discard old contents?
291 return xret;
292}
293
294static xrt_result_t
295client_d3d11_swapchain_barrier_image(struct xrt_swapchain *xsc, enum xrt_barrier_direction direction, uint32_t index)
296{
297 return XRT_SUCCESS;
298}
299
300static xrt_result_t
301client_d3d11_swapchain_release_image(struct xrt_swapchain *xsc, uint32_t index)
302{
303 struct client_d3d11_swapchain *sc = as_client_d3d11_swapchain(xsc);
304
305 // Pipe down call into imported swapchain in native compositor.
306 xrt_result_t xret = xrt_swapchain_release_image(sc->xsc.get(), index);
307
308 if (xret == XRT_SUCCESS) {
309 // Release the keyed mutex
310 xret = sc->data->keyed_mutex_collection.releaseKeyedMutex(index);
311 }
312 return xret;
313}
314
315static void
316client_d3d11_swapchain_destroy(struct xrt_swapchain *xsc)
317{
318 // letting destruction do it all
319 std::unique_ptr<client_d3d11_swapchain> sc(as_client_d3d11_swapchain(xsc));
320}
321
322/*
323 *
324 * Import helpers
325 *
326 */
327
328static wil::com_ptr<ID3D11Texture2D1>
329import_image(ID3D11Device1 &device, HANDLE h)
330{
331 wil::com_ptr<ID3D11Texture2D1> tex;
332
333 if (h == nullptr) {
334 return {};
335 }
336 THROW_IF_FAILED(device.OpenSharedResource1(h, __uuidof(ID3D11Texture2D1), tex.put_void()));
337 return tex;
338}
339
340static wil::com_ptr<ID3D11Texture2D1>
341import_image_dxgi(ID3D11Device1 &device, HANDLE h)
342{
343 wil::com_ptr<ID3D11Texture2D1> tex;
344
345 if (h == nullptr) {
346 return {};
347 }
348 THROW_IF_FAILED(device.OpenSharedResource(h, __uuidof(ID3D11Texture2D1), tex.put_void()));
349 return tex;
350}
351
352static wil::com_ptr<ID3D11Fence>
353import_fence(ID3D11Device5 &device, HANDLE h)
354{
355 wil::com_ptr<ID3D11Fence> fence;
356
357 if (h == nullptr) {
358 return {};
359 }
360 THROW_IF_FAILED(device.OpenSharedFence(h, __uuidof(ID3D11Fence), fence.put_void()));
361 return fence;
362}
363
364
365xrt_result_t
366client_d3d11_create_swapchain(struct xrt_compositor *xc,
367 const struct xrt_swapchain_create_info *info,
368 struct xrt_swapchain **out_xsc)
369try {
370 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
371 xrt_result_t xret = XRT_SUCCESS;
372 xrt_swapchain_create_properties xsccp{};
373 xret = xrt_comp_get_swapchain_create_properties(xc, info, &xsccp);
374
375 if (xret != XRT_SUCCESS) {
376 D3D_ERROR(c, "Could not get properties for creating swapchain");
377 return xret;
378 }
379 uint32_t image_count = xsccp.image_count;
380
381
382 if ((info->create & XRT_SWAPCHAIN_CREATE_PROTECTED_CONTENT) != 0) {
383 D3D_WARN(c,
384 "Swapchain info is valid but this compositor doesn't support creating protected content "
385 "swapchains!");
386 return XRT_ERROR_SWAPCHAIN_FLAG_VALID_BUT_UNSUPPORTED;
387 }
388
389 int64_t vk_format = d3d_dxgi_format_to_vk((DXGI_FORMAT)info->format);
390 if (vk_format == 0) {
391 D3D_ERROR(c, "Invalid format!");
392 return XRT_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED;
393 }
394
395 struct xrt_swapchain_create_info xinfo = *info;
396 struct xrt_swapchain_create_info vkinfo = *info;
397
398 // Update the create info.
399 xinfo.bits = (enum xrt_swapchain_usage_bits)(xsccp.extra_bits | xinfo.bits);
400 vkinfo.format = vk_format;
401 vkinfo.bits = (enum xrt_swapchain_usage_bits)(xsccp.extra_bits | vkinfo.bits);
402
403 std::unique_ptr<struct client_d3d11_swapchain> sc = std::make_unique<struct client_d3d11_swapchain>();
404 sc->data = std::make_unique<client_d3d11_swapchain_data>(c->log_level);
405 auto &data = sc->data;
406 xret = xrt::auxiliary::d3d::d3d11::allocateSharedImages(*(c->comp_device), xinfo, image_count, true,
407 data->comp_images, data->dxgi_handles);
408 if (xret != XRT_SUCCESS) {
409 return xret;
410 }
411
412 data->app_images.reserve(image_count);
413
414 // Import from the handle for the app.
415 for (uint32_t i = 0; i < image_count; ++i) {
416 wil::com_ptr<ID3D11Texture2D1> image = import_image_dxgi(*(c->app_device), data->dxgi_handles[i]);
417
418 // Put the image where the OpenXR state tracker can get it
419 sc->base.images[i] = image.get();
420
421 // Store the owning pointer for lifetime management
422 data->app_images.emplace_back(std::move(image));
423 }
424
425 // Cache the keyed mutex interface
426 xret = data->keyed_mutex_collection.init(data->app_images);
427 if (xret != XRT_SUCCESS) {
428 D3D_ERROR(c, "Error retrieving keyex mutex interfaces");
429 return xret;
430 }
431
432 // Import into the native compositor, to create the corresponding swapchain which we wrap.
433 xret = xrt::compositor::client::importFromDxgiHandles(
434 *(c->xcn), data->dxgi_handles, vkinfo, false /** @todo not sure - dedicated allocation */, sc->xsc);
435
436 if (xret != XRT_SUCCESS) {
437 D3D_ERROR(c, "Error importing D3D11 swapchain into native compositor");
438 return xret;
439 }
440
441 sc->base.base.destroy = client_d3d11_swapchain_destroy;
442 sc->base.base.acquire_image = client_d3d11_swapchain_acquire_image;
443 sc->base.base.wait_image = client_d3d11_swapchain_wait_image;
444 sc->base.base.barrier_image = client_d3d11_swapchain_barrier_image;
445 sc->base.base.release_image = client_d3d11_swapchain_release_image;
446 sc->c = c;
447 sc->base.base.image_count = image_count;
448
449 xrt_swapchain_reference(out_xsc, &sc->base.base);
450 (void)sc.release();
451 return XRT_SUCCESS;
452} catch (wil::ResultException const &e) {
453 U_LOG_E("Error creating D3D11 swapchain: %s", e.what());
454 return XRT_ERROR_ALLOCATION;
455} catch (std::exception const &e) {
456 U_LOG_E("Error creating D3D11 swapchain: %s", e.what());
457 return XRT_ERROR_ALLOCATION;
458} catch (...) {
459 U_LOG_E("Error creating D3D11 swapchain");
460 return XRT_ERROR_ALLOCATION;
461}
462
463static xrt_result_t
464client_d3d11_compositor_passthrough_create(struct xrt_compositor *xc, const struct xrt_passthrough_create_info *info)
465{
466 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
467
468 // Pipe down call into native compositor.
469 return xrt_comp_create_passthrough(&c->xcn->base, info);
470}
471
472static xrt_result_t
473client_d3d11_compositor_passthrough_layer_create(struct xrt_compositor *xc,
474 const struct xrt_passthrough_layer_create_info *info)
475{
476 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
477
478 // Pipe down call into native compositor.
479 return xrt_comp_create_passthrough_layer(&c->xcn->base, info);
480}
481
482static xrt_result_t
483client_d3d11_compositor_passthrough_destroy(struct xrt_compositor *xc)
484{
485 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
486
487 // Pipe down call into native compositor.
488 return xrt_comp_destroy_passthrough(&c->xcn->base);
489}
490
491/*
492 *
493 * Compositor functions.
494 *
495 */
496
497
498static xrt_result_t
499client_d3d11_compositor_begin_session(struct xrt_compositor *xc, const struct xrt_begin_session_info *info)
500{
501 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
502
503 // Pipe down call into native compositor.
504 return xrt_comp_begin_session(&c->xcn->base, info);
505}
506
507static xrt_result_t
508client_d3d11_compositor_end_session(struct xrt_compositor *xc)
509{
510 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
511
512 // Pipe down call into native compositor.
513 return xrt_comp_end_session(&c->xcn->base);
514}
515
516static xrt_result_t
517client_d3d11_compositor_wait_frame(struct xrt_compositor *xc,
518 int64_t *out_frame_id,
519 int64_t *predicted_display_time,
520 int64_t *predicted_display_period)
521{
522 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
523
524 // Pipe down call into native compositor.
525 return xrt_comp_wait_frame(&c->xcn->base, out_frame_id, predicted_display_time, predicted_display_period);
526}
527
528static xrt_result_t
529client_d3d11_compositor_begin_frame(struct xrt_compositor *xc, int64_t frame_id)
530{
531 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
532
533 // Pipe down call into native compositor.
534 return xrt_comp_begin_frame(&c->xcn->base, frame_id);
535}
536
537static xrt_result_t
538client_d3d11_compositor_discard_frame(struct xrt_compositor *xc, int64_t frame_id)
539{
540 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
541
542 // Pipe down call into native compositor.
543 return xrt_comp_discard_frame(&c->xcn->base, frame_id);
544}
545
546static xrt_result_t
547client_d3d11_compositor_layer_begin(struct xrt_compositor *xc, const struct xrt_layer_frame_data *data)
548{
549 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
550
551 // Pipe down call into native compositor.
552 return xrt_comp_layer_begin(&c->xcn->base, data);
553}
554
555static xrt_result_t
556client_d3d11_compositor_layer_projection(struct xrt_compositor *xc,
557 struct xrt_device *xdev,
558 struct xrt_swapchain *xsc[XRT_MAX_VIEWS],
559 const struct xrt_layer_data *data)
560{
561 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
562
563 assert(data->type == XRT_LAYER_PROJECTION);
564
565 struct xrt_swapchain *xscn[XRT_MAX_VIEWS];
566 for (uint32_t i = 0; i < data->view_count; ++i) {
567 xscn[i] = as_client_d3d11_swapchain(xsc[i])->xsc.get();
568 }
569
570 // No flip required: D3D11 swapchain image convention matches Vulkan.
571 return xrt_comp_layer_projection(&c->xcn->base, xdev, xscn, data);
572}
573
574static xrt_result_t
575client_d3d11_compositor_layer_projection_depth(struct xrt_compositor *xc,
576 struct xrt_device *xdev,
577 struct xrt_swapchain *xsc[XRT_MAX_VIEWS],
578 struct xrt_swapchain *d_xsc[XRT_MAX_VIEWS],
579 const struct xrt_layer_data *data)
580{
581 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
582
583 assert(data->type == XRT_LAYER_PROJECTION_DEPTH);
584 struct xrt_swapchain *xscn[XRT_MAX_VIEWS];
585 struct xrt_swapchain *d_xscn[XRT_MAX_VIEWS];
586 for (uint32_t i = 0; i < data->view_count; ++i) {
587 xscn[i] = as_client_d3d11_swapchain(xsc[i])->xsc.get();
588 d_xscn[i] = as_client_d3d11_swapchain(d_xsc[i])->xsc.get();
589 }
590
591 // No flip required: D3D11 swapchain image convention matches Vulkan.
592 return xrt_comp_layer_projection_depth(&c->xcn->base, xdev, xscn, d_xscn, data);
593}
594
595static xrt_result_t
596client_d3d11_compositor_layer_quad(struct xrt_compositor *xc,
597 struct xrt_device *xdev,
598 struct xrt_swapchain *xsc,
599 const struct xrt_layer_data *data)
600{
601 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
602
603 assert(data->type == XRT_LAYER_QUAD);
604
605 struct xrt_swapchain *xscfb = as_client_d3d11_swapchain(xsc)->xsc.get();
606
607 // No flip required: D3D11 swapchain image convention matches Vulkan.
608 return xrt_comp_layer_quad(&c->xcn->base, xdev, xscfb, data);
609}
610
611static xrt_result_t
612client_d3d11_compositor_layer_cube(struct xrt_compositor *xc,
613 struct xrt_device *xdev,
614 struct xrt_swapchain *xsc,
615 const struct xrt_layer_data *data)
616{
617 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
618
619 assert(data->type == XRT_LAYER_CUBE);
620
621 struct xrt_swapchain *xscfb = as_client_d3d11_swapchain(xsc)->xsc.get();
622
623 // No flip required: D3D11 swapchain image convention matches Vulkan.
624 return xrt_comp_layer_cube(&c->xcn->base, xdev, xscfb, data);
625}
626
627static xrt_result_t
628client_d3d11_compositor_layer_cylinder(struct xrt_compositor *xc,
629 struct xrt_device *xdev,
630 struct xrt_swapchain *xsc,
631 const struct xrt_layer_data *data)
632{
633 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
634
635 assert(data->type == XRT_LAYER_CYLINDER);
636
637 struct xrt_swapchain *xscfb = as_client_d3d11_swapchain(xsc)->xsc.get();
638
639 // No flip required: D3D11 swapchain image convention matches Vulkan.
640 return xrt_comp_layer_cylinder(&c->xcn->base, xdev, xscfb, data);
641}
642
643static xrt_result_t
644client_d3d11_compositor_layer_equirect1(struct xrt_compositor *xc,
645 struct xrt_device *xdev,
646 struct xrt_swapchain *xsc,
647 const struct xrt_layer_data *data)
648{
649 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
650
651 assert(data->type == XRT_LAYER_EQUIRECT1);
652
653 struct xrt_swapchain *xscfb = as_client_d3d11_swapchain(xsc)->xsc.get();
654
655 // No flip required: D3D11 swapchain image convention matches Vulkan.
656 return xrt_comp_layer_equirect1(&c->xcn->base, xdev, xscfb, data);
657}
658
659static xrt_result_t
660client_d3d11_compositor_layer_equirect2(struct xrt_compositor *xc,
661 struct xrt_device *xdev,
662 struct xrt_swapchain *xsc,
663 const struct xrt_layer_data *data)
664{
665 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
666
667 assert(data->type == XRT_LAYER_EQUIRECT2);
668
669 struct xrt_swapchain *xscfb = as_client_d3d11_swapchain(xsc)->xsc.get();
670
671 // No flip required: D3D11 swapchain image convention matches Vulkan.
672 return xrt_comp_layer_equirect2(&c->xcn->base, xdev, xscfb, data);
673}
674
675static xrt_result_t
676client_d3d11_compositor_layer_passthrough(struct xrt_compositor *xc,
677 struct xrt_device *xdev,
678 const struct xrt_layer_data *data)
679{
680 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
681
682 assert(data->type == XRT_LAYER_PASSTHROUGH);
683
684 // No flip required: D3D11 swapchain image convention matches Vulkan.
685 return xrt_comp_layer_passthrough(&c->xcn->base, xdev, data);
686}
687
688static xrt_result_t
689client_d3d11_compositor_layer_commit(struct xrt_compositor *xc, xrt_graphics_sync_handle_t sync_handle)
690{
691 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
692
693 // We make the sync object, not st/oxr which is our user.
694 assert(!xrt_graphics_sync_handle_is_valid(sync_handle));
695
696 xrt_result_t xret = XRT_SUCCESS;
697 if (c->fence) {
698 c->timeline_semaphore_value++;
699 HRESULT hr = c->fence_context->Signal(c->fence.get(), c->timeline_semaphore_value);
700 if (!SUCCEEDED(hr)) {
701 char buf[kErrorBufSize];
702 formatMessage(hr, buf);
703 D3D_ERROR(c, "Error signaling fence: %s", buf);
704 return xrt_comp_layer_commit(&c->xcn->base, XRT_GRAPHICS_SYNC_HANDLE_INVALID);
705 }
706 }
707
708 if (c->timeline_semaphore) {
709 // We got this from the native compositor, so we can pass it back
710 return xrt_comp_layer_commit_with_semaphore( //
711 &c->xcn->base, //
712 c->timeline_semaphore.get(), //
713 c->timeline_semaphore_value); //
714 }
715
716 if (c->fence) {
717 // Wait on it ourselves, if we have it and didn't tell the native compositor to wait on it.
718 xret = xrt::auxiliary::d3d::d3d11::waitOnFenceWithTimeout( //
719 c->fence, //
720 c->local_wait_event, //
721 c->timeline_semaphore_value, //
722 kFenceTimeout); //
723 if (xret != XRT_SUCCESS) {
724 struct u_pp_sink_stack_only sink; // Not inited, very large.
725 u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink);
726 u_pp(dg, "Problem waiting on fence: ");
727 u_pp_xrt_result(dg, xret);
728 D3D_ERROR(c, "%s", sink.buffer);
729
730 return xret;
731 }
732 }
733
734 return xrt_comp_layer_commit(&c->xcn->base, XRT_GRAPHICS_SYNC_HANDLE_INVALID);
735}
736
737
738static xrt_result_t
739client_d3d11_compositor_get_swapchain_create_properties(struct xrt_compositor *xc,
740 const struct xrt_swapchain_create_info *info,
741 struct xrt_swapchain_create_properties *xsccp)
742{
743 struct client_d3d11_compositor *c = as_client_d3d11_compositor(xc);
744
745 int64_t vk_format = d3d_dxgi_format_to_vk((DXGI_FORMAT)info->format);
746 if (vk_format == 0) {
747 D3D_ERROR(c, "Invalid format!");
748 return XRT_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED;
749 }
750
751 struct xrt_swapchain_create_info xinfo = *info;
752 xinfo.format = vk_format;
753
754 return xrt_comp_get_swapchain_create_properties(&c->xcn->base, &xinfo, xsccp);
755}
756
757static void
758client_d3d11_compositor_destroy(struct xrt_compositor *xc)
759{
760 std::unique_ptr<struct client_d3d11_compositor> c{as_client_d3d11_compositor(xc)};
761}
762
763static void
764client_d3d11_compositor_init_try_timeline_semaphores(struct client_d3d11_compositor *c)
765{
766 struct xrt_compositor_semaphore *xcsem{nullptr};
767 HANDLE timeline_semaphore_handle_raw{};
768 xrt_result_t xret;
769
770 // Set the value to something non-zero.
771 c->timeline_semaphore_value = 1;
772
773 // See if we can make a "timeline semaphore", also known as ID3D11Fence
774 if (!c->xcn->base.create_semaphore || !c->xcn->base.layer_commit_with_semaphore) {
775 return;
776 }
777
778 /*
779 * This call returns a HANDLE in the out_handle argument, it is owned by
780 * the returned xrt_compositor_semaphore object we should not track it.
781 */
782 xret = xrt_comp_create_semaphore( //
783 &(c->xcn->base), // xc
784 &timeline_semaphore_handle_raw, // out_handle
785 &xcsem); // out_xcsem
786 if (xret != XRT_SUCCESS) {
787 D3D_WARN(c, "Native compositor tried but failed to created a timeline semaphore for us.");
788 return;
789 }
790 D3D_INFO(c, "Native compositor created a timeline semaphore for us.");
791
792 // Because importFence throws on failure we use this ref.
793 unique_compositor_semaphore_ref timeline_semaphore{xcsem};
794
795 // Try to import the fence.
796 wil::com_ptr<ID3D11Fence> fence = import_fence(*(c->fence_device), timeline_semaphore_handle_raw);
797
798 // And try to signal the fence to make sure it works.
799 HRESULT hr = c->fence_context->Signal(fence.get(), c->timeline_semaphore_value);
800 if (!SUCCEEDED(hr)) {
801 D3D_WARN(c,
802 "Your graphics driver does not support importing the native compositor's "
803 "semaphores into D3D11, falling back to local blocking.");
804 return;
805 }
806
807 D3D_INFO(c, "We imported a timeline semaphore and can signal it.");
808 // OK, keep these resources around.
809 c->fence = std::move(fence);
810 c->timeline_semaphore = std::move(timeline_semaphore);
811 // c->timeline_semaphore_handle = std::move(timeline_semaphore_handle);
812}
813
814static void
815client_d3d11_compositor_init_try_internal_blocking(struct client_d3d11_compositor *c)
816{
817 wil::com_ptr<ID3D11Fence> fence;
818 HRESULT hr = c->fence_device->CreateFence( //
819 0, // InitialValue
820 D3D11_FENCE_FLAG_NONE, // Flags
821 __uuidof(ID3D11Fence), // ReturnedInterface
822 fence.put_void()); // ppFence
823
824 if (!SUCCEEDED(hr)) {
825 char buf[kErrorBufSize];
826 formatMessage(hr, buf);
827 D3D_WARN(c, "Cannot even create an ID3D11Fence for internal use: %s", buf);
828 return;
829 }
830
831 hr = c->local_wait_event.create();
832 if (!SUCCEEDED(hr)) {
833 char buf[kErrorBufSize];
834 formatMessage(hr, buf);
835 D3D_ERROR(c, "Error creating event for synchronization usage: %s", buf);
836 return;
837 }
838
839 D3D_INFO(c, "We created our own ID3D11Fence and will wait on it ourselves.");
840 c->fence = std::move(fence);
841}
842
843struct xrt_compositor_d3d11 *
844client_d3d11_compositor_create(struct xrt_compositor_native *xcn, ID3D11Device *device)
845try {
846 std::unique_ptr<struct client_d3d11_compositor> c = std::make_unique<struct client_d3d11_compositor>();
847 c->log_level = debug_get_log_option_log();
848 c->xcn = xcn;
849
850 wil::com_ptr<ID3D11Device> app_dev{device};
851 if (!app_dev.try_query_to(c->app_device.put())) {
852 U_LOG_E("Could not get d3d11 device!");
853 return nullptr;
854 }
855 c->app_device->GetImmediateContext3(c->app_context.put());
856
857 wil::com_ptr<IDXGIAdapter> adapter;
858
859 THROW_IF_FAILED(app_dev.query<IDXGIDevice>()->GetAdapter(adapter.put()));
860
861 {
862 // Now, try to get an equivalent device of our own
863 wil::com_ptr<ID3D11Device> our_dev;
864 wil::com_ptr<ID3D11DeviceContext> our_context;
865 std::tie(our_dev, our_context) = xrt::auxiliary::d3d::d3d11::createDevice(adapter, c->log_level);
866 our_dev.query_to(c->comp_device.put());
867 our_context.query_to(c->comp_context.put());
868 }
869
870 // Upcast fence to context version 4 and reference fence device.
871 {
872 c->app_device.query_to(c->fence_device.put());
873 c->app_context.query_to(c->fence_context.put());
874 }
875
876 // See if we can make a "timeline semaphore", also known as ID3D11Fence
877 client_d3d11_compositor_init_try_timeline_semaphores(c.get());
878 if (!c->timeline_semaphore) {
879 // OK native compositor doesn't know how to handle timeline semaphores, or we can't import them, but we
880 // can still use them entirely internally.
881 client_d3d11_compositor_init_try_internal_blocking(c.get());
882 }
883 if (!c->fence) {
884 D3D_WARN(c, "No sync mechanism for D3D11 was successful!");
885 }
886 c->base.base.get_swapchain_create_properties = client_d3d11_compositor_get_swapchain_create_properties;
887 c->base.base.create_swapchain = client_d3d11_create_swapchain;
888 c->base.base.create_passthrough = client_d3d11_compositor_passthrough_create;
889 c->base.base.create_passthrough_layer = client_d3d11_compositor_passthrough_layer_create;
890 c->base.base.destroy_passthrough = client_d3d11_compositor_passthrough_destroy;
891 c->base.base.begin_session = client_d3d11_compositor_begin_session;
892 c->base.base.end_session = client_d3d11_compositor_end_session;
893 c->base.base.wait_frame = client_d3d11_compositor_wait_frame;
894 c->base.base.begin_frame = client_d3d11_compositor_begin_frame;
895 c->base.base.discard_frame = client_d3d11_compositor_discard_frame;
896 c->base.base.layer_begin = client_d3d11_compositor_layer_begin;
897 c->base.base.layer_projection = client_d3d11_compositor_layer_projection;
898 c->base.base.layer_projection_depth = client_d3d11_compositor_layer_projection_depth;
899 c->base.base.layer_quad = client_d3d11_compositor_layer_quad;
900 c->base.base.layer_cube = client_d3d11_compositor_layer_cube;
901 c->base.base.layer_cylinder = client_d3d11_compositor_layer_cylinder;
902 c->base.base.layer_equirect1 = client_d3d11_compositor_layer_equirect1;
903 c->base.base.layer_equirect2 = client_d3d11_compositor_layer_equirect2;
904 c->base.base.layer_passthrough = client_d3d11_compositor_layer_passthrough;
905 c->base.base.layer_commit = client_d3d11_compositor_layer_commit;
906 c->base.base.destroy = client_d3d11_compositor_destroy;
907
908
909 // Passthrough our formats from the native compositor to the client.
910 uint32_t count = 0;
911 for (uint32_t i = 0; i < xcn->base.info.format_count; i++) {
912 // Can we turn this format into DXGI?
913 DXGI_FORMAT f = d3d_vk_format_to_dxgi(xcn->base.info.formats[i]);
914 if (f == 0) {
915 continue;
916 }
917 // And back to Vulkan?
918 auto v = d3d_dxgi_format_to_vk(f);
919 if (v == 0) {
920 continue;
921 }
922 // Do we have a typeless version of it?
923 DXGI_FORMAT typeless = d3d_dxgi_format_to_typeless_dxgi(f);
924 if (typeless == f) {
925 continue;
926 }
927
928 c->base.base.info.formats[count++] = f;
929 }
930 c->base.base.info.format_count = count;
931
932 return &(c.release()->base);
933} catch (wil::ResultException const &e) {
934 U_LOG_E("Error creating D3D11 client compositor: %s", e.what());
935 return nullptr;
936} catch (std::exception const &e) {
937 U_LOG_E("Error creating D3D11 client compositor: %s", e.what());
938 return nullptr;
939} catch (...) {
940 U_LOG_E("Error creating D3D11 client compositor");
941 return nullptr;
942}