Skip to content
Merged
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 @@ -2333,17 +2333,21 @@ depending on the API and operating system, so do external semaphore resource
handles.

It is important to note that the use of imported external semaphore objects
within SYCL requires the SYCL queue to have been constructed with both of the
following properties:

* `sycl::property::queue::in_order` -- the semaphore synchronization mechanism
is not supported on the default out-of-order queues.
* `sycl::ext::intel::property::queue::immediate_command_list` -- external
semaphore operations are only supported on queues backed by immediate
command lists. This restriction might be lifted in the future.
within SYCL requires the SYCL queue to have been constructed with the
`sycl::property::queue::in_order` property. The semaphore synchronization
mechanism is not supported on the default out-of-order queues.
Comment on lines +2336 to +2338

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Suggestion: With this change the spec no longer restricts external semaphore use to immediate command lists. The new tests, however, need REQUIRES-INTEL-DRIVER: lin: 39758, win: 101.9030. On older L0 drivers, a semaphore wait on a regular command list could hang silently, and nothing in the spec or the runtime warns the user. Could you add an implementation note here (or a runtime check or diagnostic in the L0 adapter) saying that regular command list support depends on the backend or driver? That way users on older drivers aren't caught out.


Use of the semaphore synchronization mechanism with a SYCL queue that was not
constructed with both of these properties will result in undefined behaviour.
constructed with this property will result in undefined behaviour.

[NOTE]
====
Support for external semaphore operations on queues backed by regular command
lists is backend- and driver-dependent. The {dpcpp} Level Zero backend requires
Intel graphics driver version 39758 or newer on Linux and 101.9030 or newer on
Windows. On older drivers, an external semaphore wait submitted to a regular
command list might not complete.
====

