mirror of
https://github.com/KhronosGroup/OpenCL-CTS.git
synced 2026-03-19 06:09:01 +00:00
Add tests for cl_khr_external_semaphore_dx_fence (#2482)
- Add tests to import DirectX 12 fences as semaphores. - Add tests to export semaphores as DirectX 12 fences. - Add queries tests. - Add negative tests. Fixes: https://github.com/KhronosGroup/OpenCL-CTS/issues/2480
This commit is contained in:
@@ -30,6 +30,7 @@ endif(USE_CL_EXPERIMENTAL)
|
|||||||
#-----------------------------------------------------------
|
#-----------------------------------------------------------
|
||||||
option(D3D10_IS_SUPPORTED "Run DirectX 10 interop tests" OFF)
|
option(D3D10_IS_SUPPORTED "Run DirectX 10 interop tests" OFF)
|
||||||
option(D3D11_IS_SUPPORTED "Run DirectX 11 interop tests" OFF)
|
option(D3D11_IS_SUPPORTED "Run DirectX 11 interop tests" OFF)
|
||||||
|
option(D3D12_IS_SUPPORTED "Run DirectX 12 interop tests" OFF)
|
||||||
option(GL_IS_SUPPORTED "Run OpenGL interop tests" OFF)
|
option(GL_IS_SUPPORTED "Run OpenGL interop tests" OFF)
|
||||||
option(GLES_IS_SUPPORTED "Run OpenGL ES interop tests" OFF)
|
option(GLES_IS_SUPPORTED "Run OpenGL ES interop tests" OFF)
|
||||||
option(VULKAN_IS_SUPPORTED "Run Vulkan interop tests" OFF)
|
option(VULKAN_IS_SUPPORTED "Run Vulkan interop tests" OFF)
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ if(VULKAN_IS_SUPPORTED)
|
|||||||
add_subdirectory( common/vulkan_wrapper )
|
add_subdirectory( common/vulkan_wrapper )
|
||||||
add_subdirectory( vulkan )
|
add_subdirectory( vulkan )
|
||||||
endif()
|
endif()
|
||||||
|
if(D3D12_IS_SUPPORTED)
|
||||||
|
add_subdirectory(common/directx_wrapper)
|
||||||
|
endif ()
|
||||||
|
|
||||||
file(GLOB CSV_FILES "opencl_conformance_tests_*.csv")
|
file(GLOB CSV_FILES "opencl_conformance_tests_*.csv")
|
||||||
|
|
||||||
|
|||||||
13
test_conformance/common/directx_wrapper/CMakeLists.txt
Normal file
13
test_conformance/common/directx_wrapper/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
set(DIRECTX_WRAPPER_SOURCES
|
||||||
|
directx_wrapper.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(directx_wrapper STATIC ${DIRECTX_WRAPPER_SOURCES})
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
include_directories(${CLConform_INCLUDE_DIR})
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
target_link_libraries(directx_wrapper d3d12)
|
||||||
|
endif ()
|
||||||
71
test_conformance/common/directx_wrapper/directx_wrapper.cpp
Normal file
71
test_conformance/common/directx_wrapper/directx_wrapper.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2025 The Khronos Group Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "directx_wrapper.hpp"
|
||||||
|
|
||||||
|
DirectXWrapper::DirectXWrapper()
|
||||||
|
{
|
||||||
|
|
||||||
|
HRESULT hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_0,
|
||||||
|
IID_PPV_ARGS(&dx_device));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to create DirectX 12 device");
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_COMMAND_QUEUE_DESC desc{};
|
||||||
|
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||||
|
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||||
|
hr = dx_device->CreateCommandQueue(&desc, IID_PPV_ARGS(&dx_command_queue));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to create DirectX 12 command queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = dx_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||||
|
IID_PPV_ARGS(&dx_command_allocator));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Failed to create DirectX 12 command allocator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D12Device* DirectXWrapper::getDXDevice() const { return dx_device.Get(); }
|
||||||
|
|
||||||
|
ID3D12CommandQueue* DirectXWrapper::getDXCommandQueue() const
|
||||||
|
{
|
||||||
|
return dx_command_queue.Get();
|
||||||
|
}
|
||||||
|
ID3D12CommandAllocator* DirectXWrapper::getDXCommandAllocator() const
|
||||||
|
{
|
||||||
|
return dx_command_allocator.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectXFenceWrapper::DirectXFenceWrapper(ID3D12Device* dx_device)
|
||||||
|
: dx_device(dx_device)
|
||||||
|
{
|
||||||
|
if (!dx_device)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("ID3D12Device is not valid");
|
||||||
|
}
|
||||||
|
const HRESULT hr = dx_device->CreateFence(0, D3D12_FENCE_FLAG_SHARED,
|
||||||
|
IID_PPV_ARGS(&dx_fence));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to create the DirectX fence");
|
||||||
|
}
|
||||||
|
}
|
||||||
47
test_conformance/common/directx_wrapper/directx_wrapper.hpp
Normal file
47
test_conformance/common/directx_wrapper/directx_wrapper.hpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2025 The Khronos Group Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <d3d12.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
using namespace Microsoft::WRL;
|
||||||
|
|
||||||
|
class DirectXWrapper {
|
||||||
|
public:
|
||||||
|
DirectXWrapper();
|
||||||
|
|
||||||
|
ID3D12Device* getDXDevice() const;
|
||||||
|
ID3D12CommandQueue* getDXCommandQueue() const;
|
||||||
|
ID3D12CommandAllocator* getDXCommandAllocator() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ComPtr<ID3D12Device> dx_device = nullptr;
|
||||||
|
ComPtr<ID3D12CommandQueue> dx_command_queue = nullptr;
|
||||||
|
ComPtr<ID3D12CommandAllocator> dx_command_allocator = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DirectXFenceWrapper {
|
||||||
|
public:
|
||||||
|
DirectXFenceWrapper(ID3D12Device* dx_device);
|
||||||
|
ID3D12Fence* operator*() const { return dx_fence.Get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComPtr<ID3D12Fence> dx_fence = nullptr;
|
||||||
|
ComPtr<ID3D12Device> dx_device = nullptr;
|
||||||
|
};
|
||||||
@@ -15,3 +15,6 @@ add_subdirectory( cl_ext_buffer_device_address )
|
|||||||
if(VULKAN_IS_SUPPORTED)
|
if(VULKAN_IS_SUPPORTED)
|
||||||
add_subdirectory( cl_khr_external_semaphore )
|
add_subdirectory( cl_khr_external_semaphore )
|
||||||
endif()
|
endif()
|
||||||
|
if(D3D12_IS_SUPPORTED)
|
||||||
|
add_subdirectory( cl_khr_external_semaphore_dx_fence )
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
if (WIN32)
|
||||||
|
include_directories(${CLConform_SOURCE_DIR}/test_common/harness
|
||||||
|
${CLConform_INCLUDE_DIR})
|
||||||
|
link_directories(${CL_LIB_DIR})
|
||||||
|
|
||||||
|
list(APPEND CLConform_LIBRARIES directx_wrapper)
|
||||||
|
|
||||||
|
set(MODULE_NAME CL_KHR_EXTERNAL_SEMAPHORE_DX_FENCE)
|
||||||
|
|
||||||
|
set(${MODULE_NAME}_SOURCES
|
||||||
|
main.cpp
|
||||||
|
test_external_semaphore_dx_fence.cpp
|
||||||
|
test_external_semaphore_dx_fence_negative_wait_signal.cpp
|
||||||
|
test_external_semaphore_dx_fence_queries.cpp
|
||||||
|
test_external_semaphore_dx_fence_export.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set_source_files_properties(
|
||||||
|
${MODULE_NAME}_SOURCES
|
||||||
|
PROPERTIES LANGUAGE CXX)
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
include_directories("../../common/directx_wrapper")
|
||||||
|
include(../../CMakeCommon.txt)
|
||||||
|
endif (WIN32)
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2025 The Khronos Group Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
#include "harness/testHarness.h"
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[])
|
||||||
|
{
|
||||||
|
return runTestHarness(argc, argv, test_registry::getInstance().num_tests(),
|
||||||
|
test_registry::getInstance().definitions(), false, 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2025 The Khronos Group Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "harness/typeWrappers.h"
|
||||||
|
#include "harness/extensionHelpers.h"
|
||||||
|
#include "harness/errorHelpers.h"
|
||||||
|
#include "directx_wrapper.hpp"
|
||||||
|
|
||||||
|
class CLDXSemaphoreWrapper {
|
||||||
|
public:
|
||||||
|
CLDXSemaphoreWrapper(cl_device_id device, cl_context context,
|
||||||
|
ID3D12Device* dx_device)
|
||||||
|
: device(device), context(context), dx_device(dx_device){};
|
||||||
|
|
||||||
|
int createSemaphoreFromFence(ID3D12Fence* fence)
|
||||||
|
{
|
||||||
|
cl_int errcode = CL_SUCCESS;
|
||||||
|
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
|
||||||
|
const HRESULT hr = dx_device->CreateSharedHandle(
|
||||||
|
fence, nullptr, GENERIC_ALL, nullptr, &fence_handle);
|
||||||
|
test_error(FAILED(hr), "Failed to get shared handle from D3D12 fence");
|
||||||
|
|
||||||
|
cl_semaphore_properties_khr sem_props[] = {
|
||||||
|
static_cast<cl_semaphore_properties_khr>(CL_SEMAPHORE_TYPE_KHR),
|
||||||
|
static_cast<cl_semaphore_properties_khr>(
|
||||||
|
CL_SEMAPHORE_TYPE_BINARY_KHR),
|
||||||
|
static_cast<cl_semaphore_properties_khr>(
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
reinterpret_cast<cl_semaphore_properties_khr>(fence_handle), 0
|
||||||
|
};
|
||||||
|
semaphore =
|
||||||
|
clCreateSemaphoreWithPropertiesKHR(context, sem_props, &errcode);
|
||||||
|
test_error(errcode, "Could not create semaphore");
|
||||||
|
|
||||||
|
return CL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
~CLDXSemaphoreWrapper()
|
||||||
|
{
|
||||||
|
releaseSemaphore();
|
||||||
|
if (fence_handle)
|
||||||
|
{
|
||||||
|
CloseHandle(fence_handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cl_semaphore_khr* operator&() const { return &semaphore; };
|
||||||
|
cl_semaphore_khr operator*() const { return semaphore; };
|
||||||
|
|
||||||
|
HANDLE getHandle() const { return fence_handle; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
cl_semaphore_khr semaphore;
|
||||||
|
ComPtr<ID3D12Fence> fence;
|
||||||
|
HANDLE fence_handle;
|
||||||
|
cl_device_id device;
|
||||||
|
cl_context context;
|
||||||
|
ComPtr<ID3D12Device> dx_device;
|
||||||
|
|
||||||
|
int releaseSemaphore() const
|
||||||
|
{
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
|
||||||
|
if (semaphore)
|
||||||
|
{
|
||||||
|
clReleaseSemaphoreKHR(semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CL_SUCCESS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_import_handle_available(cl_device_id device,
|
||||||
|
const cl_external_memory_handle_type_khr handle_type)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
size_t import_types_size = 0;
|
||||||
|
errcode =
|
||||||
|
clGetDeviceInfo(device, CL_DEVICE_SEMAPHORE_IMPORT_HANDLE_TYPES_KHR, 0,
|
||||||
|
nullptr, &import_types_size);
|
||||||
|
if (errcode != CL_SUCCESS)
|
||||||
|
{
|
||||||
|
log_error("Could not query import semaphore handle types");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::vector<cl_external_semaphore_handle_type_khr> import_types(
|
||||||
|
import_types_size / sizeof(cl_external_semaphore_handle_type_khr));
|
||||||
|
errcode =
|
||||||
|
clGetDeviceInfo(device, CL_DEVICE_SEMAPHORE_IMPORT_HANDLE_TYPES_KHR,
|
||||||
|
import_types_size, import_types.data(), nullptr);
|
||||||
|
if (errcode != CL_SUCCESS)
|
||||||
|
{
|
||||||
|
log_error("Could not query import semaphore handle types");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::find(import_types.begin(), import_types.end(), handle_type)
|
||||||
|
!= import_types.end();
|
||||||
|
}
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2025 The Khronos Group Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "semaphore_dx_fence_base.h"
|
||||||
|
|
||||||
|
// Confirm that a signal followed by a wait in OpenCL will complete successfully
|
||||||
|
REGISTER_TEST(test_external_semaphores_signal_wait)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
const DirectXWrapper dx_wrapper;
|
||||||
|
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore");
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore_dx_fence");
|
||||||
|
|
||||||
|
// Obtain pointers to semaphore's API
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
GET_PFN(device, clEnqueueSignalSemaphoresKHR);
|
||||||
|
GET_PFN(device, clEnqueueWaitSemaphoresKHR);
|
||||||
|
|
||||||
|
test_error(!is_import_handle_available(device,
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
"Could not find CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR between the "
|
||||||
|
"supported import types");
|
||||||
|
|
||||||
|
// Import D3D12 fence into OpenCL
|
||||||
|
const DirectXFenceWrapper fence(dx_wrapper.getDXDevice());
|
||||||
|
CLDXSemaphoreWrapper semaphore(device, context, dx_wrapper.getDXDevice());
|
||||||
|
test_error(semaphore.createSemaphoreFromFence(*fence),
|
||||||
|
"Could not create semaphore");
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueSignalSemaphoresKHR\n");
|
||||||
|
constexpr cl_semaphore_payload_khr semaphore_payload = 1;
|
||||||
|
clEventWrapper signal_event;
|
||||||
|
errcode = clEnqueueSignalSemaphoresKHR(
|
||||||
|
queue, 1, &semaphore, &semaphore_payload, 0, nullptr, &signal_event);
|
||||||
|
test_error(errcode, "Failed to signal semaphore");
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueWaitSemaphoresKHR\n");
|
||||||
|
clEventWrapper wait_event;
|
||||||
|
errcode = clEnqueueWaitSemaphoresKHR(
|
||||||
|
queue, 1, &semaphore, &semaphore_payload, 0, nullptr, &wait_event);
|
||||||
|
test_error(errcode, "Failed to wait semaphore");
|
||||||
|
|
||||||
|
errcode = clFinish(queue);
|
||||||
|
test_error(errcode, "Could not finish queue");
|
||||||
|
|
||||||
|
// Verify that the events completed.
|
||||||
|
test_assert_event_complete(signal_event);
|
||||||
|
test_assert_event_complete(wait_event);
|
||||||
|
|
||||||
|
return TEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm that a wait in OpenCL followed by a CPU signal in DX12 will complete
|
||||||
|
// successfully
|
||||||
|
REGISTER_TEST(test_external_semaphores_signal_dx_cpu)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
const DirectXWrapper dx_wrapper;
|
||||||
|
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore");
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore_dx_fence");
|
||||||
|
|
||||||
|
// Obtain pointers to semaphore's API
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
GET_PFN(device, clEnqueueSignalSemaphoresKHR);
|
||||||
|
GET_PFN(device, clEnqueueWaitSemaphoresKHR);
|
||||||
|
|
||||||
|
test_error(!is_import_handle_available(device,
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
"Could not find CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR between the "
|
||||||
|
"supported import types");
|
||||||
|
|
||||||
|
// Import D3D12 fence into OpenCL
|
||||||
|
const DirectXFenceWrapper fence(dx_wrapper.getDXDevice());
|
||||||
|
CLDXSemaphoreWrapper semaphore(device, context, dx_wrapper.getDXDevice());
|
||||||
|
test_error(semaphore.createSemaphoreFromFence(*fence),
|
||||||
|
"Could not create semaphore");
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueWaitSemaphoresKHR\n");
|
||||||
|
constexpr cl_semaphore_payload_khr semaphore_payload = 1;
|
||||||
|
clEventWrapper wait_event;
|
||||||
|
errcode = clEnqueueWaitSemaphoresKHR(
|
||||||
|
queue, 1, &semaphore, &semaphore_payload, 0, nullptr, &wait_event);
|
||||||
|
test_error(errcode, "Failed to call clEnqueueWaitSemaphoresKHR");
|
||||||
|
|
||||||
|
log_info("Calling d3d12_fence->Signal()\n");
|
||||||
|
const HRESULT hr = (*fence)->Signal(semaphore_payload);
|
||||||
|
test_error(FAILED(hr), "Failed to signal D3D12 fence");
|
||||||
|
|
||||||
|
errcode = clFinish(queue);
|
||||||
|
test_error(errcode, "Could not finish queue");
|
||||||
|
|
||||||
|
test_assert_event_complete(wait_event);
|
||||||
|
|
||||||
|
return TEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm that a wait in OpenCL followed by a GPU signal in DX12 will complete
|
||||||
|
// successfully
|
||||||
|
REGISTER_TEST(test_external_semaphores_signal_dx_gpu)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
const DirectXWrapper dx_wrapper;
|
||||||
|
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore");
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore_dx_fence");
|
||||||
|
|
||||||
|
// Obtain pointers to semaphore's API
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
GET_PFN(device, clEnqueueSignalSemaphoresKHR);
|
||||||
|
GET_PFN(device, clEnqueueWaitSemaphoresKHR);
|
||||||
|
|
||||||
|
test_error(!is_import_handle_available(device,
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
"Could not find CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR between the "
|
||||||
|
"supported import types");
|
||||||
|
|
||||||
|
// Import D3D12 fence into OpenCL
|
||||||
|
const DirectXFenceWrapper fence(dx_wrapper.getDXDevice());
|
||||||
|
CLDXSemaphoreWrapper semaphore(device, context, dx_wrapper.getDXDevice());
|
||||||
|
test_error(semaphore.createSemaphoreFromFence(*fence),
|
||||||
|
"Could not create semaphore");
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueWaitSemaphoresKHR\n");
|
||||||
|
constexpr cl_semaphore_payload_khr semaphore_payload = 1;
|
||||||
|
clEventWrapper wait_event;
|
||||||
|
errcode = clEnqueueWaitSemaphoresKHR(
|
||||||
|
queue, 1, &semaphore, &semaphore_payload, 0, nullptr, &wait_event);
|
||||||
|
test_error(errcode, "Failed to call clEnqueueWaitSemaphoresKHR");
|
||||||
|
|
||||||
|
log_info("Calling d3d12_command_queue->Signal()\n");
|
||||||
|
const HRESULT hr =
|
||||||
|
dx_wrapper.getDXCommandQueue()->Signal(*fence, semaphore_payload);
|
||||||
|
test_error(FAILED(hr), "Failed to signal D3D12 fence");
|
||||||
|
|
||||||
|
errcode = clFinish(queue);
|
||||||
|
test_error(errcode, "Could not finish queue");
|
||||||
|
|
||||||
|
test_assert_event_complete(wait_event);
|
||||||
|
|
||||||
|
return TEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm that interlocking waits between OpenCL and DX12 will complete
|
||||||
|
// successfully
|
||||||
|
REGISTER_TEST(test_external_semaphores_cl_dx_interlock)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
const DirectXWrapper dx_wrapper;
|
||||||
|
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore");
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore_dx_fence");
|
||||||
|
|
||||||
|
// Obtain pointers to semaphore's API
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
GET_PFN(device, clEnqueueSignalSemaphoresKHR);
|
||||||
|
GET_PFN(device, clEnqueueWaitSemaphoresKHR);
|
||||||
|
|
||||||
|
test_error(!is_import_handle_available(device,
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
"Could not find CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR between the "
|
||||||
|
"supported import types");
|
||||||
|
|
||||||
|
// Import D3D12 fence into OpenCL
|
||||||
|
const DirectXFenceWrapper fence(dx_wrapper.getDXDevice());
|
||||||
|
CLDXSemaphoreWrapper semaphore(device, context, dx_wrapper.getDXDevice());
|
||||||
|
test_error(semaphore.createSemaphoreFromFence(*fence),
|
||||||
|
"Could not create semaphore");
|
||||||
|
|
||||||
|
log_info("Calling d3d12_command_queue->Wait(1)\n");
|
||||||
|
cl_semaphore_payload_khr semaphore_payload = 1;
|
||||||
|
HRESULT hr =
|
||||||
|
dx_wrapper.getDXCommandQueue()->Wait(*fence, semaphore_payload);
|
||||||
|
test_error(FAILED(hr), "Failed to wait on D3D12 fence");
|
||||||
|
|
||||||
|
log_info("Calling d3d12_command_queue->Signal(2)\n");
|
||||||
|
hr = dx_wrapper.getDXCommandQueue()->Signal(*fence, semaphore_payload + 1);
|
||||||
|
test_error(FAILED(hr), "Failed to signal D3D12 fence");
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueSignalSemaphoresKHR(1)\n");
|
||||||
|
clEventWrapper signal_event;
|
||||||
|
errcode = clEnqueueSignalSemaphoresKHR(
|
||||||
|
queue, 1, &semaphore, &semaphore_payload, 0, nullptr, &signal_event);
|
||||||
|
test_error(errcode, "Failed to call clEnqueueSignalSemaphoresKHR");
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueWaitSemaphoresKHR(2)\n");
|
||||||
|
semaphore_payload += 1;
|
||||||
|
clEventWrapper wait_event;
|
||||||
|
errcode = clEnqueueWaitSemaphoresKHR(
|
||||||
|
queue, 1, &semaphore, &semaphore_payload, 0, nullptr, &wait_event);
|
||||||
|
test_error(errcode, "Failed to call clEnqueueWaitSemaphoresKHR");
|
||||||
|
|
||||||
|
errcode = clFinish(queue);
|
||||||
|
test_error(errcode, "Could not finish queue");
|
||||||
|
|
||||||
|
test_assert_event_complete(wait_event);
|
||||||
|
test_assert_event_complete(signal_event);
|
||||||
|
|
||||||
|
return TEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm that multiple waits in OpenCL followed by signals in DX12 and waits
|
||||||
|
// in DX12 followed by signals in OpenCL complete successfully
|
||||||
|
REGISTER_TEST(test_external_semaphores_multiple_wait_signal)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
const DirectXWrapper dx_wrapper;
|
||||||
|
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore");
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore_dx_fence");
|
||||||
|
|
||||||
|
// Obtain pointers to semaphore's API
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
GET_PFN(device, clEnqueueSignalSemaphoresKHR);
|
||||||
|
GET_PFN(device, clEnqueueWaitSemaphoresKHR);
|
||||||
|
|
||||||
|
test_error(!is_import_handle_available(device,
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
"Could not find CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR between the "
|
||||||
|
"supported import types");
|
||||||
|
|
||||||
|
// Import D3D12 fence into OpenCL
|
||||||
|
const DirectXFenceWrapper fence_1(dx_wrapper.getDXDevice());
|
||||||
|
CLDXSemaphoreWrapper semaphore_1(device, context, dx_wrapper.getDXDevice());
|
||||||
|
test_error(semaphore_1.createSemaphoreFromFence(*fence_1),
|
||||||
|
"Could not create semaphore");
|
||||||
|
|
||||||
|
const DirectXFenceWrapper fence_2(dx_wrapper.getDXDevice());
|
||||||
|
CLDXSemaphoreWrapper semaphore_2(device, context, dx_wrapper.getDXDevice());
|
||||||
|
test_error(semaphore_2.createSemaphoreFromFence(*fence_2),
|
||||||
|
"Could not create semaphore");
|
||||||
|
|
||||||
|
const cl_semaphore_khr semaphore_list[] = { *semaphore_1, *semaphore_2 };
|
||||||
|
constexpr cl_semaphore_payload_khr semaphore_payload = 1;
|
||||||
|
cl_semaphore_payload_khr semaphore_payload_list[] = {
|
||||||
|
semaphore_payload, semaphore_payload + 1
|
||||||
|
};
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueWaitSemaphoresKHR\n");
|
||||||
|
clEventWrapper wait_event;
|
||||||
|
errcode = clEnqueueWaitSemaphoresKHR(queue, 2, semaphore_list,
|
||||||
|
semaphore_payload_list, 0, nullptr,
|
||||||
|
&wait_event);
|
||||||
|
test_error(errcode, "Failed to call clEnqueueWaitSemaphoresKHR");
|
||||||
|
|
||||||
|
log_info("Calling d3d12_command_queue->Signal()\n");
|
||||||
|
HRESULT hr =
|
||||||
|
dx_wrapper.getDXCommandQueue()->Signal(*fence_2, semaphore_payload + 1);
|
||||||
|
test_error(FAILED(hr), "Failed to signal D3D12 fence 2");
|
||||||
|
hr = dx_wrapper.getDXCommandQueue()->Signal(*fence_1, semaphore_payload);
|
||||||
|
test_error(FAILED(hr), "Failed to signal D3D12 fence 1");
|
||||||
|
|
||||||
|
log_info("Calling d3d12_command_queue->Wait() with different payloads\n");
|
||||||
|
hr = dx_wrapper.getDXCommandQueue()->Wait(*fence_1, semaphore_payload + 3);
|
||||||
|
test_error(FAILED(hr), "Failed to wait on D3D12 fence 1");
|
||||||
|
hr = dx_wrapper.getDXCommandQueue()->Wait(*fence_2, semaphore_payload + 2);
|
||||||
|
test_error(FAILED(hr), "Failed to wait on D3D12 fence 2");
|
||||||
|
|
||||||
|
errcode = clFinish(queue);
|
||||||
|
test_error(errcode, "Could not finish queue");
|
||||||
|
|
||||||
|
test_assert_event_complete(wait_event);
|
||||||
|
|
||||||
|
semaphore_payload_list[0] = semaphore_payload + 3;
|
||||||
|
semaphore_payload_list[1] = semaphore_payload + 2;
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueSignalSemaphoresKHR\n");
|
||||||
|
clEventWrapper signal_event;
|
||||||
|
errcode = clEnqueueSignalSemaphoresKHR(queue, 2, semaphore_list,
|
||||||
|
semaphore_payload_list, 0, nullptr,
|
||||||
|
&signal_event);
|
||||||
|
test_error(errcode, "Could not call clEnqueueSignalSemaphoresKHR");
|
||||||
|
|
||||||
|
// Wait until the GPU has completed commands up to this fence point.
|
||||||
|
log_info("Waiting for D3D12 command queue completion\n");
|
||||||
|
if ((*fence_1)->GetCompletedValue() < semaphore_payload_list[0])
|
||||||
|
{
|
||||||
|
const HANDLE event_handle =
|
||||||
|
CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
|
||||||
|
hr = (*fence_1)->SetEventOnCompletion(semaphore_payload_list[0],
|
||||||
|
event_handle);
|
||||||
|
test_error(FAILED(hr),
|
||||||
|
"Failed to set D3D12 fence 1 event on completion");
|
||||||
|
WaitForSingleObject(event_handle, INFINITE);
|
||||||
|
CloseHandle(event_handle);
|
||||||
|
}
|
||||||
|
if ((*fence_2)->GetCompletedValue() < semaphore_payload_list[1])
|
||||||
|
{
|
||||||
|
const HANDLE event_handle =
|
||||||
|
CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
|
||||||
|
hr = (*fence_2)->SetEventOnCompletion(semaphore_payload_list[1],
|
||||||
|
event_handle);
|
||||||
|
test_error(FAILED(hr),
|
||||||
|
"Failed to set D3D12 fence 2 event on completion");
|
||||||
|
WaitForSingleObject(event_handle, INFINITE);
|
||||||
|
CloseHandle(event_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
errcode = clFinish(queue);
|
||||||
|
test_error(errcode, "Could not finish queue");
|
||||||
|
|
||||||
|
test_assert_event_complete(signal_event);
|
||||||
|
|
||||||
|
return TEST_PASS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2025 The Khronos Group Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "semaphore_dx_fence_base.h"
|
||||||
|
|
||||||
|
// Confirm that a wait followed by a signal in DirectX 12 using an exported
|
||||||
|
// semaphore will complete successfully
|
||||||
|
REGISTER_TEST(test_external_semaphores_export_dx_signal)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
const DirectXWrapper dx_wrapper;
|
||||||
|
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore");
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore_dx_fence");
|
||||||
|
|
||||||
|
// Obtain pointers to semaphore's API
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
GET_PFN(device, clEnqueueSignalSemaphoresKHR);
|
||||||
|
GET_PFN(device, clEnqueueWaitSemaphoresKHR);
|
||||||
|
GET_PFN(device, clGetSemaphoreInfoKHR);
|
||||||
|
GET_PFN(device, clGetSemaphoreHandleForTypeKHR);
|
||||||
|
|
||||||
|
size_t export_types_size = 0;
|
||||||
|
errcode =
|
||||||
|
clGetDeviceInfo(device, CL_DEVICE_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR, 0,
|
||||||
|
nullptr, &export_types_size);
|
||||||
|
test_error(errcode, "Could not query export semaphore handle types");
|
||||||
|
std::vector<cl_external_semaphore_handle_type_khr> export_types(
|
||||||
|
export_types_size / sizeof(cl_external_semaphore_handle_type_khr));
|
||||||
|
errcode =
|
||||||
|
clGetDeviceInfo(device, CL_DEVICE_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR,
|
||||||
|
export_types_size, export_types.data(), nullptr);
|
||||||
|
test_error(errcode, "Could not query export semaphore handle types");
|
||||||
|
|
||||||
|
if (std::find(export_types.begin(), export_types.end(),
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR)
|
||||||
|
== export_types.end())
|
||||||
|
{
|
||||||
|
log_info("Could not find CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR between "
|
||||||
|
"the supported export types\n");
|
||||||
|
return TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr cl_semaphore_properties_khr sem_props[] = {
|
||||||
|
static_cast<cl_semaphore_properties_khr>(CL_SEMAPHORE_TYPE_KHR),
|
||||||
|
static_cast<cl_semaphore_properties_khr>(CL_SEMAPHORE_TYPE_BINARY_KHR),
|
||||||
|
static_cast<cl_semaphore_properties_khr>(
|
||||||
|
CL_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR),
|
||||||
|
static_cast<cl_semaphore_properties_khr>(
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
static_cast<cl_semaphore_properties_khr>(
|
||||||
|
CL_SEMAPHORE_EXPORT_HANDLE_TYPES_LIST_END_KHR),
|
||||||
|
0
|
||||||
|
};
|
||||||
|
cl_semaphore_khr semaphore =
|
||||||
|
clCreateSemaphoreWithPropertiesKHR(context, sem_props, &errcode);
|
||||||
|
test_error(errcode, "Could not create semaphore");
|
||||||
|
|
||||||
|
cl_bool is_exportable = CL_FALSE;
|
||||||
|
errcode =
|
||||||
|
clGetSemaphoreInfoKHR(semaphore, CL_SEMAPHORE_EXPORTABLE_KHR,
|
||||||
|
sizeof(is_exportable), &is_exportable, nullptr);
|
||||||
|
test_error(errcode, "Could not get semaphore info");
|
||||||
|
test_error(!is_exportable, "Semaphore is not exportable");
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueWaitSemaphoresKHR\n");
|
||||||
|
constexpr cl_semaphore_payload_khr semaphore_payload = 1;
|
||||||
|
clEventWrapper wait_event;
|
||||||
|
errcode = clEnqueueWaitSemaphoresKHR(
|
||||||
|
queue, 1, &semaphore, &semaphore_payload, 0, nullptr, &wait_event);
|
||||||
|
test_error(errcode, "Failed to wait semaphore");
|
||||||
|
|
||||||
|
HANDLE semaphore_handle = nullptr;
|
||||||
|
errcode = clGetSemaphoreHandleForTypeKHR(
|
||||||
|
semaphore, device, CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR,
|
||||||
|
sizeof(semaphore_handle), &semaphore_handle, nullptr);
|
||||||
|
test_error(errcode, "Could not get semaphore handle");
|
||||||
|
|
||||||
|
ID3D12Fence *fence = nullptr;
|
||||||
|
errcode = dx_wrapper.getDXDevice()->OpenSharedHandle(semaphore_handle,
|
||||||
|
IID_PPV_ARGS(&fence));
|
||||||
|
test_error(errcode, "Could not open semaphore handle");
|
||||||
|
|
||||||
|
log_info("Calling fence->Signal()\n");
|
||||||
|
const HRESULT hr = fence->Signal(semaphore_payload);
|
||||||
|
test_error(FAILED(hr), "Failed to signal D3D12 fence");
|
||||||
|
|
||||||
|
errcode = clFinish(queue);
|
||||||
|
test_error(errcode, "Could not finish queue");
|
||||||
|
|
||||||
|
test_assert_event_complete(wait_event);
|
||||||
|
|
||||||
|
// Release resources
|
||||||
|
CloseHandle(semaphore_handle);
|
||||||
|
test_error(clReleaseSemaphoreKHR(semaphore), "Could not release semaphore");
|
||||||
|
fence->Release();
|
||||||
|
|
||||||
|
return TEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm that a signal in OpenCL followed by a wait in DirectX 12 using an
|
||||||
|
// exported semaphore will complete successfully
|
||||||
|
REGISTER_TEST(test_external_semaphores_export_dx_wait)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
const DirectXWrapper dx_wrapper;
|
||||||
|
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore");
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore_dx_fence");
|
||||||
|
|
||||||
|
// Obtain pointers to semaphore's API
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
GET_PFN(device, clEnqueueSignalSemaphoresKHR);
|
||||||
|
GET_PFN(device, clEnqueueWaitSemaphoresKHR);
|
||||||
|
GET_PFN(device, clGetSemaphoreInfoKHR);
|
||||||
|
GET_PFN(device, clGetSemaphoreHandleForTypeKHR);
|
||||||
|
|
||||||
|
size_t export_types_size = 0;
|
||||||
|
errcode =
|
||||||
|
clGetDeviceInfo(device, CL_DEVICE_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR, 0,
|
||||||
|
nullptr, &export_types_size);
|
||||||
|
test_error(errcode, "Could not query export semaphore handle types");
|
||||||
|
std::vector<cl_external_semaphore_handle_type_khr> export_types(
|
||||||
|
export_types_size / sizeof(cl_external_semaphore_handle_type_khr));
|
||||||
|
errcode =
|
||||||
|
clGetDeviceInfo(device, CL_DEVICE_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR,
|
||||||
|
export_types_size, export_types.data(), nullptr);
|
||||||
|
test_error(errcode, "Could not query export semaphore handle types");
|
||||||
|
|
||||||
|
if (std::find(export_types.begin(), export_types.end(),
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR)
|
||||||
|
== export_types.end())
|
||||||
|
{
|
||||||
|
log_info("Could not find CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR between "
|
||||||
|
"the supported export types\n");
|
||||||
|
return TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr cl_semaphore_properties_khr sem_props[] = {
|
||||||
|
static_cast<cl_semaphore_properties_khr>(CL_SEMAPHORE_TYPE_KHR),
|
||||||
|
static_cast<cl_semaphore_properties_khr>(CL_SEMAPHORE_TYPE_BINARY_KHR),
|
||||||
|
static_cast<cl_semaphore_properties_khr>(
|
||||||
|
CL_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR),
|
||||||
|
static_cast<cl_semaphore_properties_khr>(
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
static_cast<cl_semaphore_properties_khr>(
|
||||||
|
CL_SEMAPHORE_EXPORT_HANDLE_TYPES_LIST_END_KHR),
|
||||||
|
0
|
||||||
|
};
|
||||||
|
cl_semaphore_khr semaphore =
|
||||||
|
clCreateSemaphoreWithPropertiesKHR(context, sem_props, &errcode);
|
||||||
|
test_error(errcode, "Could not create semaphore");
|
||||||
|
|
||||||
|
cl_bool is_exportable = CL_FALSE;
|
||||||
|
errcode =
|
||||||
|
clGetSemaphoreInfoKHR(semaphore, CL_SEMAPHORE_EXPORTABLE_KHR,
|
||||||
|
sizeof(is_exportable), &is_exportable, nullptr);
|
||||||
|
test_error(errcode, "Could not get semaphore info");
|
||||||
|
test_error(!is_exportable, "Semaphore is not exportable");
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueSignalSemaphoresKHR\n");
|
||||||
|
constexpr cl_semaphore_payload_khr semaphore_payload = 1;
|
||||||
|
clEventWrapper signal_event;
|
||||||
|
errcode = clEnqueueSignalSemaphoresKHR(
|
||||||
|
queue, 1, &semaphore, &semaphore_payload, 0, nullptr, &signal_event);
|
||||||
|
test_error(errcode, "Failed to signal semaphore");
|
||||||
|
|
||||||
|
HANDLE semaphore_handle = nullptr;
|
||||||
|
errcode = clGetSemaphoreHandleForTypeKHR(
|
||||||
|
semaphore, device, CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR,
|
||||||
|
sizeof(semaphore_handle), &semaphore_handle, nullptr);
|
||||||
|
test_error(errcode, "Could not get semaphore handle");
|
||||||
|
|
||||||
|
ID3D12Fence *fence = nullptr;
|
||||||
|
errcode = dx_wrapper.getDXDevice()->OpenSharedHandle(semaphore_handle,
|
||||||
|
IID_PPV_ARGS(&fence));
|
||||||
|
test_error(errcode, "Could not open semaphore handle");
|
||||||
|
|
||||||
|
log_info("Calling dx_wrapper.get_d3d12_command_queue()->Wait()\n");
|
||||||
|
HRESULT hr = dx_wrapper.getDXCommandQueue()->Wait(fence, semaphore_payload);
|
||||||
|
test_error(FAILED(hr), "Failed to wait on D3D12 fence");
|
||||||
|
|
||||||
|
log_info("Calling WaitForSingleObject\n");
|
||||||
|
if (fence->GetCompletedValue() < semaphore_payload)
|
||||||
|
{
|
||||||
|
const HANDLE event =
|
||||||
|
CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
|
||||||
|
hr = fence->SetEventOnCompletion(semaphore_payload, event);
|
||||||
|
test_error(FAILED(hr), "Failed to set event on completion");
|
||||||
|
WaitForSingleObject(event, INFINITE);
|
||||||
|
CloseHandle(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
errcode = clFinish(queue);
|
||||||
|
test_error(errcode, "Could not finish queue");
|
||||||
|
|
||||||
|
test_assert_event_complete(signal_event);
|
||||||
|
|
||||||
|
// Release resources
|
||||||
|
CloseHandle(semaphore_handle);
|
||||||
|
test_error(clReleaseSemaphoreKHR(semaphore), "Could not release semaphore");
|
||||||
|
fence->Release();
|
||||||
|
|
||||||
|
return TEST_PASS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2025 The Khronos Group Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "semaphore_dx_fence_base.h"
|
||||||
|
|
||||||
|
// Confirm that a wait without a semaphore payload list will return
|
||||||
|
// CL_INVALID_VALUE
|
||||||
|
REGISTER_TEST(test_external_semaphores_dx_fence_negative_wait)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
const DirectXWrapper dx_wrapper;
|
||||||
|
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore");
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore_dx_fence");
|
||||||
|
|
||||||
|
// Obtain pointers to semaphore's API
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
GET_PFN(device, clEnqueueWaitSemaphoresKHR);
|
||||||
|
|
||||||
|
test_error(!is_import_handle_available(device,
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
"Could not find CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR between the "
|
||||||
|
"supported import types");
|
||||||
|
|
||||||
|
// Import D3D12 fence into OpenCL
|
||||||
|
const DirectXFenceWrapper fence(dx_wrapper.getDXDevice());
|
||||||
|
CLDXSemaphoreWrapper semaphore(device, context, dx_wrapper.getDXDevice());
|
||||||
|
test_error(semaphore.createSemaphoreFromFence(*fence),
|
||||||
|
"Could not create semaphore");
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueWaitSemaphoresKHR\n");
|
||||||
|
errcode = clEnqueueWaitSemaphoresKHR(queue, 1, &semaphore, nullptr, 0,
|
||||||
|
nullptr, nullptr);
|
||||||
|
test_assert_error(
|
||||||
|
errcode == CL_INVALID_VALUE,
|
||||||
|
"Unexpected error code returned from clEnqueueWaitSemaphores");
|
||||||
|
|
||||||
|
return TEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm that a signal without a semaphore payload list will return
|
||||||
|
// CL_INVALID_VALUE
|
||||||
|
REGISTER_TEST(test_external_semaphores_dx_fence_negative_signal)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
const DirectXWrapper dx_wrapper;
|
||||||
|
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore");
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore_dx_fence");
|
||||||
|
|
||||||
|
// Obtain pointers to semaphore's API
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
GET_PFN(device, clEnqueueSignalSemaphoresKHR);
|
||||||
|
|
||||||
|
test_error(!is_import_handle_available(device,
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
"Could not find CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR between the "
|
||||||
|
"supported import types");
|
||||||
|
|
||||||
|
// Import D3D12 fence into OpenCL
|
||||||
|
const DirectXFenceWrapper fence(dx_wrapper.getDXDevice());
|
||||||
|
CLDXSemaphoreWrapper semaphore(device, context, dx_wrapper.getDXDevice());
|
||||||
|
test_error(semaphore.createSemaphoreFromFence(*fence),
|
||||||
|
"Could not create semaphore");
|
||||||
|
|
||||||
|
log_info("Calling clEnqueueWaitSemaphoresKHR\n");
|
||||||
|
errcode = clEnqueueSignalSemaphoresKHR(queue, 1, &semaphore, nullptr, 0,
|
||||||
|
nullptr, nullptr);
|
||||||
|
test_assert_error(
|
||||||
|
errcode == CL_INVALID_VALUE,
|
||||||
|
"Unexpected error code returned from clEnqueueSignalSemaphores");
|
||||||
|
|
||||||
|
return TEST_PASS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2025 The Khronos Group Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "semaphore_dx_fence_base.h"
|
||||||
|
|
||||||
|
// Confirm that the CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR property is in the
|
||||||
|
// properties returned by clGetSemaphoreInfo
|
||||||
|
REGISTER_TEST(test_external_semaphores_dx_fence_query_properties)
|
||||||
|
{
|
||||||
|
int errcode = CL_SUCCESS;
|
||||||
|
const DirectXWrapper dx_wrapper;
|
||||||
|
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore");
|
||||||
|
REQUIRE_EXTENSION("cl_khr_external_semaphore_dx_fence");
|
||||||
|
|
||||||
|
// Obtain pointers to semaphore's API
|
||||||
|
GET_PFN(device, clCreateSemaphoreWithPropertiesKHR);
|
||||||
|
GET_PFN(device, clReleaseSemaphoreKHR);
|
||||||
|
GET_PFN(device, clGetSemaphoreInfoKHR);
|
||||||
|
|
||||||
|
test_error(!is_import_handle_available(device,
|
||||||
|
CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR),
|
||||||
|
"Could not find CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR between the "
|
||||||
|
"supported import types");
|
||||||
|
|
||||||
|
// Import D3D12 fence into OpenCL
|
||||||
|
const DirectXFenceWrapper fence(dx_wrapper.getDXDevice());
|
||||||
|
CLDXSemaphoreWrapper semaphore(device, context, dx_wrapper.getDXDevice());
|
||||||
|
test_error(semaphore.createSemaphoreFromFence(*fence),
|
||||||
|
"Could not create semaphore");
|
||||||
|
|
||||||
|
size_t properties_size_bytes = 0;
|
||||||
|
errcode = clGetSemaphoreInfoKHR(*semaphore, CL_SEMAPHORE_PROPERTIES_KHR, 0,
|
||||||
|
nullptr, &properties_size_bytes);
|
||||||
|
test_error(errcode, "Could not get semaphore info");
|
||||||
|
std::vector<cl_semaphore_properties_khr> semaphore_properties(
|
||||||
|
properties_size_bytes / sizeof(cl_semaphore_properties_khr));
|
||||||
|
errcode = clGetSemaphoreInfoKHR(*semaphore, CL_SEMAPHORE_PROPERTIES_KHR,
|
||||||
|
properties_size_bytes,
|
||||||
|
semaphore_properties.data(), nullptr);
|
||||||
|
test_error(errcode, "Could not get semaphore info");
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < semaphore_properties.size() - 1; i++)
|
||||||
|
{
|
||||||
|
if (semaphore_properties[i] == CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR
|
||||||
|
&& semaphore_properties[i + 1]
|
||||||
|
== reinterpret_cast<cl_semaphore_properties_khr>(
|
||||||
|
semaphore.getHandle()))
|
||||||
|
{
|
||||||
|
return TEST_PASS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_error(
|
||||||
|
"Failed to find the dx fence handle type in the semaphore properties");
|
||||||
|
return TEST_FAIL;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user