Skip to content

Commit 48d6393

Browse files
committed
[SYCL] Add Level-Zero interop with specification of ownership
Signed-off-by: Sergey V Maslov <[email protected]>
1 parent 33a2868 commit 48d6393

File tree

9 files changed

+52
-17
lines changed

9 files changed

+52
-17
lines changed

sycl/include/CL/sycl/backend/level_zero.hpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,20 @@ struct interop<backend::level_zero, accessor<DataT, Dimensions, AccessMode,
5151

5252
namespace level_zero {
5353

54-
// Implementation of various "make" functions resides in libsycl.so
54+
// Since Level-Zero is not doing any reference counting itself, we have to
55+
// be explicit about the ownership of the native handles used in the
56+
// interop functions below.
57+
//
58+
enum ownership { transfer, keep };
59+
60+
// Implementation of various "make" functions resides in libsycl.so and thus
61+
// their interface needs to be backend agnostic.
5562
__SYCL_EXPORT platform make_platform(pi_native_handle NativeHandle);
5663
__SYCL_EXPORT device make_device(const platform &Platform,
5764
pi_native_handle NativeHandle);
5865
__SYCL_EXPORT context make_context(const vector_class<device> &DeviceList,
59-
pi_native_handle NativeHandle);
66+
pi_native_handle NativeHandle,
67+
bool keep_ownership = false);
6068
__SYCL_EXPORT program make_program(const context &Context,
6169
pi_native_handle NativeHandle);
6270
__SYCL_EXPORT queue make_queue(const context &Context,
@@ -82,11 +90,17 @@ T make(const platform &Platform,
8290
/// created SYCL context. Provided devices and native context handle must
8391
/// be associated with the same platform.
8492
/// \param Interop is a Level Zero native context handle.
93+
/// \param Ownership (optional) specifies who will assume ownership of the
94+
/// native context handle. Default is that SYCL RT does, so it destroys
95+
/// the native handle when the created SYCL object goes out of life.
96+
///
8597
template <typename T, typename std::enable_if<
8698
std::is_same<T, context>::value>::type * = nullptr>
8799
T make(const vector_class<device> &DeviceList,
88-
typename interop<backend::level_zero, T>::type Interop) {
89-
return make_context(DeviceList, detail::pi::cast<pi_native_handle>(Interop));
100+
typename interop<backend::level_zero, T>::type Interop,
101+
ownership Ownership = ownership::transfer) {
102+
return make_context(DeviceList, detail::pi::cast<pi_native_handle>(Interop),
103+
Ownership == ownership::keep);
90104
}
91105

92106
// Construction of SYCL program.

sycl/include/CL/sycl/detail/pi.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,8 @@ piextContextGetNativeHandle(pi_context context, pi_native_handle *nativeHandle);
983983
/// \param devices is the list of devices in the context. Parameter is ignored
984984
/// if devices can be queried from the context native handle for a
985985
/// backend.
986+
/// \param ownNativeHandle tells if SYCL RT should assume the ownership of
987+
/// the native handle, if it can.
986988
/// \param context is the PI context created from the native handle.
987989
/// \return PI_SUCCESS if successfully created pi_context from the handle.
988990
/// PI_OUT_OF_HOST_MEMORY if can't allocate memory for the pi_context
@@ -991,7 +993,7 @@ piextContextGetNativeHandle(pi_context context, pi_native_handle *nativeHandle);
991993
/// native handle. PI_UNKNOWN_ERROR in case of another error.
992994
__SYCL_EXPORT pi_result piextContextCreateWithNativeHandle(
993995
pi_native_handle nativeHandle, pi_uint32 numDevices,
994-
const pi_device *devices, pi_context *context);
996+
const pi_device *devices, bool ownNativeHandle, pi_context *context);
995997

996998
//
997999
// Queue

sycl/plugins/cuda/pi_cuda.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,7 @@ pi_result cuda_piextContextGetNativeHandle(pi_context context,
16981698
pi_result cuda_piextContextCreateWithNativeHandle(pi_native_handle nativeHandle,
16991699
pi_uint32 num_devices,
17001700
const pi_device *devices,
1701+
bool ownNativeHandle,
17011702
pi_context *context) {
17021703
cl::sycl::detail::pi::die(
17031704
"Creation of PI context from native handle not implemented");

sycl/plugins/level_zero/pi_level_zero.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,7 +1954,7 @@ pi_result piContextCreate(const pi_context_properties *Properties,
19541954
ZE_CALL(zeContextCreate((*Devices)->Platform->ZeDriver, &ContextDesc,
19551955
&ZeContext));
19561956
try {
1957-
*RetContext = new _pi_context(ZeContext, NumDevices, Devices);
1957+
*RetContext = new _pi_context(ZeContext, NumDevices, Devices, true);
19581958
(*RetContext)->initialize();
19591959
} catch (const std::bad_alloc &) {
19601960
return PI_OUT_OF_HOST_MEMORY;
@@ -2013,6 +2013,7 @@ pi_result piextContextGetNativeHandle(pi_context Context,
20132013
pi_result piextContextCreateWithNativeHandle(pi_native_handle NativeHandle,
20142014
pi_uint32 NumDevices,
20152015
const pi_device *Devices,
2016+
bool OwnNativeHandle,
20162017
pi_context *RetContext) {
20172018
PI_ASSERT(NativeHandle, PI_INVALID_VALUE);
20182019
PI_ASSERT(Devices, PI_INVALID_DEVICE);
@@ -2021,7 +2022,7 @@ pi_result piextContextCreateWithNativeHandle(pi_native_handle NativeHandle,
20212022

20222023
try {
20232024
*RetContext = new _pi_context(pi_cast<ze_context_handle_t>(NativeHandle),
2024-
NumDevices, Devices);
2025+
NumDevices, Devices, OwnNativeHandle);
20252026
(*RetContext)->initialize();
20262027
} catch (const std::bad_alloc &) {
20272028
return PI_OUT_OF_HOST_MEMORY;
@@ -2059,7 +2060,9 @@ pi_result piContextRelease(pi_context Context) {
20592060
// and therefore it must be valid at that point.
20602061
// Technically it should be placed to the destructor of pi_context
20612062
// but this makes API error handling more complex.
2062-
ZE_CALL(zeContextDestroy(ZeContext));
2063+
if (Context->OwnZeContext) {
2064+
ZE_CALL(zeContextDestroy(ZeContext));
2065+
}
20632066

20642067
return Result;
20652068
}

sycl/plugins/level_zero/pi_level_zero.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,9 @@ struct _pi_device : _pi_object {
171171

172172
struct _pi_context : _pi_object {
173173
_pi_context(ze_context_handle_t ZeContext, pi_uint32 NumDevices,
174-
const pi_device *Devs)
175-
: ZeContext{ZeContext}, Devices{Devs, Devs + NumDevices},
174+
const pi_device *Devs, bool OwnZeContext)
175+
: ZeContext{ZeContext},
176+
OwnZeContext{OwnZeContext}, Devices{Devs, Devs + NumDevices},
176177
ZeCommandListInit{nullptr}, ZeEventPool{nullptr},
177178
NumEventsAvailableInEventPool{}, NumEventsLiveInEventPool{} {
178179
// Create USM allocator context for each pair (device, context).
@@ -201,6 +202,10 @@ struct _pi_context : _pi_object {
201202
// resources that may be used by multiple devices.
202203
ze_context_handle_t ZeContext;
203204

205+
// Indicates if we own the ZeContext or it came from interop that
206+
// asked to not transfer the ownership to SYCL RT.
207+
bool OwnZeContext;
208+
204209
// Keep the PI devices this PI context was created for.
205210
std::vector<pi_device> Devices;
206211

sycl/plugins/opencl/pi_opencl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,10 +536,12 @@ pi_result piContextCreate(const pi_context_properties *properties,
536536
pi_result piextContextCreateWithNativeHandle(pi_native_handle nativeHandle,
537537
pi_uint32 num_devices,
538538
const pi_device *devices,
539+
bool ownNativeHandle,
539540
pi_context *piContext) {
540541
(void)num_devices;
541542
(void)devices;
542543
assert(piContext != nullptr);
544+
assert(ownNativeHandle == false);
543545
*piContext = reinterpret_cast<pi_context>(nativeHandle);
544546
return PI_SUCCESS;
545547
}

sycl/source/backend/level_zero.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ __SYCL_EXPORT device make_device(const platform &Platform,
4848
//----------------------------------------------------------------------------
4949
// Implementation of level_zero::make<context>
5050
__SYCL_EXPORT context make_context(const vector_class<device> &DeviceList,
51-
pi_native_handle NativeHandle) {
51+
pi_native_handle NativeHandle,
52+
bool KeepOwnership) {
5253
const auto &Plugin = pi::getPlugin<backend::level_zero>();
5354
// Create PI context first.
5455
pi_context PiContext;
@@ -57,7 +58,8 @@ __SYCL_EXPORT context make_context(const vector_class<device> &DeviceList,
5758
DeviceHandles.push_back(detail::getSyclObjImpl(Dev)->getHandleRef());
5859
}
5960
Plugin.call<PiApiKind::piextContextCreateWithNativeHandle>(
60-
NativeHandle, DeviceHandles.size(), DeviceHandles.data(), &PiContext);
61+
NativeHandle, DeviceHandles.size(), DeviceHandles.data(), !KeepOwnership,
62+
&PiContext);
6163
// Construct the SYCL context from PI context.
6264
return detail::createSyclObjFromImpl<context>(
6365
std::make_shared<context_impl>(PiContext, async_handler{}, Plugin));

sycl/source/backend/opencl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ __SYCL_EXPORT context make_context(pi_native_handle NativeHandle) {
5151
// Create PI context first.
5252
pi::PiContext PiContext;
5353
Plugin.call<PiApiKind::piextContextCreateWithNativeHandle>(
54-
NativeHandle, 0, nullptr, &PiContext);
54+
NativeHandle, 0, nullptr, false, &PiContext);
5555
// Construct the SYCL context from PI context.
5656
return detail::createSyclObjFromImpl<context>(
5757
std::make_shared<context_impl>(PiContext, async_handler{}, Plugin));

sycl/source/detail/context_impl.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ context_impl::context_impl(const vector_class<cl::sycl::device> Devices,
5656
getPlugin().call<PiApiKind::piContextCreate>(
5757
Props, DeviceIds.size(), DeviceIds.data(), nullptr, nullptr, &MContext);
5858
#else
59-
cl::sycl::detail::pi::die("CUDA support was not enabled at compilation time");
59+
cl::sycl::detail::pi::die(
60+
"CUDA support was not enabled at compilation time");
6061
#endif
6162
} else {
6263
getPlugin().call<PiApiKind::piContextCreate>(nullptr, DeviceIds.size(),
@@ -96,7 +97,12 @@ context_impl::context_impl(RT::PiContext PiContext, async_handler AsyncHandler,
9697
// TODO catch an exception and put it to list of asynchronous exceptions
9798
// getPlugin() will be the same as the Plugin passed. This should be taken
9899
// care of when creating device object.
99-
getPlugin().call<PiApiKind::piContextRetain>(MContext);
100+
//
101+
// TODO: Move this backend-specific retain of the context to SYCL-2020 style
102+
// make_context<backend::opencl> interop, when that is created.
103+
if (getPlugin().getBackend() == cl::sycl::backend::opencl) {
104+
getPlugin().call<PiApiKind::piContextRetain>(MContext);
105+
}
100106
MKernelProgramCache.setContextPtr(this);
101107
}
102108

@@ -153,8 +159,8 @@ KernelProgramCache &context_impl::getKernelProgramCache() const {
153159
return MKernelProgramCache;
154160
}
155161

156-
bool
157-
context_impl::hasDevice(shared_ptr_class<detail::device_impl> Device) const {
162+
bool context_impl::hasDevice(
163+
shared_ptr_class<detail::device_impl> Device) const {
158164
for (auto D : MDevices)
159165
if (getSyclObjImpl(D) == Device)
160166
return true;

0 commit comments

Comments
 (0)