Blender V5.0
oneapi/graphics_interop.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Intel Corporation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#if defined(WITH_ONEAPI) && defined(SYCL_LINEAR_MEMORY_INTEROP_AVAILABLE)
6
8
11# include "device/oneapi/queue.h"
12
14
15# ifdef _WIN32
16# include "util/windows.h"
17# else
18# include <unistd.h>
19# endif
20
22
23OneapiDeviceGraphicsInterop::OneapiDeviceGraphicsInterop(OneapiDeviceQueue *queue)
24 : queue_(queue), device_(static_cast<OneapiDevice *>(queue->device))
25{
26}
27
28OneapiDeviceGraphicsInterop::~OneapiDeviceGraphicsInterop()
29{
30 free();
31}
32
33void OneapiDeviceGraphicsInterop::set_buffer(GraphicsInteropBuffer &interop_buffer)
34{
35 if (interop_buffer.is_empty()) {
36 free();
37 return;
38 }
39
40 need_zero_ |= interop_buffer.take_zero();
41
42 if (!interop_buffer.has_new_handle()) {
43 return;
44 }
45
46 free();
47
48 if (interop_buffer.get_type() != GraphicsInteropDevice::VULKAN) {
49 /* SYCL only supports interop with Vulkan and D3D. */
51 << "oneAPI interop set_buffer called for invalid graphics API. Only Vulkan is supported.";
52 return;
53 }
54
55# ifdef _WIN32
56 /* import_external_memory will not take ownership of the handle. */
57 vulkan_windows_handle_ = reinterpret_cast<void *>(interop_buffer.take_handle());
58 auto sycl_mem_handle_type =
59 sycl::ext::oneapi::experimental::external_mem_handle_type::win32_nt_handle;
60 sycl::ext::oneapi::experimental::external_mem_descriptor<
61 sycl::ext::oneapi::experimental::resource_win32_handle>
62 sycl_external_mem_descriptor{vulkan_windows_handle_, sycl_mem_handle_type};
63# else
64 /* import_external_memory will take ownership of the file descriptor. */
65 auto sycl_mem_handle_type = sycl::ext::oneapi::experimental::external_mem_handle_type::opaque_fd;
66 sycl::ext::oneapi::experimental::external_mem_descriptor<
67 sycl::ext::oneapi::experimental::resource_fd>
68 sycl_external_mem_descriptor{static_cast<int>(interop_buffer.take_handle()),
69 sycl_mem_handle_type};
70# endif
71
72 sycl::queue *sycl_queue = reinterpret_cast<sycl::queue *>(device_->sycl_queue());
73 try {
74 sycl_external_memory_ = sycl::ext::oneapi::experimental::import_external_memory(
75 sycl_external_mem_descriptor, *sycl_queue);
76 }
77 catch (sycl::exception &e) {
78# ifdef _WIN32
79 CloseHandle(HANDLE(vulkan_windows_handle_));
80 vulkan_windows_handle_ = nullptr;
81# else
82 close(sycl_external_mem_descriptor.external_resource.file_descriptor);
83# endif
84 LOG_ERROR << "Error importing Vulkan memory: " << e.what();
85 return;
86 }
87
88 buffer_size_ = interop_buffer.get_size();
89
90 /* Like the CUDA/HIP backend, we map the buffer persistently. */
91 try {
92 sycl_memory_ptr_ = sycl::ext::oneapi::experimental::map_external_linear_memory(
93 sycl_external_memory_, 0, buffer_size_, *sycl_queue);
94 }
95 catch (sycl::exception &e) {
96 try {
97 sycl::ext::oneapi::experimental::release_external_memory(sycl_external_memory_, *sycl_queue);
98 }
99 catch (sycl::exception &e) {
100 LOG_ERROR << "Could not release external Vulkan memory: " << e.what();
101 }
102 sycl_external_memory_ = {};
103 buffer_size_ = 0;
104 /* Only need to close Windows handle, as file descriptor is owned by compute API. */
105# ifdef _WIN32
106 CloseHandle(HANDLE(vulkan_windows_handle_));
107 vulkan_windows_handle_ = nullptr;
108# endif
109 LOG_ERROR << "Error mapping external Vulkan memory: " << e.what();
110 return;
111 }
112}
113
114device_ptr OneapiDeviceGraphicsInterop::map()
115{
116 if (sycl_memory_ptr_ && need_zero_) {
117 try {
118 /* We do not wait on the returned event here, as CUDA also uses "cuMemsetD8Async". */
119 sycl::queue *sycl_queue = reinterpret_cast<sycl::queue *>(device_->sycl_queue());
120 sycl_queue->memset(sycl_memory_ptr_, 0, buffer_size_);
121 }
122 catch (sycl::exception &e) {
123 LOG_ERROR << "Error clearing external Vulkan memory: " << e.what();
124 return device_ptr(0);
125 }
126 need_zero_ = false;
127 }
128
129 return reinterpret_cast<device_ptr>(sycl_memory_ptr_);
130}
131
132void OneapiDeviceGraphicsInterop::unmap() {}
133
134void OneapiDeviceGraphicsInterop::free()
135{
136 if (sycl_external_memory_.raw_handle) {
137 sycl::queue *sycl_queue = reinterpret_cast<sycl::queue *>(device_->sycl_queue());
138 try {
139 sycl::ext::oneapi::experimental::unmap_external_linear_memory(sycl_memory_ptr_, *sycl_queue);
140 }
141 catch (sycl::exception &e) {
142 LOG_ERROR << "Could not unmap external Vulkan memory: " << e.what();
143 }
144 try {
145 sycl::ext::oneapi::experimental::release_external_memory(sycl_external_memory_, *sycl_queue);
146 }
147 catch (sycl::exception &e) {
148 LOG_ERROR << "Could not release external Vulkan memory: " << e.what();
149 }
150 sycl_memory_ptr_ = {};
151 sycl_external_memory_ = {};
152 }
153
154# ifdef _WIN32
155 if (vulkan_windows_handle_) {
156 CloseHandle(HANDLE(vulkan_windows_handle_));
157 vulkan_windows_handle_ = nullptr;
158 }
159# endif
160
161 buffer_size_ = 0;
162
163 need_zero_ = false;
164}
165
167
168#endif
void BLI_kdtree_nd_ free(KDTree *tree)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
GraphicsInteropDevice::Type get_type() const
#define CCL_NAMESPACE_END
#define LOG_ERROR
Definition log.h:101
uint64_t device_ptr
Definition types_base.h:44