The open source OpenXR runtime
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

c/client: D3D12 client compositor

+1061 -1
+1
doc/changes/compositor/mr.1340.md
··· 1 + Initial support for D3D12 client applications on Windows.
+7 -1
src/xrt/compositor/CMakeLists.txt
··· 71 71 comp_client PRIVATE client/comp_d3d11_client.cpp client/comp_d3d11_client.h 72 72 client/comp_d3d11_glue.c 73 73 ) 74 - target_link_libraries(comp_client PRIVATE aux_d3d) 74 + endif() 75 + 76 + if(XRT_HAVE_D3D12) 77 + target_sources( 78 + comp_client PRIVATE client/comp_d3d12_client.cpp client/comp_d3d12_client.h 79 + client/comp_d3d12_glue.c 80 + ) 75 81 endif() 76 82 77 83 ##
+990
src/xrt/compositor/client/comp_d3d12_client.cpp
··· 1 + // Copyright 2019-2022, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief D3D12 client side glue to compositor implementation. 6 + * @author Ryan Pavlik <ryan.pavlik@collabora.com> 7 + * @author Jakob Bornecrantz <jakob@collabora.com> 8 + * @ingroup comp_client 9 + */ 10 + 11 + #include "comp_d3d12_client.h" 12 + 13 + #include "comp_d3d_common.hpp" 14 + #include "xrt/xrt_compositor.h" 15 + #include "xrt/xrt_config_os.h" 16 + #include "xrt/xrt_handles.h" 17 + #include "xrt/xrt_deleters.hpp" 18 + #include "xrt/xrt_results.h" 19 + #include "xrt/xrt_vulkan_includes.h" 20 + #include "d3d/d3d_dxgi_formats.h" 21 + #include "d3d/d3d_d3d12_helpers.hpp" 22 + #include "d3d/d3d_d3d12_fence.hpp" 23 + #include "d3d/d3d_d3d12_bits.h" 24 + #include "d3d/d3d_d3d11_allocator.hpp" 25 + #include "d3d/d3d_d3d11_helpers.hpp" 26 + #include "d3d/d3d_dxgi_helpers.hpp" 27 + #include "util/u_misc.h" 28 + #include "util/u_pretty_print.h" 29 + #include "util/u_time.h" 30 + #include "util/u_logging.h" 31 + #include "util/u_debug.h" 32 + #include "util/u_handles.h" 33 + #include "util/u_win32_com_guard.hpp" 34 + 35 + #include <d3d12.h> 36 + #include <wil/resource.h> 37 + #include <wil/com.h> 38 + #include <wil/result_macros.h> 39 + 40 + #include <assert.h> 41 + #include <inttypes.h> 42 + #include <memory> 43 + #include <stdio.h> 44 + #include <stdlib.h> 45 + #include <string.h> 46 + #include <chrono> 47 + #include <array> 48 + 49 + using namespace std::chrono_literals; 50 + using namespace std::chrono; 51 + 52 + DEBUG_GET_ONCE_LOG_OPTION(log, "D3D_COMPOSITOR_LOG", U_LOGGING_INFO) 53 + DEBUG_GET_ONCE_BOOL_OPTION(allow_depth, "D3D_COMPOSITOR_ALLOW_DEPTH", false); 54 + 55 + DEBUG_GET_ONCE_BOOL_OPTION(barriers, "D3D12_COMPOSITOR_BARRIERS", false); 56 + DEBUG_GET_ONCE_BOOL_OPTION(initial_transition, "D3D12_COMPOSITOR_INITIAL_TRANSITION", true); 57 + 58 + /*! 59 + * Spew level logging. 60 + * 61 + * @relates client_d3d12_compositor 62 + */ 63 + #define D3D_SPEW(c, ...) U_LOG_IFL_T(c->log_level, __VA_ARGS__); 64 + 65 + /*! 66 + * Debug level logging. 67 + * 68 + * @relates client_d3d12_compositor 69 + */ 70 + #define D3D_DEBUG(c, ...) U_LOG_IFL_D(c->log_level, __VA_ARGS__); 71 + 72 + /*! 73 + * Info level logging. 74 + * 75 + * @relates client_d3d12_compositor 76 + */ 77 + #define D3D_INFO(c, ...) U_LOG_IFL_I(c->log_level, __VA_ARGS__); 78 + 79 + /*! 80 + * Warn level logging. 81 + * 82 + * @relates client_d3d12_compositor 83 + */ 84 + #define D3D_WARN(c, ...) U_LOG_IFL_W(c->log_level, __VA_ARGS__); 85 + 86 + /*! 87 + * Error level logging. 88 + * 89 + * @relates client_d3d12_compositor 90 + */ 91 + #define D3D_ERROR(c, ...) U_LOG_IFL_E(c->log_level, __VA_ARGS__); 92 + 93 + using unique_compositor_semaphore_ref = std::unique_ptr< 94 + struct xrt_compositor_semaphore, 95 + xrt::deleters::reference_deleter<struct xrt_compositor_semaphore, xrt_compositor_semaphore_reference>>; 96 + 97 + using unique_swapchain_ref = 98 + std::unique_ptr<struct xrt_swapchain, 99 + xrt::deleters::reference_deleter<struct xrt_swapchain, xrt_swapchain_reference>>; 100 + 101 + // 0 is special 102 + static constexpr uint64_t kKeyedMutexKey = 0; 103 + 104 + // Timeout to wait for completion 105 + static constexpr auto kFenceTimeout = 500ms; 106 + 107 + /*! 108 + * @class client_d3d12_compositor 109 + * 110 + * Wraps the real compositor providing a D3D12 based interface. 111 + * 112 + * @ingroup comp_client 113 + * @implements xrt_compositor_d3d12 114 + */ 115 + struct client_d3d12_compositor 116 + { 117 + struct xrt_compositor_d3d12 base = {}; 118 + 119 + //! Owning reference to the backing native compositor 120 + struct xrt_compositor_native *xcn{nullptr}; 121 + 122 + //! Just keeps COM alive while we keep references to COM things. 123 + xrt::auxiliary::util::ComGuard com_guard; 124 + 125 + //! Logging level. 126 + enum u_logging_level log_level; 127 + 128 + //! Device we got from the app 129 + wil::com_ptr<ID3D12Device> device; 130 + 131 + //! Command queue for @ref device 132 + wil::com_ptr<ID3D12CommandQueue> app_queue; 133 + 134 + //! Command list allocator for the compositor 135 + wil::com_ptr<ID3D12CommandAllocator> command_allocator; 136 + 137 + //! D3D11 device used for allocating images 138 + wil::com_ptr<ID3D11Device5> d3d11_device; 139 + 140 + //! D3D11 context used for allocating images 141 + wil::com_ptr<ID3D11DeviceContext4> d3d11_context; 142 + 143 + /*! 144 + * A timeline semaphore made by the native compositor and imported by us. 145 + * 146 + * When this is valid, we should use xrt_compositor::layer_commit_with_sync: 147 + * it means the native compositor knows about timeline semaphores, and we can import its semaphores, so we can 148 + * pass @ref timeline_semaphore instead of blocking locally. 149 + */ 150 + unique_compositor_semaphore_ref timeline_semaphore; 151 + 152 + /*! 153 + * A fence (timeline semaphore) object, owned by @ref fence_device. 154 + * 155 + * Signal using @ref fence_context if this is not null. 156 + * 157 + * Wait on it in `layer_commit` if @ref timeline_semaphore *is* null/invalid. 158 + */ 159 + wil::com_ptr<ID3D12Fence> fence; 160 + 161 + /*! 162 + * Event used for blocking in `layer_commit` if required (if @ref timeline_semaphore *is* null/invalid) 163 + */ 164 + wil::unique_event_nothrow local_wait_event; 165 + 166 + /*! 167 + * The value most recently signaled on the timeline semaphore 168 + */ 169 + uint64_t timeline_semaphore_value = 0; 170 + }; 171 + 172 + static_assert(std::is_standard_layout<client_d3d12_compositor>::value); 173 + 174 + struct client_d3d12_swapchain; 175 + 176 + static inline DWORD 177 + convertTimeoutToWindowsMilliseconds(uint64_t timeout_ns) 178 + { 179 + return (timeout_ns == XRT_INFINITE_DURATION) ? INFINITE : (DWORD)(timeout_ns / (uint64_t)U_TIME_1MS_IN_NS); 180 + } 181 + 182 + /*! 183 + * Split out from @ref client_d3d12_swapchain to ensure that it is standard 184 + * layout, std::vector for instance is not standard layout. 185 + */ 186 + struct client_d3d12_swapchain_data 187 + { 188 + explicit client_d3d12_swapchain_data(enum u_logging_level log_level) : keyed_mutex_collection(log_level) {} 189 + 190 + xrt::compositor::client::KeyedMutexCollection keyed_mutex_collection; 191 + 192 + //! The shared handles for all our images 193 + std::vector<wil::unique_handle> handles; 194 + 195 + //! D3D11 Images 196 + std::vector<wil::com_ptr<ID3D11Texture2D1>> d3d11_images; 197 + 198 + //! Images 199 + std::vector<wil::com_ptr<ID3D12Resource>> images; 200 + 201 + //! Command list per-image to put the resource in a state for acquire (@ref appResourceState) from @ref 202 + //! compositorResourceState 203 + std::vector<wil::com_ptr<ID3D12CommandList>> commandsToApp; 204 + 205 + //! Command list per-image to put the resource in a state for composition (@ref compositorResourceState) from 206 + //! @ref appResourceState 207 + std::vector<wil::com_ptr<ID3D12CommandList>> commandsToCompositor; 208 + 209 + //! State we hand over the image in, and expect it back in. 210 + D3D12_RESOURCE_STATES appResourceState = D3D12_RESOURCE_STATE_RENDER_TARGET; 211 + 212 + //! State the compositor wants the image in before use. 213 + D3D12_RESOURCE_STATES compositorResourceState = D3D12_RESOURCE_STATE_COMMON; 214 + 215 + std::vector<D3D12_RESOURCE_STATES> state; 216 + }; 217 + 218 + /*! 219 + * Wraps the real compositor swapchain providing a D3D12 based interface. 220 + * 221 + * @ingroup comp_client 222 + * @implements xrt_swapchain_d3d12 223 + */ 224 + struct client_d3d12_swapchain 225 + { 226 + struct xrt_swapchain_d3d12 base; 227 + 228 + //! Owning reference to the imported swapchain. 229 + unique_swapchain_ref xsc; 230 + 231 + //! Non-owning reference to our parent compositor. 232 + struct client_d3d12_compositor *c{nullptr}; 233 + 234 + //! implementation struct with things that aren't standard_layout 235 + std::unique_ptr<client_d3d12_swapchain_data> data; 236 + }; 237 + 238 + static_assert(std::is_standard_layout<client_d3d12_swapchain>::value); 239 + 240 + /*! 241 + * Down-cast helper. 242 + * @private @memberof client_d3d12_swapchain 243 + */ 244 + static inline struct client_d3d12_swapchain * 245 + as_client_d3d12_swapchain(struct xrt_swapchain *xsc) 246 + { 247 + return reinterpret_cast<client_d3d12_swapchain *>(xsc); 248 + } 249 + 250 + /*! 251 + * Down-cast helper. 252 + * @private @memberof client_d3d12_compositor 253 + */ 254 + static inline struct client_d3d12_compositor * 255 + as_client_d3d12_compositor(struct xrt_compositor *xc) 256 + { 257 + return (struct client_d3d12_compositor *)xc; 258 + } 259 + 260 + 261 + /* 262 + * 263 + * Logging helper. 264 + * 265 + */ 266 + static constexpr size_t kErrorBufSize = 256; 267 + 268 + template <size_t N> 269 + static inline bool 270 + formatMessage(DWORD err, char (&buf)[N]) 271 + { 272 + if (0 != FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 273 + LANG_SYSTEM_DEFAULT, buf, N - 1, NULL)) { 274 + return true; 275 + } 276 + memset(buf, 0, N); 277 + return false; 278 + } 279 + 280 + 281 + /* 282 + * 283 + * Helpers for Swapchain 284 + * 285 + */ 286 + 287 + static xrt_result_t 288 + client_d3d12_swapchain_barrier_to_app(client_d3d12_swapchain *sc, uint32_t index) 289 + { 290 + auto *data = sc->data.get(); 291 + if (data->commandsToApp.empty()) { 292 + // We have decided not to use barriers here 293 + return XRT_SUCCESS; 294 + } 295 + if (data->state[index] == data->appResourceState) { 296 + D3D_INFO(sc->c, "Image %" PRId32 " is already in the right state", index); 297 + return XRT_SUCCESS; 298 + } 299 + if (data->state[index] == data->compositorResourceState) { 300 + D3D_INFO(sc->c, "Acquiring image %" PRId32, index); 301 + std::array<ID3D12CommandList *, 1> commandLists{{data->commandsToApp[index].get()}}; 302 + sc->c->app_queue->ExecuteCommandLists(1, commandLists.data()); 303 + data->state[index] = data->appResourceState; 304 + return XRT_SUCCESS; 305 + } 306 + D3D_WARN(sc->c, "Image %" PRId32 " is in an unknown state", index); 307 + return XRT_ERROR_D3D12; 308 + } 309 + 310 + static xrt_result_t 311 + client_d3d12_swapchain_barrier_to_compositor(client_d3d12_swapchain *sc, uint32_t index) 312 + { 313 + auto *data = sc->data.get(); 314 + 315 + if (data->commandsToCompositor.empty()) { 316 + // We have decided not to use barriers here 317 + return XRT_SUCCESS; 318 + } 319 + 320 + std::array<ID3D12CommandList *, 1> commandLists{{data->commandsToCompositor[index].get()}}; 321 + sc->c->app_queue->ExecuteCommandLists(1, commandLists.data()); 322 + data->state[index] = data->compositorResourceState; 323 + return XRT_SUCCESS; 324 + } 325 + 326 + /* 327 + * 328 + * Swapchain functions. 329 + * 330 + */ 331 + 332 + static xrt_result_t 333 + client_d3d12_swapchain_acquire_image(struct xrt_swapchain *xsc, uint32_t *out_index) 334 + { 335 + struct client_d3d12_swapchain *sc = as_client_d3d12_swapchain(xsc); 336 + 337 + uint32_t index = 0; 338 + // Pipe down call into imported swapchain in native compositor. 339 + xrt_result_t xret = xrt_swapchain_acquire_image(sc->xsc.get(), &index); 340 + 341 + if (xret == XRT_SUCCESS) { 342 + // Set output variable 343 + *out_index = index; 344 + } 345 + return xret; 346 + } 347 + 348 + static xrt_result_t 349 + client_d3d12_swapchain_wait_image(struct xrt_swapchain *xsc, uint64_t timeout_ns, uint32_t index) 350 + { 351 + struct client_d3d12_swapchain *sc = as_client_d3d12_swapchain(xsc); 352 + 353 + // Pipe down call into imported swapchain in native compositor. 354 + xrt_result_t xret = xrt_swapchain_wait_image(sc->xsc.get(), timeout_ns, index); 355 + 356 + if (xret == XRT_SUCCESS) { 357 + // OK, we got the image in the native compositor, now need the keyed mutex in d3d11. 358 + xret = sc->data->keyed_mutex_collection.waitKeyedMutex(index, timeout_ns); 359 + } 360 + if (xret == XRT_SUCCESS) { 361 + // OK, we got the image in the native compositor, now need the transition in d3d12. 362 + xret = client_d3d12_swapchain_barrier_to_app(sc, index); 363 + } 364 + 365 + //! @todo discard old contents? 366 + return xret; 367 + } 368 + 369 + static xrt_result_t 370 + client_d3d12_swapchain_release_image(struct xrt_swapchain *xsc, uint32_t index) 371 + { 372 + struct client_d3d12_swapchain *sc = as_client_d3d12_swapchain(xsc); 373 + 374 + // Pipe down call into imported swapchain in native compositor. 375 + xrt_result_t xret = xrt_swapchain_release_image(sc->xsc.get(), index); 376 + 377 + if (xret == XRT_SUCCESS) { 378 + // Release the keyed mutex 379 + xret = sc->data->keyed_mutex_collection.releaseKeyedMutex(index); 380 + } 381 + 382 + if (xret == XRT_SUCCESS) { 383 + xret = client_d3d12_swapchain_barrier_to_compositor(sc, index); 384 + } 385 + return xret; 386 + } 387 + 388 + static void 389 + client_d3d12_swapchain_destroy(struct xrt_swapchain *xsc) 390 + { 391 + // letting destruction do it all 392 + std::unique_ptr<client_d3d12_swapchain> sc(as_client_d3d12_swapchain(xsc)); 393 + } 394 + 395 + 396 + xrt_result_t 397 + client_d3d12_create_swapchain(struct xrt_compositor *xc, 398 + const struct xrt_swapchain_create_info *info, 399 + struct xrt_swapchain **out_xsc) 400 + try { 401 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 402 + xrt_result_t xret = XRT_SUCCESS; 403 + xrt_swapchain_create_properties xsccp{}; 404 + xret = xrt_comp_get_swapchain_create_properties(xc, info, &xsccp); 405 + 406 + if (xret != XRT_SUCCESS) { 407 + D3D_ERROR(c, "Could not get properties for creating swapchain"); 408 + return xret; 409 + } 410 + uint32_t image_count = xsccp.image_count; 411 + 412 + 413 + if ((info->create & XRT_SWAPCHAIN_CREATE_PROTECTED_CONTENT) != 0) { 414 + D3D_WARN(c, 415 + "Swapchain info is valid but this compositor doesn't support creating protected content " 416 + "swapchains!"); 417 + return XRT_ERROR_SWAPCHAIN_FLAG_VALID_BUT_UNSUPPORTED; 418 + } 419 + 420 + int64_t vk_format = d3d_dxgi_format_to_vk((DXGI_FORMAT)info->format); 421 + if (vk_format == 0) { 422 + D3D_ERROR(c, "Invalid format!"); 423 + return XRT_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED; 424 + } 425 + 426 + struct xrt_swapchain_create_info xinfo = *info; 427 + struct xrt_swapchain_create_info vkinfo = *info; 428 + vkinfo.format = vk_format; 429 + 430 + std::unique_ptr<struct client_d3d12_swapchain> sc = std::make_unique<struct client_d3d12_swapchain>(); 431 + sc->data = std::make_unique<client_d3d12_swapchain_data>(c->log_level); 432 + auto &data = sc->data; 433 + 434 + // Make images with D3D11 435 + xret = xrt::auxiliary::d3d::d3d11::allocateSharedImages(*(c->d3d11_device), xinfo, image_count, true, 436 + data->d3d11_images, data->handles); 437 + if (xret != XRT_SUCCESS) { 438 + return xret; 439 + } 440 + 441 + data->images.reserve(image_count); 442 + 443 + // Import to D3D12 from the handle. 444 + for (uint32_t i = 0; i < image_count; ++i) { 445 + const auto &handle = data->handles[i]; 446 + wil::unique_handle dupedForD3D12{u_graphics_buffer_ref(handle.get())}; 447 + auto d3d12Image = xrt::auxiliary::d3d::d3d12::importImage(*(c->device), dupedForD3D12.get()); 448 + // Put the image where the OpenXR state tracker can get it 449 + sc->base.images[i] = d3d12Image.get(); 450 + 451 + // Store the owning pointer for lifetime management 452 + data->images.emplace_back(std::move(d3d12Image)); 453 + } 454 + 455 + D3D12_RESOURCE_STATES appResourceState = d3d_convert_usage_bits_to_d3d12_app_resource_state(xinfo.bits); 456 + 457 + // Transition all images from _COMMON to the correct state 458 + if (debug_get_bool_option_initial_transition()) { 459 + D3D_INFO(c, "Executing initial barriers"); 460 + D3D12_RESOURCE_BARRIER barrier{}; 461 + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 462 + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON; // state at creation in d3d11 463 + barrier.Transition.StateAfter = appResourceState; 464 + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; 465 + 466 + data->state.resize(image_count, barrier.Transition.StateAfter); 467 + 468 + std::vector<D3D12_RESOURCE_BARRIER> barriers; 469 + for (const auto &image : data->images) { 470 + barrier.Transition.pResource = image.get(); 471 + barriers.emplace_back(barrier); 472 + } 473 + wil::com_ptr<ID3D12GraphicsCommandList> commandList; 474 + THROW_IF_FAILED(c->device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, 475 + c->command_allocator.get(), nullptr, 476 + IID_PPV_ARGS(commandList.put()))); 477 + commandList->ResourceBarrier((UINT)barriers.size(), barriers.data()); 478 + commandList->Close(); 479 + std::array<ID3D12CommandList *, 1> commandLists{commandList.get()}; 480 + 481 + c->app_queue->ExecuteCommandLists((UINT)commandLists.size(), commandLists.data()); 482 + } 483 + 484 + /// @todo No idea if this is right, might depend on whether it's the compute or graphics compositor! 485 + D3D12_RESOURCE_STATES compositorResourceState = D3D12_RESOURCE_STATE_COMMON; 486 + 487 + data->appResourceState = appResourceState; 488 + data->compositorResourceState = compositorResourceState; 489 + 490 + data->state.resize(image_count, appResourceState); 491 + 492 + if (debug_get_bool_option_barriers()) { 493 + D3D_INFO(c, "Will use barriers at runtime"); 494 + data->commandsToApp.reserve(image_count); 495 + data->commandsToCompositor.reserve(image_count); 496 + 497 + // Make the command lists to transition images 498 + for (uint32_t i = 0; i < image_count; ++i) { 499 + wil::com_ptr<ID3D12CommandList> commandsToApp; 500 + wil::com_ptr<ID3D12CommandList> commandsToCompositor; 501 + 502 + D3D_INFO(c, "Creating command lists for image %" PRId32, i); 503 + HRESULT hr = xrt::auxiliary::d3d::d3d12::createCommandLists( 504 + *(c->device), *(c->command_allocator), *(data->images[i]), xinfo.bits, commandsToApp, 505 + commandsToCompositor); 506 + if (!SUCCEEDED(hr)) { 507 + char buf[kErrorBufSize]; 508 + formatMessage(hr, buf); 509 + D3D_ERROR(c, "Error creating command list: %s", buf); 510 + return XRT_ERROR_D3D12; 511 + } 512 + 513 + data->commandsToApp.emplace_back(std::move(commandsToApp)); 514 + data->commandsToCompositor.emplace_back(std::move(commandsToCompositor)); 515 + } 516 + } 517 + 518 + // Cache the keyed mutex interface 519 + xret = data->keyed_mutex_collection.init(data->d3d11_images); 520 + if (xret != XRT_SUCCESS) { 521 + D3D_ERROR(c, "Error retrieving keyex mutex interfaces"); 522 + return xret; 523 + } 524 + 525 + // Import into the native compositor, to create the corresponding swapchain which we wrap. 526 + xret = xrt::compositor::client::importFromHandleDuplicates( 527 + *(c->xcn), data->handles, vkinfo, false /** @todo not sure - dedicated allocation */, sc->xsc); 528 + if (xret != XRT_SUCCESS) { 529 + D3D_ERROR(c, "Error importing D3D swapchain into native compositor"); 530 + return xret; 531 + } 532 + 533 + sc->base.base.destroy = client_d3d12_swapchain_destroy; 534 + sc->base.base.acquire_image = client_d3d12_swapchain_acquire_image; 535 + sc->base.base.wait_image = client_d3d12_swapchain_wait_image; 536 + sc->base.base.release_image = client_d3d12_swapchain_release_image; 537 + sc->c = c; 538 + sc->base.base.image_count = image_count; 539 + 540 + xrt_swapchain_reference(out_xsc, &sc->base.base); 541 + (void)sc.release(); 542 + return XRT_SUCCESS; 543 + } catch (wil::ResultException const &e) { 544 + U_LOG_E("Error creating D3D12 swapchain: %s", e.what()); 545 + return XRT_ERROR_ALLOCATION; 546 + } catch (std::exception const &e) { 547 + U_LOG_E("Error creating D3D12 swapchain: %s", e.what()); 548 + return XRT_ERROR_ALLOCATION; 549 + } catch (...) { 550 + U_LOG_E("Error creating D3D12 swapchain"); 551 + return XRT_ERROR_ALLOCATION; 552 + } 553 + 554 + /* 555 + * 556 + * Compositor functions. 557 + * 558 + */ 559 + 560 + 561 + static xrt_result_t 562 + client_d3d12_compositor_begin_session(struct xrt_compositor *xc, enum xrt_view_type type) 563 + { 564 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 565 + 566 + // Pipe down call into native compositor. 567 + return xrt_comp_begin_session(&c->xcn->base, type); 568 + } 569 + 570 + static xrt_result_t 571 + client_d3d12_compositor_end_session(struct xrt_compositor *xc) 572 + { 573 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 574 + 575 + // Pipe down call into native compositor. 576 + return xrt_comp_end_session(&c->xcn->base); 577 + } 578 + 579 + static xrt_result_t 580 + client_d3d12_compositor_wait_frame(struct xrt_compositor *xc, 581 + int64_t *out_frame_id, 582 + uint64_t *predicted_display_time, 583 + uint64_t *predicted_display_period) 584 + { 585 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 586 + 587 + // Pipe down call into native compositor. 588 + return xrt_comp_wait_frame(&c->xcn->base, out_frame_id, predicted_display_time, predicted_display_period); 589 + } 590 + 591 + static xrt_result_t 592 + client_d3d12_compositor_begin_frame(struct xrt_compositor *xc, int64_t frame_id) 593 + { 594 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 595 + 596 + // Pipe down call into native compositor. 597 + return xrt_comp_begin_frame(&c->xcn->base, frame_id); 598 + } 599 + 600 + static xrt_result_t 601 + client_d3d12_compositor_discard_frame(struct xrt_compositor *xc, int64_t frame_id) 602 + { 603 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 604 + 605 + // Pipe down call into native compositor. 606 + return xrt_comp_discard_frame(&c->xcn->base, frame_id); 607 + } 608 + 609 + static xrt_result_t 610 + client_d3d12_compositor_layer_begin(struct xrt_compositor *xc, 611 + int64_t frame_id, 612 + uint64_t display_time_ns, 613 + enum xrt_blend_mode env_blend_mode) 614 + { 615 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 616 + 617 + // Pipe down call into native compositor. 618 + return xrt_comp_layer_begin(&c->xcn->base, frame_id, display_time_ns, env_blend_mode); 619 + } 620 + 621 + static xrt_result_t 622 + client_d3d12_compositor_layer_stereo_projection(struct xrt_compositor *xc, 623 + struct xrt_device *xdev, 624 + struct xrt_swapchain *l_xsc, 625 + struct xrt_swapchain *r_xsc, 626 + const struct xrt_layer_data *data) 627 + { 628 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 629 + 630 + assert(data->type == XRT_LAYER_STEREO_PROJECTION); 631 + 632 + struct xrt_swapchain *l_xscn = as_client_d3d12_swapchain(l_xsc)->xsc.get(); 633 + struct xrt_swapchain *r_xscn = as_client_d3d12_swapchain(r_xsc)->xsc.get(); 634 + 635 + // No flip required: D3D12 swapchain image convention matches Vulkan. 636 + return xrt_comp_layer_stereo_projection(&c->xcn->base, xdev, l_xscn, r_xscn, data); 637 + } 638 + 639 + static xrt_result_t 640 + client_d3d12_compositor_layer_stereo_projection_depth(struct xrt_compositor *xc, 641 + struct xrt_device *xdev, 642 + struct xrt_swapchain *l_xsc, 643 + struct xrt_swapchain *r_xsc, 644 + struct xrt_swapchain *l_d_xsc, 645 + struct xrt_swapchain *r_d_xsc, 646 + const struct xrt_layer_data *data) 647 + { 648 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 649 + 650 + assert(data->type == XRT_LAYER_STEREO_PROJECTION_DEPTH); 651 + 652 + struct xrt_swapchain *l_xscn = as_client_d3d12_swapchain(l_xsc)->xsc.get(); 653 + struct xrt_swapchain *r_xscn = as_client_d3d12_swapchain(r_xsc)->xsc.get(); 654 + struct xrt_swapchain *l_d_xscn = as_client_d3d12_swapchain(l_d_xsc)->xsc.get(); 655 + struct xrt_swapchain *r_d_xscn = as_client_d3d12_swapchain(r_d_xsc)->xsc.get(); 656 + 657 + // No flip required: D3D12 swapchain image convention matches Vulkan. 658 + return xrt_comp_layer_stereo_projection_depth(&c->xcn->base, xdev, l_xscn, r_xscn, l_d_xscn, r_d_xscn, data); 659 + } 660 + 661 + static xrt_result_t 662 + client_d3d12_compositor_layer_quad(struct xrt_compositor *xc, 663 + struct xrt_device *xdev, 664 + struct xrt_swapchain *xsc, 665 + const struct xrt_layer_data *data) 666 + { 667 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 668 + 669 + assert(data->type == XRT_LAYER_QUAD); 670 + 671 + struct xrt_swapchain *xscfb = as_client_d3d12_swapchain(xsc)->xsc.get(); 672 + 673 + // No flip required: D3D12 swapchain image convention matches Vulkan. 674 + return xrt_comp_layer_quad(&c->xcn->base, xdev, xscfb, data); 675 + } 676 + 677 + static xrt_result_t 678 + client_d3d12_compositor_layer_cube(struct xrt_compositor *xc, 679 + struct xrt_device *xdev, 680 + struct xrt_swapchain *xsc, 681 + const struct xrt_layer_data *data) 682 + { 683 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 684 + 685 + assert(data->type == XRT_LAYER_CUBE); 686 + 687 + struct xrt_swapchain *xscfb = as_client_d3d12_swapchain(xsc)->xsc.get(); 688 + 689 + // No flip required: D3D12 swapchain image convention matches Vulkan. 690 + return xrt_comp_layer_cube(&c->xcn->base, xdev, xscfb, data); 691 + } 692 + 693 + static xrt_result_t 694 + client_d3d12_compositor_layer_cylinder(struct xrt_compositor *xc, 695 + struct xrt_device *xdev, 696 + struct xrt_swapchain *xsc, 697 + const struct xrt_layer_data *data) 698 + { 699 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 700 + 701 + assert(data->type == XRT_LAYER_CYLINDER); 702 + 703 + struct xrt_swapchain *xscfb = as_client_d3d12_swapchain(xsc)->xsc.get(); 704 + 705 + // No flip required: D3D12 swapchain image convention matches Vulkan. 706 + return xrt_comp_layer_cylinder(&c->xcn->base, xdev, xscfb, data); 707 + } 708 + 709 + static xrt_result_t 710 + client_d3d12_compositor_layer_equirect1(struct xrt_compositor *xc, 711 + struct xrt_device *xdev, 712 + struct xrt_swapchain *xsc, 713 + const struct xrt_layer_data *data) 714 + { 715 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 716 + 717 + assert(data->type == XRT_LAYER_EQUIRECT1); 718 + 719 + struct xrt_swapchain *xscfb = as_client_d3d12_swapchain(xsc)->xsc.get(); 720 + 721 + // No flip required: D3D12 swapchain image convention matches Vulkan. 722 + return xrt_comp_layer_equirect1(&c->xcn->base, xdev, xscfb, data); 723 + } 724 + 725 + static xrt_result_t 726 + client_d3d12_compositor_layer_equirect2(struct xrt_compositor *xc, 727 + struct xrt_device *xdev, 728 + struct xrt_swapchain *xsc, 729 + const struct xrt_layer_data *data) 730 + { 731 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 732 + 733 + assert(data->type == XRT_LAYER_EQUIRECT2); 734 + 735 + struct xrt_swapchain *xscfb = as_client_d3d12_swapchain(xsc)->xsc.get(); 736 + 737 + // No flip required: D3D12 swapchain image convention matches Vulkan. 738 + return xrt_comp_layer_equirect2(&c->xcn->base, xdev, xscfb, data); 739 + } 740 + 741 + static xrt_result_t 742 + client_d3d12_compositor_layer_commit(struct xrt_compositor *xc, 743 + int64_t frame_id, 744 + xrt_graphics_sync_handle_t sync_handle) 745 + { 746 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 747 + 748 + // We make the sync object, not st/oxr which is our user. 749 + assert(!xrt_graphics_sync_handle_is_valid(sync_handle)); 750 + 751 + xrt_result_t xret = XRT_SUCCESS; 752 + if (c->fence) { 753 + c->timeline_semaphore_value++; 754 + HRESULT hr = c->fence->Signal(c->timeline_semaphore_value); 755 + if (!SUCCEEDED(hr)) { 756 + char buf[kErrorBufSize]; 757 + formatMessage(hr, buf); 758 + D3D_ERROR(c, "Error signaling fence: %s", buf); 759 + return xrt_comp_layer_commit(&c->xcn->base, frame_id, XRT_GRAPHICS_SYNC_HANDLE_INVALID); 760 + } 761 + } 762 + if (c->timeline_semaphore) { 763 + // We got this from the native compositor, so we can pass it back 764 + return xrt_comp_layer_commit_with_semaphore(&c->xcn->base, frame_id, c->timeline_semaphore.get(), 765 + c->timeline_semaphore_value); 766 + } 767 + 768 + if (c->fence) { 769 + // Wait on it ourselves, if we have it and didn't tell the native compositor to wait on it. 770 + xret = xrt::auxiliary::d3d::d3d12::waitOnFenceWithTimeout(c->fence, c->local_wait_event, 771 + c->timeline_semaphore_value, kFenceTimeout); 772 + if (xret != XRT_SUCCESS) { 773 + struct u_pp_sink_stack_only sink; // Not inited, very large. 774 + u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink); 775 + u_pp(dg, "Problem waiting on fence: "); 776 + u_pp_xrt_result(dg, xret); 777 + D3D_ERROR(c, "%s", sink.buffer); 778 + 779 + return xret; 780 + } 781 + } 782 + 783 + return xrt_comp_layer_commit(&c->xcn->base, frame_id, XRT_GRAPHICS_SYNC_HANDLE_INVALID); 784 + } 785 + 786 + 787 + static xrt_result_t 788 + client_d3d12_compositor_get_swapchain_create_properties(struct xrt_compositor *xc, 789 + const struct xrt_swapchain_create_info *info, 790 + struct xrt_swapchain_create_properties *xsccp) 791 + { 792 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 793 + 794 + return xrt_comp_get_swapchain_create_properties(&c->xcn->base, info, xsccp); 795 + } 796 + 797 + static xrt_result_t 798 + client_d3d12_compositor_poll_events(struct xrt_compositor *xc, union xrt_compositor_event *out_xce) 799 + { 800 + struct client_d3d12_compositor *c = as_client_d3d12_compositor(xc); 801 + 802 + // Pipe down call into native compositor. 803 + return xrt_comp_poll_events(&c->xcn->base, out_xce); 804 + } 805 + 806 + static void 807 + client_d3d12_compositor_destroy(struct xrt_compositor *xc) 808 + { 809 + std::unique_ptr<struct client_d3d12_compositor> c{as_client_d3d12_compositor(xc)}; 810 + } 811 + 812 + static void 813 + client_d3d12_compositor_init_try_timeline_semaphores(struct client_d3d12_compositor *c) 814 + { 815 + c->timeline_semaphore_value = 1; 816 + // See if we can make a "timeline semaphore", also known as ID3D12Fence 817 + if (!c->xcn->base.create_semaphore || !c->xcn->base.layer_commit_with_semaphore) { 818 + return; 819 + } 820 + struct xrt_compositor_semaphore *xcsem = nullptr; 821 + wil::unique_handle timeline_semaphore_handle; 822 + if (XRT_SUCCESS != xrt_comp_create_semaphore(&(c->xcn->base), timeline_semaphore_handle.put(), &xcsem)) { 823 + D3D_WARN(c, "Native compositor tried but failed to created a timeline semaphore for us."); 824 + return; 825 + } 826 + D3D_INFO(c, "Native compositor created a timeline semaphore for us."); 827 + 828 + unique_compositor_semaphore_ref timeline_semaphore{xcsem}; 829 + 830 + // try to import and signal 831 + wil::com_ptr<ID3D12Fence1> fence = 832 + xrt::auxiliary::d3d::d3d12::importFence(*(c->device), timeline_semaphore_handle.get()); 833 + D3D12_FENCE_FLAGS flags = fence->GetCreationFlags(); 834 + if (flags & D3D12_FENCE_FLAG_NON_MONITORED) { 835 + D3D_WARN(c, 836 + "Your graphics driver creates the native compositor's semaphores as 'non-monitored' making " 837 + "them unusable in D3D12, falling back to local blocking."); 838 + return; 839 + } 840 + HRESULT hr = fence->Signal(c->timeline_semaphore_value); 841 + if (!SUCCEEDED(hr)) { 842 + D3D_WARN(c, 843 + "Your graphics driver does not support importing the native compositor's " 844 + "semaphores into D3D12, falling back to local blocking."); 845 + return; 846 + } 847 + 848 + D3D_INFO(c, "We imported a timeline semaphore and can signal it."); 849 + // OK, keep these resources around. 850 + c->fence = std::move(fence); 851 + c->timeline_semaphore = std::move(timeline_semaphore); 852 + } 853 + 854 + static void 855 + client_d3d12_compositor_init_try_internal_blocking(struct client_d3d12_compositor *c) 856 + { 857 + wil::com_ptr<ID3D12Fence> fence; 858 + HRESULT hr = c->device->CreateFence( // 859 + 0, // InitialValue 860 + D3D12_FENCE_FLAG_NONE, // Flags 861 + __uuidof(ID3D12Fence), // ReturnedInterface 862 + fence.put_void()); // ppFence 863 + 864 + if (!SUCCEEDED(hr)) { 865 + char buf[kErrorBufSize]; 866 + formatMessage(hr, buf); 867 + D3D_WARN(c, "Cannot even create an ID3D12Fence for internal use: %s", buf); 868 + return; 869 + } 870 + 871 + hr = c->local_wait_event.create(); 872 + if (!SUCCEEDED(hr)) { 873 + char buf[kErrorBufSize]; 874 + formatMessage(hr, buf); 875 + D3D_ERROR(c, "Error creating event for synchronization usage: %s", buf); 876 + return; 877 + } 878 + 879 + D3D_INFO(c, "We created our own ID3D12Fence and will wait on it ourselves."); 880 + c->fence = std::move(fence); 881 + } 882 + 883 + struct xrt_compositor_d3d12 * 884 + client_d3d12_compositor_create(struct xrt_compositor_native *xcn, ID3D12Device *device, ID3D12CommandQueue *queue) 885 + try { 886 + std::unique_ptr<struct client_d3d12_compositor> c = std::make_unique<struct client_d3d12_compositor>(); 887 + c->log_level = debug_get_log_option_log(); 888 + c->xcn = xcn; 889 + 890 + c->device = device; 891 + c->app_queue = queue; 892 + 893 + HRESULT hr = 894 + c->device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(c->command_allocator.put())); 895 + if (!SUCCEEDED(hr)) { 896 + char buf[kErrorBufSize]; 897 + formatMessage(hr, buf); 898 + D3D_ERROR(c, "Error creating command allocator: %s", buf); 899 + return nullptr; 900 + } 901 + 902 + 903 + // Get D3D11 device/context for the same underlying adapter 904 + { 905 + LUID adapterLuid = device->GetAdapterLuid(); 906 + auto adapter = xrt::auxiliary::d3d::getAdapterByLUID( 907 + *reinterpret_cast<const xrt_luid_t *>(&adapterLuid), c->log_level); 908 + if (!adapter) { 909 + D3D_ERROR(c, "Error getting DXGI adapter"); 910 + return nullptr; 911 + } 912 + 913 + // Now, try to get an equivalent device of our own 914 + wil::com_ptr<ID3D11Device> our_dev; 915 + wil::com_ptr<ID3D11DeviceContext> our_context; 916 + std::tie(our_dev, our_context) = xrt::auxiliary::d3d::d3d11::createDevice(adapter, c->log_level); 917 + our_dev.query_to(c->d3d11_device.put()); 918 + our_context.query_to(c->d3d11_context.put()); 919 + } 920 + 921 + 922 + // See if we can make a "timeline semaphore", also known as ID3D12Fence 923 + client_d3d12_compositor_init_try_timeline_semaphores(c.get()); 924 + if (!c->timeline_semaphore) { 925 + // OK native compositor doesn't know how to handle timeline semaphores, or we can't import them, but we 926 + // can still use them entirely internally. 927 + client_d3d12_compositor_init_try_internal_blocking(c.get()); 928 + } 929 + if (!c->fence) { 930 + D3D_WARN(c, "No sync mechanism for D3D12 was successful!"); 931 + } 932 + c->base.base.get_swapchain_create_properties = client_d3d12_compositor_get_swapchain_create_properties; 933 + c->base.base.create_swapchain = client_d3d12_create_swapchain; 934 + c->base.base.begin_session = client_d3d12_compositor_begin_session; 935 + c->base.base.end_session = client_d3d12_compositor_end_session; 936 + c->base.base.wait_frame = client_d3d12_compositor_wait_frame; 937 + c->base.base.begin_frame = client_d3d12_compositor_begin_frame; 938 + c->base.base.discard_frame = client_d3d12_compositor_discard_frame; 939 + c->base.base.layer_begin = client_d3d12_compositor_layer_begin; 940 + c->base.base.layer_stereo_projection = client_d3d12_compositor_layer_stereo_projection; 941 + c->base.base.layer_stereo_projection_depth = client_d3d12_compositor_layer_stereo_projection_depth; 942 + c->base.base.layer_quad = client_d3d12_compositor_layer_quad; 943 + c->base.base.layer_cube = client_d3d12_compositor_layer_cube; 944 + c->base.base.layer_cylinder = client_d3d12_compositor_layer_cylinder; 945 + c->base.base.layer_equirect1 = client_d3d12_compositor_layer_equirect1; 946 + c->base.base.layer_equirect2 = client_d3d12_compositor_layer_equirect2; 947 + c->base.base.layer_commit = client_d3d12_compositor_layer_commit; 948 + c->base.base.destroy = client_d3d12_compositor_destroy; 949 + c->base.base.poll_events = client_d3d12_compositor_poll_events; 950 + 951 + 952 + // Passthrough our formats from the native compositor to the client. 953 + uint32_t count = 0; 954 + for (uint32_t i = 0; i < xcn->base.info.format_count; i++) { 955 + // Can we turn this format into DXGI? 956 + DXGI_FORMAT f = d3d_vk_format_to_dxgi(xcn->base.info.formats[i]); 957 + if (f == 0) { 958 + continue; 959 + } 960 + // And back to Vulkan? 961 + auto v = d3d_dxgi_format_to_vk(f); 962 + if (v == 0) { 963 + continue; 964 + } 965 + // Do we have a typeless version of it? 966 + DXGI_FORMAT typeless = d3d_dxgi_format_to_typeless_dxgi(f); 967 + if (typeless == f) { 968 + continue; 969 + } 970 + // Sometimes we have to forbid depth formats to avoid errors in Vulkan. 971 + if (!debug_get_bool_option_allow_depth() && 972 + (f == DXGI_FORMAT_D32_FLOAT || f == DXGI_FORMAT_D16_UNORM || f == DXGI_FORMAT_D24_UNORM_S8_UINT)) { 973 + continue; 974 + } 975 + 976 + c->base.base.info.formats[count++] = f; 977 + } 978 + c->base.base.info.format_count = count; 979 + 980 + return &(c.release()->base); 981 + } catch (wil::ResultException const &e) { 982 + U_LOG_E("Error creating D3D12 client compositor: %s", e.what()); 983 + return nullptr; 984 + } catch (std::exception const &e) { 985 + U_LOG_E("Error creating D3D12 client compositor: %s", e.what()); 986 + return nullptr; 987 + } catch (...) { 988 + U_LOG_E("Error creating D3D12 client compositor"); 989 + return nullptr; 990 + }
+44
src/xrt/compositor/client/comp_d3d12_client.h
··· 1 + // Copyright 2021-2022, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Interface for D3D12 client-side code. 6 + * @author Ryan Pavlik <ryan.pavlik@collabora.com> 7 + * @ingroup comp_client 8 + */ 9 + 10 + #pragma once 11 + 12 + #include "xrt/xrt_compositor.h" 13 + #include "xrt/xrt_gfx_d3d12.h" 14 + 15 + #include <d3d12.h> 16 + 17 + #ifdef __cplusplus 18 + extern "C" { 19 + #endif 20 + 21 + 22 + /* 23 + * 24 + * Structs 25 + * 26 + */ 27 + 28 + struct client_d3d12_compositor; 29 + 30 + /*! 31 + * Create a new client_d3d12_compositor. 32 + * 33 + * Takes ownership of provided xcn. 34 + * 35 + * @public @memberof client_d3d12_compositor 36 + * @see xrt_compositor_native 37 + */ 38 + struct xrt_compositor_d3d12 * 39 + client_d3d12_compositor_create(struct xrt_compositor_native *xcn, ID3D12Device *device, ID3D12CommandQueue *queue); 40 + 41 + 42 + #ifdef __cplusplus 43 + } 44 + #endif
+19
src/xrt/compositor/client/comp_d3d12_glue.c
··· 1 + // Copyright 2021-2022, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Glue code to D3D12 client side code: expressing requirements and connecting `comp_` APIs to `xrt_gfx_` 6 + * interfaces. 7 + * @author Ryan Pavlik <ryan.pavlik@collabora.com> 8 + * @ingroup comp_client 9 + */ 10 + 11 + #include "client/comp_d3d12_client.h" 12 + 13 + #include <stdlib.h> 14 + 15 + struct xrt_compositor_d3d12 * 16 + xrt_gfx_d3d12_provider_create(struct xrt_compositor_native *xcn, ID3D12Device *device, ID3D12CommandQueue *queue) 17 + { 18 + return client_d3d12_compositor_create(xcn, device, queue); 19 + }