Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,13 @@ auto get_native(const SyclObjectT &Obj)
-> backend_return_t<BackendName, SyclObjectT>
```
It is currently supported for SYCL ```platform```, ```device```, ```context```, ```queue```, ```event```,
```kernel_bundle```, and ```kernel``` classes.
```kernel_bundle```, and ```kernel``` classes.

#### 4.2.1 Default context mapping guarantee

When using the Level Zero v2 adapter, if a SYCL context is created with all root devices of a Level Zero platform (for example, the context returned by ```platform::khr_get_default_context()```), the backend reuses the Level Zero driver default context. In this case, the native handle returned by ```sycl::get_native<backend::ext_oneapi_level_zero>(Ctx)``` is the same handle returned by ```zeDriverGetDefaultContext``` for that platform's driver.

This guarantee applies only to full-platform root-device contexts. Contexts created for a subset of devices, with sub-devices, or with custom context creation properties are not guaranteed to map to the Level Zero driver default context.

The ```get_native(queue)``` function returns either ```ze_command_queue_handle_t``` or ```ze_command_list_handle_t``` depending on the manner in which the input argument ```queue``` had been created. Queues created with the SYCL ```queue``` constructors have a default setting for whether they use command queues or command lists. The default and how it may be changed is documented in the description for the environment variable ```SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS```. Queues created using ```make_queue()``` use either a command list or command queue depending on the input argument to ```make_queue``` and are not affected by the default for SYCL queues or the environment variable.

Expand Down
100 changes: 100 additions & 0 deletions sycl/test-e2e/Adapters/level_zero/default_context_reuse.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// REQUIRES: level_zero_v2_adapter

// API 1.14+ is required for zeDriverGetDefaultContext
// REQUIRES-INTEL-DRIVER: lin: 36300, win: 101.7080

// RUN: %{build} %level_zero_options -o %t.out
// RUN: %{run} %t.out

// Test that full-platform SYCL contexts are reused and match the platform
// default context when all root devices of a platform are requested.

#include <iostream>
#include <level_zero/ze_api.h>
#include <sycl/backend.hpp>
#include <sycl/detail/core.hpp>
#include <sycl/ext/oneapi/backend/level_zero.hpp>
#include <sycl/platform.hpp>
#include <vector>

int main() {
ze_result_t initResult = zeInit(ZE_INIT_FLAG_GPU_ONLY);
if (initResult != ZE_RESULT_SUCCESS) {
std::cerr << "FAIL: zeInit failed with error code: " << initResult << "\n";
return 1;
}

int failed = 0;
bool anyTested = false;

for (auto &platform : sycl::platform::get_platforms()) {
if (platform.get_backend() != sycl::backend::ext_oneapi_level_zero)
continue;

// Collect all root devices exposed by this platform.
std::vector<sycl::device> rootDevices = platform.get_devices();
if (rootDevices.empty())
continue;

anyTested = true;

// Create two independent SYCL contexts with the full set of root devices.
// After the fix both should wrap the same underlying ze_context (the
// driver default). Before the fix each call produces a distinct handle.
sycl::context ctx1(rootDevices);
sycl::context ctx2(rootDevices);

ze_context_handle_t h1 =
sycl::get_native<sycl::backend::ext_oneapi_level_zero>(ctx1);
ze_context_handle_t h2 =
sycl::get_native<sycl::backend::ext_oneapi_level_zero>(ctx2);

std::cout << "ctx1 ze_context handle: " << h1 << "\n";
std::cout << "ctx2 ze_context handle: " << h2 << "\n";

if (h1 != h2) {
std::cerr << "FAIL: Two full-platform SYCL contexts have different "
"ze_context handles.\n";
++failed;
} else {
std::cout << "PASS: Both full-platform contexts share the same "
"ze_context handle.\n";
}

ze_driver_handle_t driver =
sycl::get_native<sycl::backend::ext_oneapi_level_zero>(platform);
ze_context_handle_t hDriverDef = zeDriverGetDefaultContext(driver);
if (!hDriverDef) {
std::cerr << "FAIL: zeDriverGetDefaultContext() returned a null "
"handle.\n";
++failed;
continue;
}
if (h1 != hDriverDef) {
std::cerr << "FAIL: explicit full-platform context handle differs "
"from zeDriverGetDefaultContext().\n";
++failed;
}

// Also verify the platform default context.
sycl::context defCtx = platform.khr_get_default_context();
ze_context_handle_t hDef =
sycl::get_native<sycl::backend::ext_oneapi_level_zero>(defCtx);
std::cout << "platform default context handle: " << hDef << "\n";
if (hDef != hDriverDef) {
std::cerr << "FAIL: platform.khr_get_default_context() handle differs "
"from zeDriverGetDefaultContext().\n";
++failed;
} else {
std::cout << "PASS: platform default context matches "
"zeDriverGetDefaultContext().\n";
}
}

if (!anyTested) {
std::cout << "No suitable L0 platform found, test skipped.\n";
return 0;
}

return failed ? 1 : 0;
}
72 changes: 68 additions & 4 deletions unified-runtime/source/adapters/level_zero/v2/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
//
//===----------------------------------------------------------------------===//

#include "../adapter.hpp"
#include "../device.hpp"

#include "context.hpp"
Expand Down Expand Up @@ -61,14 +62,54 @@ populateP2PDevices(const std::vector<ur_device_handle_t> &devices) {
return p2pDevices;
}

template <typename T> static void sortAndUnique(std::vector<T> &values) {
std::sort(values.begin(), values.end());
values.erase(std::unique(values.begin(), values.end()), values.end());
}

static std::vector<ur_device_handle_t>
uniqueDevices(uint32_t numDevices, const ur_device_handle_t *phDevices) {
std::vector<ur_device_handle_t> devices(phDevices, phDevices + numDevices);
std::sort(devices.begin(), devices.end());
devices.erase(std::unique(devices.begin(), devices.end()), devices.end());
sortAndUnique(devices);
return devices;
}

static bool isFullPlatformRootDeviceList(uint32_t deviceCount,
const ur_device_handle_t *phDevices) {
ur_platform_handle_t hPlatform = phDevices[0]->Platform;

std::vector<ur_device_handle_t> requestedDevices;
requestedDevices.reserve(deviceCount);
for (uint32_t i = 0; i < deviceCount; ++i) {
requestedDevices.push_back(phDevices[i]);
}

sortAndUnique(requestedDevices);

uint32_t platformDeviceCount = 0;
ur_result_t result = ur::level_zero::urDeviceGet(
hPlatform, UR_DEVICE_TYPE_ALL, 0, nullptr, &platformDeviceCount);
if (result != UR_RESULT_SUCCESS || platformDeviceCount == 0) {
return false;
}

std::vector<ur_device_handle_t> platformDevices(platformDeviceCount);
result = ur::level_zero::urDeviceGet(hPlatform, UR_DEVICE_TYPE_ALL,
platformDeviceCount,
platformDevices.data(), nullptr);
if (result != UR_RESULT_SUCCESS) {
return false;
}

if (platformDevices.empty()) {
return false;
}

sortAndUnique(platformDevices);

return requestedDevices == platformDevices;
}

ur_context_handle_t_::ur_context_handle_t_(ze_context_handle_t hContext,
uint32_t numDevices,
const ur_device_handle_t *phDevices,
Expand Down Expand Up @@ -166,14 +207,37 @@ ur_result_t urContextCreate(uint32_t deviceCount,
const ur_context_properties_t * /*pProperties*/,
ur_context_handle_t *phContext) try {

if (deviceCount == 0 || !phDevices || !phContext) {
return UR_RESULT_ERROR_INVALID_NULL_POINTER;
}
for (uint32_t i = 0; i < deviceCount; ++i) {
if (!phDevices[i]) {
return UR_RESULT_ERROR_INVALID_NULL_POINTER;
}
}

ur_platform_handle_t hPlatform = phDevices[0]->Platform;
ZeStruct<ze_context_desc_t> contextDesc{};

ze_context_handle_t zeContext{};
ZE2UR_CALL(zeContextCreate, (hPlatform->ZeDriver, &contextDesc, &zeContext));
bool ownZeContext = true;

if (isFullPlatformRootDeviceList(deviceCount, phDevices)) {
ze_context_handle_t zeDefaultContext =
zeDriverGetDefaultContext(hPlatform->ZeDriver);
if (zeDefaultContext) {
zeContext = zeDefaultContext;
ownZeContext = false;
}
}

if (!zeContext) {
ZE2UR_CALL(zeContextCreate,
(hPlatform->ZeDriver, &contextDesc, &zeContext));
}

*phContext =
new ur_context_handle_t_(zeContext, deviceCount, phDevices, true);
new ur_context_handle_t_(zeContext, deviceCount, phDevices, ownZeContext);
return UR_RESULT_SUCCESS;
} catch (...) {
return exceptionToResult(std::current_exception());
Expand Down
Loading