The open source OpenXR runtime
at prediction-2 942 lines 31 kB view raw
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}