External semaphore import is facilitated through the following proposed
descriptor struct.
Expand Down Expand Up @@ -2850,4 +2854,7 @@ This query should be added in a later revision of the proposal.
- Add `supports_importing_handle_type`
|6.12|2025-07-03| - Add `win32_nt_dx11_resource` enum entry to
`external_mem_handle_type` for Direct3D 11 memory interop.
|6.13|2026-10-07| - Allow external semaphore operations on in-order queues
backed by regular command lists and document the associated
backend and driver requirements.
|======================
Original file line number Diff line number Diff line change
Expand Up @@ -475,8 +475,8 @@ int runTest(D3D11ProgramState &d3d11ProgramState, sycl::queue syclQueue,
int main() {
// Create SYCL queue, relying on SYCL device selection.
#ifdef TEST_SEMAPHORE_IMPORT
// External semaphore ops require an in-order queue backed by immediate
// command lists (see sycl_ext_oneapi_bindless_images.asciidoc).
// External semaphore operations require an in-order queue. This test also
// uses immediate command lists for its existing configuration.
sycl::queue syclQueue{
{sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::immediate_command_list{}}};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,8 @@ int main(int argc, char **argv) {

// SYCL INTEROP
try {
// Bindless image interop requires an in-order queue (per spec). External
// semaphore ops additionally require immediate command lists; see
// sycl_ext_oneapi_bindless_images.asciidoc.
// External semaphore operations require an in-order queue. This test also
// uses immediate command lists for its existing configuration.
sycl::property_list qProps =
useSemaphores ? sycl::property_list{sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,8 @@ int runTest(

// SYCL Import and Verification
try {
// Bindless image interop requires an in-order queue (per spec). External
// semaphore ops additionally require immediate command lists; see
// sycl_ext_oneapi_bindless_images.asciidoc.
// External semaphore operations require an in-order queue. This test also
// uses immediate command lists for its existing configuration.
sycl::property_list qProps =
useSemaphores ? sycl::property_list{sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,8 @@ int runTest(
}

try {
// Bindless image interop requires an in-order queue (per spec). External
// semaphore ops additionally require immediate command lists; see
// sycl_ext_oneapi_bindless_images.asciidoc.
// External semaphore operations require an in-order queue. This test also
// uses immediate command lists for its existing configuration.
sycl::property_list qProps =
useSemaphores ? sycl::property_list{sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,9 +408,8 @@ int runTest(
signalExportableFence(ctx, extFenceB);

try {
// Bindless image interop requires an in-order queue (per spec). External
// semaphore ops additionally require immediate command lists; see
// sycl_ext_oneapi_bindless_images.asciidoc.
// External semaphore operations require an in-order queue. This test also
// uses immediate command lists for its existing configuration.
sycl::property_list qProps =
useSemaphores ? sycl::property_list{sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,8 @@ int runTest(

// SYCL Import and Verification
try {
// Bindless image interop requires an in-order queue (per spec). External
// semaphore ops additionally require immediate command lists; see
// sycl_ext_oneapi_bindless_images.asciidoc.
// External semaphore operations require an in-order queue. This test also
// uses immediate command lists for its existing configuration.
sycl::property_list qProps =
useSemaphores ? sycl::property_list{sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,8 @@ int runTest(
}

try {
// Bindless image interop requires an in-order queue (per spec). External
// semaphore ops additionally require immediate command lists; see
// sycl_ext_oneapi_bindless_images.asciidoc.
// External semaphore operations require an in-order queue. This test also
// uses immediate command lists for its existing configuration.
sycl::property_list qProps =
useSemaphores ? sycl::property_list{sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,8 @@ int runTest(

// SYCL Import and Verification
try {
// Bindless image interop requires an in-order queue (per spec). External
// semaphore ops additionally require immediate command lists; see
// sycl_ext_oneapi_bindless_images.asciidoc.
// External semaphore operations require an in-order queue. This test also
// uses immediate command lists for its existing configuration.
sycl::property_list qProps =
useSemaphores ? sycl::property_list{sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,8 @@ int runTest(
}

try {
// Bindless image interop requires an in-order queue (per spec). External
// semaphore ops additionally require immediate command lists; see
// sycl_ext_oneapi_bindless_images.asciidoc.
// External semaphore operations require an in-order queue. This test also
// uses immediate command lists for its existing configuration.
sycl::property_list qProps =
useSemaphores ? sycl::property_list{sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ int main(int argc, char **argv) {
executeAndWait(d3dCtx);

try {
// Semaphore ops require an in-order queue with immediate CLs.
// External semaphore operations require an in-order queue. This test also
// uses immediate command lists for its existing configuration.
sycl::property_list qProps{
sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::immediate_command_list{}};
Expand Down
4 changes: 2 additions & 2 deletions sycl/test-e2e/bindless_images/dx12_interop/d3d12_setup.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,10 @@ struct D3D12ImageResources {
// ---------------------------------------------------------
// D3D12 LIFECYCLE MANAGEMENT
// ---------------------------------------------------------
inline D3D12Context createD3D12Context() {
inline D3D12Context createD3D12Context(IDXGIAdapter *adapter = nullptr) {
D3D12Context ctx;

ThrowIfFailed(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0,
ThrowIfFailed(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&ctx.device)),
"Failed to create D3D12 Device");

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// REQUIRES: aspect-ext_oneapi_external_semaphore_import, aspect-usm_shared_allocations
// REQUIRES: aspect-ext_intel_device_info_luid
// REQUIRES: windows, level_zero

// Regular command-list external semaphore support requires this driver
// version.
// REQUIRES-INTEL-DRIVER: win: 101.9030

// XFAIL: windows && run-mode && gpu-intel-gen12
// XFAIL-TRACKER: https://github.com/intel/llvm/issues/23249

// RUN: %{build} %link-directx -o %t.exe %if target-spir %{ -Wno-ignored-attributes %}
// RUN: env UR_L0_BATCH_SIZE=8 %{run} %t.exe

// Verify that external semaphore wait and signal operations work on an
// in-order queue backed by regular (non-immediate) command lists.

#include "d3d12_setup.hpp"
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <sycl/atomic_ref.hpp>
#include <sycl/detail/core.hpp>
#include <sycl/ext/intel/info/device.hpp>
#include <sycl/ext/oneapi/bindless_images.hpp>
#include <sycl/properties/queue_properties.hpp>
#include <sycl/usm.hpp>
#include <thread>

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

namespace syclexp = sycl::ext::oneapi::experimental;

D3D12Context createSyclD3D12Context(const sycl::device &device) {
auto syclLuid = device.get_info<sycl::ext::intel::info::device::luid>();
static_assert(sizeof(LUID) == sizeof(syclLuid));
LUID adapterLuid{};
std::memcpy(&adapterLuid, syclLuid.data(), sizeof(adapterLuid));

ComPtr<IDXGIFactory4> factory;
ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&factory)),
"Failed to create DXGI factory");

ComPtr<IDXGIAdapter1> adapter;
ThrowIfFailed(factory->EnumAdapterByLuid(adapterLuid, IID_PPV_ARGS(&adapter)),
"Failed to find the DXGI adapter for the SYCL device");
return createD3D12Context(adapter.Get());
}

[[noreturn]] void failWithoutCleanup(const std::string &message) {
std::cerr << message << std::endl;
std::_Exit(EXIT_FAILURE);
}

using MarkerAtomicRef =
sycl::atomic_ref<int, sycl::memory_order::relaxed,
sycl::memory_scope::system,
sycl::access::address_space::global_space>;

bool waitForMarker(int *marker) {
const auto deadline =
std::chrono::steady_clock::now() + std::chrono::seconds(5);
while (MarkerAtomicRef(*marker).load() == 0) {
if (std::chrono::steady_clock::now() >= deadline)
return false;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return true;
}

bool remainsIncomplete(const sycl::event &event) {
const auto deadline =
std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
do {
if (event.get_info<sycl::info::event::command_execution_status>() ==
sycl::info::event_command_status::complete)
return false;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
} while (std::chrono::steady_clock::now() < deadline);
return true;
}

bool waitForCompletion(const sycl::event &event) {
const auto deadline =
std::chrono::steady_clock::now() + std::chrono::seconds(5);
while (event.get_info<sycl::info::event::command_execution_status>() !=
sycl::info::event_command_status::complete) {
if (std::chrono::steady_clock::now() >= deadline)
return false;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return true;
}

int main() {
sycl::queue q{
{sycl::property::queue::in_order{},
sycl::ext::intel::property::queue::no_immediate_command_list{}}};
auto device = q.get_device();
auto context = q.get_context();

D3D12Context d3dCtx = createSyclD3D12Context(device);
D3D12ExportableFence extFence = createExportableFence(d3dCtx);

auto semDesc =
syclexp::external_semaphore_descriptor<syclexp::resource_win32_handle>{
extFence.sharedHandle,
syclexp::external_semaphore_handle_type::win32_nt_dx12_fence};
syclexp::external_semaphore syclSem =
syclexp::import_external_semaphore(semDesc, device, context);
int *marker = sycl::malloc_shared<int>(1, q);
MarkerAtomicRef(*marker).store(0);

try {
const uint64_t D3DSignalValue = extFence.fenceValue + 1;
q.single_task([=]() { MarkerAtomicRef(*marker).store(1); });
sycl::event waitEvent =
q.ext_oneapi_wait_external_semaphore(syclSem, D3DSignalValue);

if (!waitForMarker(marker))
failWithoutCleanup(
"The external semaphore wait did not submit the preceding batch.");
if (!remainsIncomplete(waitEvent))
failWithoutCleanup(
"The external semaphore wait completed before D3D12 signaled it.");

signalExportableFence(d3dCtx, extFence);
if (!waitForCompletion(waitEvent))
failWithoutCleanup(
"The external semaphore wait did not complete after D3D12 signaled "
"it.");

const uint64_t SyclSignalValue = D3DSignalValue + 1;
q.ext_oneapi_signal_external_semaphore(syclSem, SyclSignalValue);

ThrowIfFailed(extFence.fence->SetEventOnCompletion(SyclSignalValue,
d3dCtx.fenceEvent),
"Failed to wait for shared fence");
constexpr DWORD TimeoutMs = 5000;
DWORD waitResult = WaitForSingleObject(d3dCtx.fenceEvent, TimeoutMs);
if (waitResult == WAIT_TIMEOUT)
failWithoutCleanup(
"Timed out waiting for the regular command-list semaphore signal.");
if (waitResult != WAIT_OBJECT_0)
failWithoutCleanup("Waiting for the external semaphore failed with "
"error " +
std::to_string(GetLastError()));

q.wait_and_throw();
} catch (const std::exception &e) {
failWithoutCleanup(e.what());
}

sycl::free(marker, q);
syclexp::release_external_semaphore(syclSem, device, context);
cleanupExportableFence(extFence);
if (d3dCtx.fenceEvent)
CloseHandle(d3dCtx.fenceEvent);
return 0;
}
Loading
Loading