From c2baafae3f75e2a42e58fbbc0e01ae04597b4d9d Mon Sep 17 00:00:00 2001 From: Marcin Hajder Date: Tue, 16 Sep 2025 17:45:22 +0200 Subject: [PATCH 01/33] Added test to verify negative result of clSetKernelArg with CL_INVALID_ARG_SIZE (#2448) Related to #2282, according to work plan related to CL_INVALID_ARG_SIZE result --- test_conformance/api/test_kernels.cpp | 82 ++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/test_conformance/api/test_kernels.cpp b/test_conformance/api/test_kernels.cpp index 3c156d87..1b8ed1cf 100644 --- a/test_conformance/api/test_kernels.cpp +++ b/test_conformance/api/test_kernels.cpp @@ -1,6 +1,6 @@ // // Copyright (c) 2017 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 @@ -16,6 +16,8 @@ #include "testBase.h" #include "harness/typeWrappers.h" #include "harness/conversions.h" +#include "harness/stringHelpers.h" +#include #include const char *sample_single_test_kernel[] = { @@ -117,6 +119,14 @@ const char *sample_write_only_image_test_kernel = R"( } )"; +const char *sample_arg_size_test_kernel = R"( + %s + __kernel void arg_size_test(%s src, __global %s *dst) + { + dst[0]=src; + } +)"; + REGISTER_TEST(get_kernel_info) { int error; @@ -734,6 +744,76 @@ REGISTER_TEST(negative_set_immutable_memory_to_writeable_kernel_arg) return TEST_PASS; } +REGISTER_TEST(negative_invalid_arg_size) +{ + std::vector exp_types = { kChar, kUChar, kShort, kUShort, + kInt, kUInt, kLong, kULong, + kFloat, kHalf, kDouble }; + + bool fp16_supported = is_extension_available(device, "cl_khr_fp16"); + bool fp64_supported = is_extension_available(device, "cl_khr_fp64"); + + for (unsigned int type_num = 0; type_num < exp_types.size(); type_num++) + { + auto type = exp_types[type_num]; + + if ((type == kLong || type == kULong) && !gHasLong) + continue; + else if (type == kDouble && !fp64_supported) + continue; + else if (type == kHalf && !fp16_supported) + continue; + else if (strchr(get_explicit_type_name(type), ' ') != 0) + continue; + + std::array sizes = { 1, 2, 4, 8, 16 }; + std::vector buf(sizeof(cl_ulong16), 0); + for (unsigned i = 0; i < sizes.size(); i++) + { + clProgramWrapper program; + clKernelWrapper kernel; + + size_t destStride = get_explicit_type_size(type) * sizes[i]; + + std::ostringstream vecNameStr; + vecNameStr << get_explicit_type_name(type); + if (sizes[i] != 1) vecNameStr << sizes[i]; + + std::string ext_str; + if (type == kDouble) + ext_str = "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"; + + if (type == kHalf) + ext_str = "#pragma OPENCL EXTENSION cl_khr_fp16 : enable\n"; + + auto vt_name = vecNameStr.str(); + std::string program_source = + str_sprintf(std::string(sample_arg_size_test_kernel), + ext_str.c_str(), vt_name.c_str(), vt_name.c_str()); + + const char *ptr = program_source.c_str(); + cl_int error = create_single_kernel_helper( + context, &program, &kernel, 1, &ptr, "arg_size_test"); + test_error(error, "Unable to build test program!"); + + // Run the test + size_t reduced = destStride / 2; + error = clSetKernelArg(kernel, 0, reduced, buf.data()); + if (error != CL_INVALID_ARG_SIZE) + { + std::stringstream sstr; + sstr << "clSetKernelArg is supposed to fail " + "with CL_INVALID_ARG_SIZE with type " + << vecNameStr.str() << " and sizeof " << reduced + << std::endl; + log_error("%s", sstr.str().c_str()); + return TEST_FAIL; + } + } + } + return TEST_PASS; +} + REGISTER_TEST(negative_invalid_arg_mem_obj) { cl_int error = CL_SUCCESS; From 7412973dc9c45b60a7233cbf9714babc53db559a Mon Sep 17 00:00:00 2001 From: Jose Lopez Date: Tue, 16 Sep 2025 18:41:49 +0100 Subject: [PATCH 02/33] 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 --- CMakeLists.txt | 1 + test_conformance/CMakeLists.txt | 3 + .../common/directx_wrapper/CMakeLists.txt | 13 + .../directx_wrapper/directx_wrapper.cpp | 71 ++++ .../directx_wrapper/directx_wrapper.hpp | 47 +++ test_conformance/extensions/CMakeLists.txt | 3 + .../CMakeLists.txt | 26 ++ .../main.cpp | 22 ++ .../semaphore_dx_fence_base.h | 117 +++++++ .../test_external_semaphore_dx_fence.cpp | 324 ++++++++++++++++++ ...est_external_semaphore_dx_fence_export.cpp | 220 ++++++++++++ ...emaphore_dx_fence_negative_wait_signal.cpp | 89 +++++ ...st_external_semaphore_dx_fence_queries.cpp | 69 ++++ 13 files changed, 1005 insertions(+) create mode 100644 test_conformance/common/directx_wrapper/CMakeLists.txt create mode 100644 test_conformance/common/directx_wrapper/directx_wrapper.cpp create mode 100644 test_conformance/common/directx_wrapper/directx_wrapper.hpp create mode 100644 test_conformance/extensions/cl_khr_external_semaphore_dx_fence/CMakeLists.txt create mode 100644 test_conformance/extensions/cl_khr_external_semaphore_dx_fence/main.cpp create mode 100644 test_conformance/extensions/cl_khr_external_semaphore_dx_fence/semaphore_dx_fence_base.h create mode 100644 test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence.cpp create mode 100644 test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_export.cpp create mode 100644 test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_negative_wait_signal.cpp create mode 100644 test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_queries.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 90c343fc..5c0481ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ endif(USE_CL_EXPERIMENTAL) #----------------------------------------------------------- option(D3D10_IS_SUPPORTED "Run DirectX 10 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(GLES_IS_SUPPORTED "Run OpenGL ES interop tests" OFF) option(VULKAN_IS_SUPPORTED "Run Vulkan interop tests" OFF) diff --git a/test_conformance/CMakeLists.txt b/test_conformance/CMakeLists.txt index 1f1970af..d0e44961 100644 --- a/test_conformance/CMakeLists.txt +++ b/test_conformance/CMakeLists.txt @@ -56,6 +56,9 @@ if(VULKAN_IS_SUPPORTED) add_subdirectory( common/vulkan_wrapper ) add_subdirectory( vulkan ) endif() +if(D3D12_IS_SUPPORTED) + add_subdirectory(common/directx_wrapper) +endif () file(GLOB CSV_FILES "opencl_conformance_tests_*.csv") diff --git a/test_conformance/common/directx_wrapper/CMakeLists.txt b/test_conformance/common/directx_wrapper/CMakeLists.txt new file mode 100644 index 00000000..58ae19a8 --- /dev/null +++ b/test_conformance/common/directx_wrapper/CMakeLists.txt @@ -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 () diff --git a/test_conformance/common/directx_wrapper/directx_wrapper.cpp b/test_conformance/common/directx_wrapper/directx_wrapper.cpp new file mode 100644 index 00000000..2255a541 --- /dev/null +++ b/test_conformance/common/directx_wrapper/directx_wrapper.cpp @@ -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"); + } +} diff --git a/test_conformance/common/directx_wrapper/directx_wrapper.hpp b/test_conformance/common/directx_wrapper/directx_wrapper.hpp new file mode 100644 index 00000000..dec85b78 --- /dev/null +++ b/test_conformance/common/directx_wrapper/directx_wrapper.hpp @@ -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 +#include +#include + +using namespace Microsoft::WRL; + +class DirectXWrapper { +public: + DirectXWrapper(); + + ID3D12Device* getDXDevice() const; + ID3D12CommandQueue* getDXCommandQueue() const; + ID3D12CommandAllocator* getDXCommandAllocator() const; + +protected: + ComPtr dx_device = nullptr; + ComPtr dx_command_queue = nullptr; + ComPtr dx_command_allocator = nullptr; +}; + +class DirectXFenceWrapper { +public: + DirectXFenceWrapper(ID3D12Device* dx_device); + ID3D12Fence* operator*() const { return dx_fence.Get(); } + +private: + ComPtr dx_fence = nullptr; + ComPtr dx_device = nullptr; +}; \ No newline at end of file diff --git a/test_conformance/extensions/CMakeLists.txt b/test_conformance/extensions/CMakeLists.txt index 2fee828a..a2af536e 100644 --- a/test_conformance/extensions/CMakeLists.txt +++ b/test_conformance/extensions/CMakeLists.txt @@ -15,3 +15,6 @@ add_subdirectory( cl_ext_buffer_device_address ) if(VULKAN_IS_SUPPORTED) add_subdirectory( cl_khr_external_semaphore ) endif() +if(D3D12_IS_SUPPORTED) + add_subdirectory( cl_khr_external_semaphore_dx_fence ) +endif() \ No newline at end of file diff --git a/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/CMakeLists.txt b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/CMakeLists.txt new file mode 100644 index 00000000..08b03b42 --- /dev/null +++ b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/CMakeLists.txt @@ -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) + diff --git a/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/main.cpp b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/main.cpp new file mode 100644 index 00000000..85c8fc7f --- /dev/null +++ b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/main.cpp @@ -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); +} \ No newline at end of file diff --git a/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/semaphore_dx_fence_base.h b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/semaphore_dx_fence_base.h new file mode 100644 index 00000000..f8ccb570 --- /dev/null +++ b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/semaphore_dx_fence_base.h @@ -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_TYPE_KHR), + static_cast( + CL_SEMAPHORE_TYPE_BINARY_KHR), + static_cast( + CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR), + reinterpret_cast(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 fence; + HANDLE fence_handle; + cl_device_id device; + cl_context context; + ComPtr 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 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(); +} \ No newline at end of file diff --git a/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence.cpp b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence.cpp new file mode 100644 index 00000000..569fb204 --- /dev/null +++ b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence.cpp @@ -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; +} \ No newline at end of file diff --git a/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_export.cpp b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_export.cpp new file mode 100644 index 00000000..c54cf61b --- /dev/null +++ b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_export.cpp @@ -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 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_TYPE_KHR), + static_cast(CL_SEMAPHORE_TYPE_BINARY_KHR), + static_cast( + CL_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR), + static_cast( + CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR), + static_cast( + 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 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_TYPE_KHR), + static_cast(CL_SEMAPHORE_TYPE_BINARY_KHR), + static_cast( + CL_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR), + static_cast( + CL_SEMAPHORE_HANDLE_D3D12_FENCE_KHR), + static_cast( + 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; +} \ No newline at end of file diff --git a/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_negative_wait_signal.cpp b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_negative_wait_signal.cpp new file mode 100644 index 00000000..6c032c56 --- /dev/null +++ b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_negative_wait_signal.cpp @@ -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; +} \ No newline at end of file diff --git a/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_queries.cpp b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_queries.cpp new file mode 100644 index 00000000..03aa2b15 --- /dev/null +++ b/test_conformance/extensions/cl_khr_external_semaphore_dx_fence/test_external_semaphore_dx_fence_queries.cpp @@ -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 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( + semaphore.getHandle())) + { + return TEST_PASS; + } + } + log_error( + "Failed to find the dx fence handle type in the semaphore properties"); + return TEST_FAIL; +} \ No newline at end of file From aa950deaf193ae9e695992af0372134b34fc848c Mon Sep 17 00:00:00 2001 From: Marcin Hajder Date: Wed, 17 Sep 2025 17:14:30 +0200 Subject: [PATCH 03/33] Added test to verify negative result of clSetKernelArg with CL_INVALID_ARG_SIZE and sampler argument (#2452) Related to #2282, according to work plan from [here](https://github.com/KhronosGroup/OpenCL-CTS/issues/2282#issuecomment-3069182773) --- test_conformance/api/test_kernels.cpp | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test_conformance/api/test_kernels.cpp b/test_conformance/api/test_kernels.cpp index 1b8ed1cf..063dfb97 100644 --- a/test_conformance/api/test_kernels.cpp +++ b/test_conformance/api/test_kernels.cpp @@ -89,6 +89,16 @@ const char *sample_two_kernel_program[] = { "\n" "}\n" }; +const char *sample_sampler_size_test_kernel = R"( + __kernel void sampler_size_test(sampler_t sampler, __read_only image2d_t src, __global float4 *dst) + { + int tid = get_global_id(0); + int2 coord = (int2)(get_global_id(0), get_global_id(1)); + float4 data = read_imagef(src, sampler, coord); + dst[tid] = data; + } +)"; + const char *sample_mem_obj_size_test_kernel = R"( __kernel void mem_obj_size_test(__global int *src, __global int *dst) { @@ -744,6 +754,48 @@ REGISTER_TEST(negative_set_immutable_memory_to_writeable_kernel_arg) return TEST_PASS; } +REGISTER_TEST(negative_invalid_arg_sampler) +{ + PASSIVE_REQUIRE_IMAGE_SUPPORT(device) + + cl_int error = CL_SUCCESS; + clProgramWrapper program; + clKernelWrapper sampler_arg_kernel; + + // Setup the test + error = + create_single_kernel_helper(context, &program, nullptr, 1, + &sample_sampler_size_test_kernel, nullptr); + test_error(error, "Unable to build test program"); + + sampler_arg_kernel = clCreateKernel(program, "sampler_size_test", &error); + test_error(error, + "Unable to get sampler_size_test kernel for built program"); + + clSamplerWrapper sampler = clCreateSampler( + context, CL_FALSE, CL_ADDRESS_NONE, CL_FILTER_NEAREST, &error); + test_error(error, "Unable to create sampler"); + + // Run the test - CL_INVALID_ARG_SIZE + error = + clSetKernelArg(sampler_arg_kernel, 0, sizeof(cl_sampler) * 2, &sampler); + test_failure_error_ret( + error, CL_INVALID_ARG_SIZE, + "clSetKernelArg is supposed to fail with CL_INVALID_ARG_SIZE when " + "argument is a sampler object and arg_size > sizeof(cl_sampler)", + TEST_FAIL); + + error = + clSetKernelArg(sampler_arg_kernel, 0, sizeof(cl_sampler) / 2, &sampler); + test_failure_error_ret( + error, CL_INVALID_ARG_SIZE, + "clSetKernelArg is supposed to fail with CL_INVALID_ARG_SIZE when " + "argument is a sampler object and arg_size < sizeof(cl_sampler)", + TEST_FAIL); + + return TEST_PASS; +} + REGISTER_TEST(negative_invalid_arg_size) { std::vector exp_types = { kChar, kUChar, kShort, kUShort, From 4e8aa9d3e23acc1d04ffa6638a60c81661a11196 Mon Sep 17 00:00:00 2001 From: Marcin Hajder Date: Wed, 17 Sep 2025 18:25:12 +0200 Subject: [PATCH 04/33] Added test to verify negative result of clSetKernelArg with CL_INVALID_SAMPLER and sampler argument (#2453) Related to #2282, according to work plan from [here](https://github.com/KhronosGroup/OpenCL-CTS/issues/2282#issuecomment-3069182773) --- test_conformance/api/test_kernels.cpp | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test_conformance/api/test_kernels.cpp b/test_conformance/api/test_kernels.cpp index 063dfb97..47100b7a 100644 --- a/test_conformance/api/test_kernels.cpp +++ b/test_conformance/api/test_kernels.cpp @@ -758,6 +758,36 @@ REGISTER_TEST(negative_invalid_arg_sampler) { PASSIVE_REQUIRE_IMAGE_SUPPORT(device) + cl_int error = CL_SUCCESS; + + clProgramWrapper program; + clKernelWrapper sampler_arg_kernel; + + // Setup the test + error = + create_single_kernel_helper(context, &program, nullptr, 1, + &sample_sampler_size_test_kernel, nullptr); + test_error(error, "Unable to build test program"); + + sampler_arg_kernel = clCreateKernel(program, "sampler_size_test", &error); + test_error(error, + "Unable to get sampler_size_test kernel for built program"); + + // Run the test - CL_INVALID_SAMPLER + error = clSetKernelArg(sampler_arg_kernel, 0, sizeof(cl_sampler), nullptr); + test_failure_error_ret( + error, CL_INVALID_SAMPLER, + "clSetKernelArg is supposed to fail with CL_INVALID_SAMPLER when " + "argument is declared to be of type sampler_t and the specified " + "arg_value is not a valid sampler object", + TEST_FAIL); + return TEST_PASS; +} + +REGISTER_TEST(negative_invalid_arg_sampler_size) +{ + PASSIVE_REQUIRE_IMAGE_SUPPORT(device) + cl_int error = CL_SUCCESS; clProgramWrapper program; clKernelWrapper sampler_arg_kernel; From 217ba80e32243d4210a802a6168bec3a8be73617 Mon Sep 17 00:00:00 2001 From: Ben Ashbaugh Date: Wed, 17 Sep 2025 11:04:53 -0700 Subject: [PATCH 05/33] invoke the python executable explicitly to assemble SPIR-V files (#2528) On some platforms (e.g. Windows), we need to explicitly invoke the python executable when assembling SPIR-V files. --- test_conformance/compiler/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test_conformance/compiler/CMakeLists.txt b/test_conformance/compiler/CMakeLists.txt index 498c6218..0b82d661 100644 --- a/test_conformance/compiler/CMakeLists.txt +++ b/test_conformance/compiler/CMakeLists.txt @@ -1,5 +1,7 @@ set(MODULE_NAME COMPILER) +find_package(Python3 COMPONENTS Interpreter QUIET) + set(${MODULE_NAME}_SOURCES main.cpp test_build_helpers.cpp @@ -52,7 +54,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E copy_directory ${CLConform_SOURCE_DIR}/test_conformance/compiler/secondIncludeTestDirectory ${COMPILER_TEST_RESOURCES}/secondIncludeTestDirectory - COMMAND ${COMPILER_ASSEMBLY_SCRIPT} --source-dir "${COMPILER_ASM_PATH}" --output-dir "${COMPILER_SPV_PATH}" ${COMPILER_SPV_EXTRA} --verbose + COMMAND ${Python3_EXECUTABLE} ${COMPILER_ASSEMBLY_SCRIPT} --source-dir "${COMPILER_ASM_PATH}" --output-dir "${COMPILER_SPV_PATH}" ${COMPILER_SPV_EXTRA} --verbose DEPENDS ${COMPILER_ASSEMBLY_SCRIPT} ${COMPILER_ASM} VERBATIM) From ac673e5e8cc7d7558c5b532ce73201e510d9ecf7 Mon Sep 17 00:00:00 2001 From: Ahmed Hesham <117350656+ahesham-arm@users.noreply.github.com> Date: Wed, 24 Sep 2025 10:28:33 +0100 Subject: [PATCH 06/33] Fix file paths in test_compiler (#2523) Add a command line argument to override the SPIR-V file paths if needed, similar to `test_spirv_new`. Set the default path to the one required by `run_conformance.py`, which assumes the current working directory to be `/test_conformance`. Signed-off-by: Ahmed Hesham --- test_conformance/compiler/main.cpp | 52 +++++++++++++++++++ .../test_unload_platform_compiler.cpp | 6 +-- ...est_unload_platform_compiler_resources.hpp | 4 +- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/test_conformance/compiler/main.cpp b/test_conformance/compiler/main.cpp index 20a62f26..e4c6ade7 100644 --- a/test_conformance/compiler/main.cpp +++ b/test_conformance/compiler/main.cpp @@ -13,10 +13,62 @@ // See the License for the specific language governing permissions and // limitations under the License. // +#include +#include + #include "harness/testHarness.h" +std::string spvBinariesPath = + (std::filesystem::path("compiler") / "spirv_bin").u8string(); +const std::string spvBinariesPathArg = "--spirv-binaries-path"; + +void printUsage() +{ + log_info("Reading SPIR-V files from default '%s' path.\n", + spvBinariesPath.c_str()); + log_info("In case you want to set other directory use '%s' argument.\n", + spvBinariesPathArg.c_str()); +} + int main(int argc, const char *argv[]) { + bool modifiedSpvBinariesPath = false; + bool listTests = false; + for (int i = 0; i < argc; ++i) + { + int argsRemoveNum = 0; + if (argv[i] == spvBinariesPathArg) + { + if (i + 1 == argc) + { + log_error("Missing value for '%s' argument.\n", + spvBinariesPathArg.c_str()); + return TEST_FAIL; + } + else + { + spvBinariesPath = std::string(argv[i + 1]); + argsRemoveNum += 2; + modifiedSpvBinariesPath = true; + } + } + + if (argsRemoveNum > 0) + { + for (int j = i; j < (argc - argsRemoveNum); ++j) + argv[j] = argv[j + argsRemoveNum]; + + argc -= argsRemoveNum; + --i; + } + listTests |= (argv[i] == std::string("--list") + || argv[i] == std::string("-list")); + } + if (modifiedSpvBinariesPath == false && !listTests) + { + printUsage(); + } + return runTestHarness(argc, argv, test_registry::getInstance().num_tests(), test_registry::getInstance().definitions(), false, 0); } diff --git a/test_conformance/compiler/test_unload_platform_compiler.cpp b/test_conformance/compiler/test_unload_platform_compiler.cpp index bb41f64d..521dea11 100644 --- a/test_conformance/compiler/test_unload_platform_compiler.cpp +++ b/test_conformance/compiler/test_unload_platform_compiler.cpp @@ -32,8 +32,6 @@ const std::string slash = "\\"; #else const std::string slash = "/"; #endif -std::string compilerSpvBinaries = "test_conformance" + slash + "compiler" - + slash + "spirv_bin" + slash + "write_kernel.spv"; const std::string spvExt = ".spv"; @@ -338,8 +336,8 @@ public: std::vector kernel_buffer; - std::string file_name = - compilerSpvBinaries + std::to_string(address_bits); + std::string file_name = spvBinariesPath + slash + "write_kernel.spv" + + std::to_string(address_bits); m_spirv_binary = readBinary(file_name.c_str()); m_spirv_size = m_spirv_binary.size(); } diff --git a/test_conformance/compiler/test_unload_platform_compiler_resources.hpp b/test_conformance/compiler/test_unload_platform_compiler_resources.hpp index a529c212..029cbf4b 100644 --- a/test_conformance/compiler/test_unload_platform_compiler_resources.hpp +++ b/test_conformance/compiler/test_unload_platform_compiler_resources.hpp @@ -1,4 +1,6 @@ -#include +#include + +extern std::string spvBinariesPath; static const char write_kernel_source[] = R"( kernel void write_kernel(global unsigned int *p) { From e92140f82d0a5f4f93e92a29be5bf6c9df9e298c Mon Sep 17 00:00:00 2001 From: David Neto Date: Wed, 1 Oct 2025 16:40:43 -0400 Subject: [PATCH 07/33] Add helper to wrap result of std::filesystem::path::u8string() (#2535) In C++17 the return type of std::filesystem::path::u8string() is std::string, but in C++20 the return type changed to std::u8string. Add a helper to copy a std::u8string to a std::string, to be used when a std::string is required. This fixes the build for C++20. --- test_common/harness/stringHelpers.h | 16 ++++++++++++++++ test_conformance/compiler/main.cpp | 3 ++- test_conformance/compiler/test_compile.cpp | 10 ++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/test_common/harness/stringHelpers.h b/test_common/harness/stringHelpers.h index e1275f10..5f474bb1 100644 --- a/test_common/harness/stringHelpers.h +++ b/test_common/harness/stringHelpers.h @@ -39,4 +39,20 @@ inline std::string str_sprintf(const std::string &str, Args... args) return std::string(buffer.get(), buffer.get() + s - 1); } +// Returns the argument, converted to std::string. +// The return type of std::filesystem::path::u8string() was +// std::string in C++17, but became std::u8string in C++20. +// Use this method to wrap the result when a std::string +// is desired. +// +// Use a template with a specialization for std::string, +// so the generic template applies when std::u8string exists +// and is used. +template +inline std::string to_string(const STRING_TYPE &str) +{ + return std::string(str.begin(), str.end()); +} +inline std::string to_string(const std::string &str) { return str; } + #endif // STRING_HELPERS_H diff --git a/test_conformance/compiler/main.cpp b/test_conformance/compiler/main.cpp index e4c6ade7..3845f2fd 100644 --- a/test_conformance/compiler/main.cpp +++ b/test_conformance/compiler/main.cpp @@ -17,9 +17,10 @@ #include #include "harness/testHarness.h" +#include "harness/stringHelpers.h" std::string spvBinariesPath = - (std::filesystem::path("compiler") / "spirv_bin").u8string(); + to_string((std::filesystem::path("compiler") / "spirv_bin").u8string()); const std::string spvBinariesPathArg = "--spirv-binaries-path"; void printUsage() diff --git a/test_conformance/compiler/test_compile.cpp b/test_conformance/compiler/test_compile.cpp index 70ca9449..6e6e5f53 100644 --- a/test_conformance/compiler/test_compile.cpp +++ b/test_conformance/compiler/test_compile.cpp @@ -24,6 +24,7 @@ #include #endif #include "harness/conversions.h" +#include "harness/stringHelpers.h" #define MAX_LINE_SIZE_IN_PROGRAM 1024 #define MAX_LOG_SIZE_IN_PROGRAM 2048 @@ -3059,12 +3060,13 @@ REGISTER_TEST(execute_after_included_header_link) } const auto simple_header_path = temp_dir_path / simple_header_name; - FILE *simple_header_file = - fopen(simple_header_path.u8string().c_str(), "w"); + const std::string simple_header_path_str = + to_string(simple_header_path.u8string()); + FILE *simple_header_file = fopen(simple_header_path_str.c_str(), "w"); if (simple_header_file == NULL) { log_error("ERROR: Unable to create simple header file %s! (in %s:%d)\n", - simple_header_path.u8string().c_str(), __FILE__, __LINE__); + simple_header_path_str.c_str(), __FILE__, __LINE__); return -1; } if (fprintf(simple_header_file, "%s", simple_header) < 0) @@ -3082,7 +3084,7 @@ REGISTER_TEST(execute_after_included_header_link) } const std::string include_path = - std::string("-I") + temp_dir_path.generic_u8string(); + std::string("-I") + to_string(temp_dir_path.generic_u8string()); error = clCompileProgram(program, 1, &device, include_path.c_str(), 0, NULL, NULL, NULL, NULL); test_error(error, From 93c37f17fc7ce3a819169beec81680eb2f61f034 Mon Sep 17 00:00:00 2001 From: Harald van Dijk Date: Mon, 6 Oct 2025 19:11:22 +0100 Subject: [PATCH 08/33] One last file path fix for test_compiler. (#2534) Although run_conformance.py runs from test_conformance, it will change the current working directory before launching each individual test. Adjust the path accordingly. Co-authored-by: Ben Ashbaugh --- test_conformance/compiler/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test_conformance/compiler/main.cpp b/test_conformance/compiler/main.cpp index 3845f2fd..5d07159b 100644 --- a/test_conformance/compiler/main.cpp +++ b/test_conformance/compiler/main.cpp @@ -19,8 +19,7 @@ #include "harness/testHarness.h" #include "harness/stringHelpers.h" -std::string spvBinariesPath = - to_string((std::filesystem::path("compiler") / "spirv_bin").u8string()); +std::string spvBinariesPath = "spirv_bin"; const std::string spvBinariesPathArg = "--spirv-binaries-path"; void printUsage() From 51445f3743e9026886bb802326c82b77484497a3 Mon Sep 17 00:00:00 2001 From: Marcin Hajder Date: Tue, 7 Oct 2025 17:42:02 +0200 Subject: [PATCH 09/33] Added support for cl_ext_float_atomics in CBaseTestFetchAdd with atomic_double (#2347) Related to https://github.com/KhronosGroup/OpenCL-CTS/issues/2142, according to the work plan, extending CBasicTestFetchAdd with support for atomic_double. --- test_conformance/c11_atomics/common.h | 1 + test_conformance/c11_atomics/host_atomics.h | 5 +- test_conformance/c11_atomics/main.cpp | 8 ++ test_conformance/c11_atomics/test_atomics.cpp | 105 ++++++++++++++---- 4 files changed, 97 insertions(+), 22 deletions(-) diff --git a/test_conformance/c11_atomics/common.h b/test_conformance/c11_atomics/common.h index d321819f..fe2bd37d 100644 --- a/test_conformance/c11_atomics/common.h +++ b/test_conformance/c11_atomics/common.h @@ -79,6 +79,7 @@ extern cl_device_atomic_capabilities gAtomicMemCap, extern cl_half_rounding_mode gHalfRoundingMode; extern bool gFloatAtomicsSupported; extern cl_device_fp_atomic_capabilities_ext gHalfAtomicCaps; +extern cl_device_fp_atomic_capabilities_ext gDoubleAtomicCaps; extern cl_device_fp_atomic_capabilities_ext gFloatAtomicCaps; extern const char * diff --git a/test_conformance/c11_atomics/host_atomics.h b/test_conformance/c11_atomics/host_atomics.h index d9482fb7..aabbfdde 100644 --- a/test_conformance/c11_atomics/host_atomics.h +++ b/test_conformance/c11_atomics/host_atomics.h @@ -99,7 +99,10 @@ template CorrespondingType host_atomic_fetch_add(volatile AtomicType *a, CorrespondingType c, TExplicitMemoryOrderType order) { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + AtomicType, + HOST_ATOMIC_FLOAT> || std::is_same_v) { static std::mutex mx; std::lock_guard lock(mx); diff --git a/test_conformance/c11_atomics/main.cpp b/test_conformance/c11_atomics/main.cpp index 485445f7..78291f06 100644 --- a/test_conformance/c11_atomics/main.cpp +++ b/test_conformance/c11_atomics/main.cpp @@ -34,6 +34,7 @@ cl_device_atomic_capabilities gAtomicMemCap, cl_half_rounding_mode gHalfRoundingMode = CL_HALF_RTE; bool gFloatAtomicsSupported = false; cl_device_fp_atomic_capabilities_ext gHalfAtomicCaps = 0; +cl_device_fp_atomic_capabilities_ext gDoubleAtomicCaps = 0; cl_device_fp_atomic_capabilities_ext gFloatAtomicCaps = 0; test_status InitCL(cl_device_id device) { @@ -133,6 +134,13 @@ test_status InitCL(cl_device_id device) { if (is_extension_available(device, "cl_ext_float_atomics")) { gFloatAtomicsSupported = true; + if (is_extension_available(device, "cl_khr_fp64")) + { + cl_int error = clGetDeviceInfo( + device, CL_DEVICE_DOUBLE_FP_ATOMIC_CAPABILITIES_EXT, + sizeof(gDoubleAtomicCaps), &gDoubleAtomicCaps, nullptr); + test_error_ret(error, "clGetDeviceInfo failed!", TEST_FAIL); + } cl_int error = clGetDeviceInfo( device, CL_DEVICE_SINGLE_FP_ATOMIC_CAPABILITIES_EXT, diff --git a/test_conformance/c11_atomics/test_atomics.cpp b/test_conformance/c11_atomics/test_atomics.cpp index b51f4461..2a99ffa2 100644 --- a/test_conformance/c11_atomics/test_atomics.cpp +++ b/test_conformance/c11_atomics/test_atomics.cpp @@ -1163,13 +1163,30 @@ REGISTER_TEST(svm_atomic_compare_exchange_weak) num_elements, true); } +template double kahan_sum(const std::vector &nums) +{ + return 0.0; +} +template <> double kahan_sum(const std::vector &nums) +{ + double sum = 0.0; + double compensation = 0.0; + for (double num : nums) + { + double y = num - compensation; + double t = sum + y; + compensation = (t - sum) - y; + sum = t; + } + return sum; +} template class CBasicTestFetchAdd : public CBasicTestMemOrderScope { double min_range; double max_range; - double max_error_fp32; + double max_error; std::vector ref_vals; public: @@ -1182,11 +1199,14 @@ public: CBasicTestFetchAdd(TExplicitAtomicType dataType, bool useSVM) : CBasicTestMemOrderScope(dataType, useSVM), - min_range(-999.0), max_range(999.0), max_error_fp32(0.0) + min_range(-999.0), max_range(999.0), max_error(0.0) { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_FLOAT> || std::is_same_v) { - StartValue(0.f); + StartValue((HostDataType)0.0); CBasicTestMemOrderScope::OldValueCheck(false); } @@ -1194,14 +1214,21 @@ public: bool GenerateRefs(cl_uint threadCount, HostDataType *startRefValues, MTdata d) override { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_FLOAT> || std::is_same_v) { if (threadCount > ref_vals.size()) { ref_vals.resize(threadCount); for (cl_uint i = 0; i < threadCount; i++) - ref_vals[i] = get_random_float(min_range, max_range, d); + if constexpr (std::is_same_v) + ref_vals[i] = + get_random_double(min_range, max_range, d); + else + ref_vals[i] = get_random_float(min_range, max_range, d); memcpy(startRefValues, ref_vals.data(), sizeof(HostDataType) * ref_vals.size()); @@ -1216,12 +1243,17 @@ public: sums.push_back( std::accumulate(ref_vals.rbegin(), ref_vals.rend(), 0.f)); - std::sort( - ref_vals.begin(), ref_vals.end(), - [](float a, float b) { return std::abs(a) < std::abs(b); }); + std::sort(ref_vals.begin(), ref_vals.end(), + [](HostDataType a, HostDataType b) { + return std::abs(a) < std::abs(b); + }); double precise = 0.0; - for (auto elem : ref_vals) precise += double(elem); + if constexpr (std::is_same_v) + precise = kahan_sum(ref_vals); + else + for (auto elem : ref_vals) precise += double(elem); + sums.push_back(precise); sums.push_back( @@ -1231,8 +1263,7 @@ public: std::accumulate(ref_vals.rbegin(), ref_vals.rend(), 0.f)); std::sort(sums.begin(), sums.end()); - max_error_fp32 = - std::abs((HOST_ATOMIC_FLOAT)sums.front() - sums.back()); + max_error = std::abs(sums.front() - sums.back()); // restore unsorted order memcpy(ref_vals.data(), startRefValues, @@ -1252,7 +1283,10 @@ public: std::string memoryOrderScope = MemoryOrderScopeStr(); std::string postfix(memoryOrderScope.empty() ? "" : "_explicit"); - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_DOUBLE> || std::is_same_v) { return " atomic_fetch_add" + postfix + "(&destMemory[0], (" + DataType().AddSubOperandTypeName() + ")oldValues[tid]" @@ -1286,7 +1320,10 @@ public: volatile HostAtomicType *destMemory, HostDataType *oldValues) override { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_DOUBLE> || std::is_same_v) { host_atomic_fetch_add(&destMemory[0], (HostDataType)oldValues[tid], MemoryOrder()); @@ -1312,7 +1349,10 @@ public: cl_uint whichDestValue) override { expected = StartValue(); - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_DOUBLE> || std::is_same_v) { if (whichDestValue == 0) for (cl_uint i = 0; i < threadCount; i++) @@ -1331,12 +1371,15 @@ public: const std::vector &testValues, cl_uint whichDestValue) override { - if (std::is_same::value) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_DOUBLE> || std::is_same::value) { if (whichDestValue == 0) - return std::abs((HOST_ATOMIC_FLOAT)expected + return std::abs((HostDataType)expected - testValues[whichDestValue]) - > max_error_fp32; + > max_error; } return CBasicTestMemOrderScope< HostAtomicType, HostDataType>::IsTestNotAsExpected(expected, @@ -1346,7 +1389,8 @@ public: bool VerifyRefs(bool &correct, cl_uint threadCount, HostDataType *refValues, HostAtomicType *finalValues) override { - if (std::is_same::value) + if (std::is_same::value + || std::is_same::value) { correct = true; for (cl_uint i = 1; i < threadCount; i++) @@ -1369,7 +1413,18 @@ public: int ExecuteSingleTest(cl_device_id deviceID, cl_context context, cl_command_queue queue) override { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (LocalMemory() + && (gDoubleAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_ADD_EXT) == 0) + return 0; // skip test - not applicable + + if (!LocalMemory() + && (gDoubleAtomicCaps & CL_DEVICE_GLOBAL_FP_ATOMIC_ADD_EXT) + == 0) + return 0; + } + else if constexpr (std::is_same_v) { if (LocalMemory() && (gFloatAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_ADD_EXT) == 0) @@ -1385,7 +1440,10 @@ public: } cl_uint NumResults(cl_uint threadCount, cl_device_id deviceID) override { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_DOUBLE> || std::is_same_v) { return threadCount; } @@ -1420,6 +1478,11 @@ static int test_atomic_fetch_add_generic(cl_device_id deviceID, if (gFloatAtomicsSupported) { + CBasicTestFetchAdd test_double( + TYPE_ATOMIC_DOUBLE, useSVM); + EXECUTE_TEST( + error, test_double.Execute(deviceID, context, queue, num_elements)); + CBasicTestFetchAdd test_float( TYPE_ATOMIC_FLOAT, useSVM); EXECUTE_TEST( From d733c2b802ff61ece34a5a87c027cb58c185bd13 Mon Sep 17 00:00:00 2001 From: Grzegorz Wawiorko Date: Tue, 14 Oct 2025 17:39:54 +0200 Subject: [PATCH 10/33] c11_atomics: Fix verification loop (#2543) Looks like bug. @shajder Could you look at this fix? Thanks --- test_conformance/c11_atomics/test_atomics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_conformance/c11_atomics/test_atomics.cpp b/test_conformance/c11_atomics/test_atomics.cpp index 2a99ffa2..9a0a306d 100644 --- a/test_conformance/c11_atomics/test_atomics.cpp +++ b/test_conformance/c11_atomics/test_atomics.cpp @@ -2799,7 +2799,7 @@ public: correct = true; for (cl_uint i = 1; i < threadCount; i++) { - for (cl_uint i = 1; i < threadCount; i++) + if (refValues[i] != StartValue()) { log_error("Thread %d found %d mismatch(es)\n", i, (cl_uint)refValues[i]); From 38711492082c4d5a5f338b1df90b4588e9d4f3b3 Mon Sep 17 00:00:00 2001 From: Yilong Guo Date: Tue, 14 Oct 2025 23:47:59 +0800 Subject: [PATCH 11/33] commonfns: Fix max_error initialization and improve test output (#2500) Before this change, `max_error` was initialized to `0.0f`, which caused issues when the actual maximum `error` was also `0.0f`. In such cases, `max_val` would never be updated, leading to misleading test output showing `NaN` values: ``` degrees: Max error 0.000000 ulps at 0: *nan vs 0x1.9802318e8abefp+21 ``` This fix initializes `max_error` to `-INFINITY` to ensure `max_val` is always updated at least once. Additionally, the log output now includes the input value for better debugging: ``` degrees: Max error 0.000000 ulps at 0, input 0x1.c7bffcp+15: *0x1.9802318e8abefp+21 vs 0x1.9802318e8abefp+21 ``` This makes the test output more informative and eliminates confusing `NaN` values in the summary. --- test_conformance/commonfns/test_unary_fn.cpp | 34 ++++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/test_conformance/commonfns/test_unary_fn.cpp b/test_conformance/commonfns/test_unary_fn.cpp index 6515adf7..599067d2 100644 --- a/test_conformance/commonfns/test_unary_fn.cpp +++ b/test_conformance/commonfns/test_unary_fn.cpp @@ -57,7 +57,7 @@ namespace { template int verify_degrees(const T *const inptr, const T *const outptr, int n) { - float error, max_error = 0.0f; + float error, max_error = -INFINITY; double r, max_val = NAN; int max_index = 0; @@ -89,13 +89,16 @@ int verify_degrees(const T *const inptr, const T *const outptr, int n) } if (std::is_same::value) - log_info("degrees: Max error %f ulps at %d: *%a vs %a (*%g vs %g)\n", - max_error, max_index, max_val, conv_to_flt(outptr[max_index]), - max_val, conv_to_flt(outptr[max_index])); + log_info("degrees: Max error %f ulps at %d, input %a: *%a vs %a (*%g " + "vs %g)\n", + max_error, max_index, conv_to_flt(inptr[max_index]), max_val, + conv_to_flt(outptr[max_index]), max_val, + conv_to_flt(outptr[max_index])); else - log_info("degrees: Max error %f ulps at %d: *%a vs %a (*%g vs %g)\n", - max_error, max_index, max_val, outptr[max_index], max_val, - outptr[max_index]); + log_info("degrees: Max error %f ulps at %d, input %a: *%a vs %a (*%g " + "vs %g)\n", + max_error, max_index, conv_to_flt(inptr[max_index]), max_val, + outptr[max_index], max_val, outptr[max_index]); return 0; } @@ -103,7 +106,7 @@ int verify_degrees(const T *const inptr, const T *const outptr, int n) template int verify_radians(const T *const inptr, const T *const outptr, int n) { - float error, max_error = 0.0f; + float error, max_error = -INFINITY; double r, max_val = NAN; int max_index = 0; @@ -135,13 +138,16 @@ int verify_radians(const T *const inptr, const T *const outptr, int n) } if (std::is_same::value) - log_info("radians: Max error %f ulps at %d: *%a vs %a (*%g vs %g)\n", - max_error, max_index, max_val, conv_to_flt(outptr[max_index]), - max_val, conv_to_flt(outptr[max_index])); + log_info("radians: Max error %f ulps at %d, input %a: *%a vs %a (*%g " + "vs %g)\n", + max_error, max_index, conv_to_flt(inptr[max_index]), max_val, + conv_to_flt(outptr[max_index]), max_val, + conv_to_flt(outptr[max_index])); else - log_info("radians: Max error %f ulps at %d: *%a vs %a (*%g vs %g)\n", - max_error, max_index, max_val, outptr[max_index], max_val, - outptr[max_index]); + log_info("radians: Max error %f ulps at %d, input %a: *%a vs %a (*%g " + "vs %g)\n", + max_error, max_index, conv_to_flt(inptr[max_index]), max_val, + outptr[max_index], max_val, outptr[max_index]); return 0; } From a31589412f740df74c364b48bd4ccabce44e540b Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Tue, 14 Oct 2025 16:48:20 +0100 Subject: [PATCH 12/33] Implement negative tests for cl_command_queue functions (#2505) This change adds negative tests to `cl_command_queue`-related APIs: - clCreateCommandQueue - clCreateCommandQueueWithProperties - clSetDefaultDeviceCommandQueue - clRetainCommandQueue - clReleaseCommandQueue - clGetCommandQueueInfo - clSetCommandQueueProperty Signed-off-by: Michael Rizkalla Co-authored-by: Chetankumar Mistry Co-authored-by: Ahmed Hesham --- test_conformance/api/negative_queue.cpp | 455 ++++++++++++++++++++---- 1 file changed, 390 insertions(+), 65 deletions(-) diff --git a/test_conformance/api/negative_queue.cpp b/test_conformance/api/negative_queue.cpp index c25b571d..7da68b32 100644 --- a/test_conformance/api/negative_queue.cpp +++ b/test_conformance/api/negative_queue.cpp @@ -16,92 +16,184 @@ #include "testBase.h" #include "harness/typeWrappers.h" +#include + REGISTER_TEST(negative_create_command_queue) { - cl_command_queue_properties device_props = 0; - cl_int error = clGetDeviceInfo(device, CL_DEVICE_QUEUE_PROPERTIES, - sizeof(device_props), &device_props, NULL); - test_error(error, "clGetDeviceInfo for CL_DEVICE_QUEUE_PROPERTIES failed"); + cl_int err = 0; + clCreateCommandQueue(nullptr, device, 0, &err); + test_failure_error_ret( + err, CL_INVALID_CONTEXT, + "clCreateCommandQueue should return CL_INVALID_CONTEXT when: \"context " + "is not a valid context\" using a nullptr", + TEST_FAIL); - // CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE is the only optional property to - // clCreateCommandQueue, CL_QUEUE_PROFILING_ENABLE is mandatory. - const bool out_of_order_device_support = - device_props & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE; - if (out_of_order_device_support) + clCreateCommandQueue(context, nullptr, 0, &err); + test_failure_error_ret( + err, CL_INVALID_DEVICE, + "clCreateCommandQueue should return CL_INVALID_DEVICE when: \"device " + "is not a valid device\" using a nullptr", + TEST_FAIL); + + cl_device_id different_device = GetOpposingDevice(device); + if (different_device && device != different_device) { - // Early return as we can't check correct error is returned for - // unsupported property. - return TEST_PASS; + clCreateCommandQueue(context, different_device, 0, &err); + test_failure_error_ret( + err, CL_INVALID_DEVICE, + "clCreateCommandQueue should return CL_INVALID_DEVICE when: " + "\"device is not associated with context\"", + TEST_FAIL); } - // Try create a command queue with out-of-order property and check return - // code - cl_int test_error = CL_SUCCESS; - clCommandQueueWrapper test_queue = clCreateCommandQueue( - context, device, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &test_error); - + cl_queue_properties invalid_property{ static_cast( + -1) }; + clCreateCommandQueue(context, device, invalid_property, &err); test_failure_error_ret( - test_error, CL_INVALID_QUEUE_PROPERTIES, - "clCreateCommandQueue should return CL_INVALID_QUEUE_PROPERTIES if " - "values specified in properties are valid but are not supported by " - "the " - "device.", + err, CL_INVALID_VALUE, + "clCreateCommandQueue should return CL_INVALID_VALUE when: \"values " + "specified in properties are not valid\"", TEST_FAIL); + + cl_command_queue_properties device_queue_properties = 0; + err = clGetDeviceInfo(device, CL_DEVICE_QUEUE_PROPERTIES, + sizeof(device_queue_properties), + &device_queue_properties, nullptr); + test_error(err, "clGetDeviceInfo"); + cl_command_queue_properties valid_properties[] = { + CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, CL_QUEUE_PROFILING_ENABLE + }; + cl_queue_properties property{ 0 }; + bool missing_property = false; + // Iterate through all possible properties to find one which isn't supported + for (auto prop : valid_properties) + { + if ((device_queue_properties & prop) == 0) + { + missing_property = true; + property = prop; + break; + } + } + // This test can only run when a device does not support a property + if (missing_property) + { + clCreateCommandQueue(context, device, property, &err); + test_failure_error_ret( + err, CL_INVALID_QUEUE_PROPERTIES, + "clCreateCommandQueue should return CL_INVALID_QUEUE_PROPERTIES " + "when: \"values specified in properties are valid but are not " + "supported by the device\"", + TEST_FAIL); + } + return TEST_PASS; } REGISTER_TEST_VERSION(negative_create_command_queue_with_properties, Version(2, 0)) { - cl_command_queue_properties device_props = 0; - cl_int error = clGetDeviceInfo(device, CL_DEVICE_QUEUE_PROPERTIES, - sizeof(device_props), &device_props, NULL); - test_error(error, "clGetDeviceInfo for CL_DEVICE_QUEUE_PROPERTIES failed"); + cl_int err = 0; + clCreateCommandQueueWithProperties(nullptr, device, nullptr, &err); + test_failure_error_ret( + err, CL_INVALID_CONTEXT, + "clCreateCommandQueueWithProperties should return CL_INVALID_CONTEXT " + "when: \"context is not a valid context\" using a nullptr", + TEST_FAIL); - cl_command_queue_properties device_on_host_props = 0; - error = clGetDeviceInfo(device, CL_DEVICE_QUEUE_ON_HOST_PROPERTIES, - sizeof(device_on_host_props), &device_on_host_props, - NULL); - test_error(error, - "clGetDeviceInfo for CL_DEVICE_QUEUE_ON_HOST_PROPERTIES failed"); + clCreateCommandQueueWithProperties(context, nullptr, nullptr, &err); + test_failure_error_ret( + err, CL_INVALID_DEVICE, + "clCreateCommandQueueWithProperties should return CL_INVALID_DEVICE " + "when: \"device is not a valid device\" using a nullptr", + TEST_FAIL); - if (device_on_host_props != device_props) + cl_device_id different_device = GetOpposingDevice(device); + if (different_device && device != different_device) { - log_error( - "ERROR: CL_DEVICE_QUEUE_PROPERTIES and " - "CL_DEVICE_QUEUE_ON_HOST_PROPERTIES properties should match\n"); - return TEST_FAIL; + clCreateCommandQueueWithProperties(context, different_device, nullptr, + &err); + test_failure_error_ret( + err, CL_INVALID_DEVICE, + "clCreateCommandQueueWithProperties should return " + "CL_INVALID_DEVICE when: \"device is not associated with context\"", + TEST_FAIL); } - // CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE is the only optional host-queue - // property to clCreateCommandQueueWithProperties, - // CL_QUEUE_PROFILING_ENABLE is mandatory. - const bool out_of_order_device_support = - device_props & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE; - if (out_of_order_device_support) - { - // Early return as we can't check correct error is returned for - // unsupported property. - return TEST_PASS; - } + cl_queue_properties invalid_property{ static_cast( + -1) }; - // Try create a command queue with out-of-order property and check return - // code - cl_command_queue_properties queue_prop_def[] = { - CL_QUEUE_PROPERTIES, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, 0 + // Depending on the OpenCL Version, there can be up to 2 properties which + // each take values, and the list should be terminated with a 0 + cl_queue_properties properties[] = { invalid_property, invalid_property, 0, + 0, 0 }; + clCreateCommandQueueWithProperties(context, device, properties, &err); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clCreateCommandQueueWithProperties should return CL_INVALID_VALUE " + "when: \"values specified in properties are not valid\"", + TEST_FAIL); + + cl_command_queue_properties device_queue_properties = 0; + err = clGetDeviceInfo(device, CL_DEVICE_QUEUE_PROPERTIES, + sizeof(device_queue_properties), + &device_queue_properties, nullptr); + test_error(err, "clGetDeviceInfo"); + cl_command_queue_properties valid_properties[] = { + CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, CL_QUEUE_PROFILING_ENABLE }; - - cl_int test_error = CL_SUCCESS; - clCommandQueueWrapper test_queue = clCreateCommandQueueWithProperties( - context, device, queue_prop_def, &test_error); - - test_failure_error_ret(test_error, CL_INVALID_QUEUE_PROPERTIES, - "clCreateCommandQueueWithProperties should " - "return CL_INVALID_QUEUE_PROPERTIES if " - "values specified in properties are valid but " - "are not supported by the " - "device.", - TEST_FAIL); + properties[0] = CL_QUEUE_PROPERTIES; + bool missing_property = false; + // Iterate through all possible properties to find one which isn't supported + for (auto property : valid_properties) + { + if ((device_queue_properties & property) == 0) + { + missing_property = true; + properties[1] = property; + break; + } + } + if (missing_property) + { + clCreateCommandQueueWithProperties(context, device, properties, &err); + test_failure_error_ret( + err, CL_INVALID_QUEUE_PROPERTIES, + "clCreateCommandQueueWithProperties should return " + "CL_INVALID_QUEUE_PROPERTIES when: \"values specified in " + "properties are valid but are not supported by the device\"", + TEST_FAIL); + } + else if (get_device_cl_version(device) >= Version(2, 0)) + { + cl_uint max_size = -1; + err = clGetDeviceInfo(device, CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE, + sizeof(max_size), &max_size, nullptr); + test_error(err, "clGetDeviceInfo"); + if (max_size > 0 && max_size < CL_UINT_MAX) + { + properties[0] = CL_QUEUE_PROPERTIES; + properties[1] = + CL_QUEUE_ON_DEVICE | CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE; + properties[2] = CL_QUEUE_SIZE; + properties[3] = max_size + 1; + clCreateCommandQueueWithProperties(context, device, properties, + &err); + if (err != CL_INVALID_VALUE && err != CL_INVALID_QUEUE_PROPERTIES) + { + log_error("ERROR: %s! (Got %s, expected (%s) from %s:%d)\n", + "clCreateCommandQueueWithProperties should return " + "CL_INVALID_VALUE or CL_INVALID_QUEUE_PROPERTIES " + "when: \"values specified in properties are not " + "valid\" using a queue size greather than " + "CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE", + IGetErrorString(err), + "CL_INVALID_VALUE or CL_INVALID_QUEUE_PROPERTIES", + __FILE__, __LINE__); + return TEST_FAIL; + } + } + } return TEST_PASS; } @@ -166,3 +258,236 @@ REGISTER_TEST(negative_create_command_queue_with_properties_khr) TEST_FAIL); return TEST_PASS; } + +REGISTER_TEST_VERSION(negative_set_default_device_command_queue, Version(2, 1)) +{ + cl_int err = 0; + if (get_device_cl_version(device) >= Version(3, 0)) + { + cl_device_device_enqueue_capabilities device_capabilities = 0; + cl_int err = clGetDeviceInfo( + device, CL_DEVICE_DEVICE_ENQUEUE_CAPABILITIES, + sizeof(device_capabilities), &device_capabilities, nullptr); + test_error(err, "clGetDeviceInfo"); + if (((device_capabilities & CL_DEVICE_QUEUE_REPLACEABLE_DEFAULT) == 0) + && ((device_capabilities & CL_DEVICE_QUEUE_SUPPORTED) == 1)) + { + const cl_queue_properties properties[] = { + CL_QUEUE_PROPERTIES, + CL_QUEUE_ON_DEVICE_DEFAULT | CL_QUEUE_ON_DEVICE + | CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, + 0 + }; + clCommandQueueWrapper cmd_queue = + clCreateCommandQueueWithProperties(context, device, properties, + &err); + test_error(err, "clCreateCommandQueueWithProperties"); + err = clSetDefaultDeviceCommandQueue(context, device, cmd_queue); + test_failure_error_ret( + err, CL_INVALID_OPERATION, + "clSetDefaultDeviceCommandQueue should return " + "CL_INVALID_OPERATION when \"device does not support a " + "replaceable default on-device queue\"", + TEST_FAIL); + } + } + + err = clSetDefaultDeviceCommandQueue(nullptr, device, queue); + if (err != CL_INVALID_OPERATION && err != CL_INVALID_CONTEXT) + { + log_error("ERROR: %s! (Got %s, expected (%s) from %s:%d)\n", + "clSetDefaultDeviceCommandQueue should return " + "CL_INVALID_OPERATION or CL_INVALID_CONTEXT when: \"context " + "is not a valid context\" using a nullptr", + IGetErrorString(err), + "CL_INVALID_OPERATION or CL_INVALID_CONTEXT", __FILE__, + __LINE__); + return TEST_FAIL; + } + + err = clSetDefaultDeviceCommandQueue(context, nullptr, queue); + if (err != CL_INVALID_OPERATION && err != CL_INVALID_DEVICE) + { + log_error("ERROR: %s! (Got %s, expected (%s) from %s:%d)\n", + "clSetDefaultDeviceCommandQueue should return " + "CL_INVALID_OPERATION or CL_INVALID_DEVICE when: \"device " + "is not a valid device\" using a nullptr", + IGetErrorString(err), + "CL_INVALID_OPERATION or CL_INVALID_DEVICE", __FILE__, + __LINE__); + return TEST_FAIL; + } + + cl_device_id different_device = GetOpposingDevice(device); + if (different_device && device != different_device) + { + err = clSetDefaultDeviceCommandQueue(context, different_device, queue); + if (err != CL_INVALID_OPERATION && err != CL_INVALID_DEVICE) + { + log_error("ERROR: %s! (Got %s, expected (%s) from %s:%d)\n", + "clSetDefaultDeviceCommandQueue should return " + "CL_INVALID_OPERATION or CL_INVALID_DEVICE when: " + "\"device is not associated with context\"", + IGetErrorString(err), + "CL_INVALID_OPERATION or CL_INVALID_DEVICE", __FILE__, + __LINE__); + return TEST_FAIL; + } + } + err = clSetDefaultDeviceCommandQueue(context, device, nullptr); + if (err != CL_INVALID_OPERATION && err != CL_INVALID_COMMAND_QUEUE) + { + log_error( + "ERROR: %s! (Got %s, expected (%s) from %s:%d)\n", + "clSetDefaultDeviceCommandQueue should return CL_INVALID_OPERATION " + "or CL_INVALID_COMMAND_QUEUE when: \"command_queue is not a valid " + "command-queue for device\" using a nullptr", + IGetErrorString(err), + "CL_INVALID_OPERATION or CL_INVALID_COMMAND_QUEUE", __FILE__, + __LINE__); + return TEST_FAIL; + } + + { + constexpr cl_queue_properties props[] = { + CL_QUEUE_PROPERTIES, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, 0 + }; + clCommandQueueWrapper not_on_device_queue = + clCreateCommandQueueWithProperties(context, device, props, &err); + test_error_fail(err, "clCreateCommandQueueWithProperties failed"); + err = clSetDefaultDeviceCommandQueue(context, device, + not_on_device_queue); + if (err != CL_INVALID_OPERATION && err != CL_INVALID_COMMAND_QUEUE) + { + log_error("ERROR: %s! (Got %s, expected (%s) from %s:%d)\n", + "clSetDefaultDeviceCommandQueue should return " + "CL_INVALID_OPERATION or CL_INVALID_COMMAND_QUEUE when: " + "\"command_queue is not a valid command-queue for " + "device\" using a command queue that is not on device", + IGetErrorString(err), + "CL_INVALID_OPERATION or CL_INVALID_COMMAND_QUEUE", + __FILE__, __LINE__); + } + } + + return TEST_PASS; +} + +REGISTER_TEST(negative_retain_command_queue) +{ + cl_int err = clRetainCommandQueue(nullptr); + test_failure_error_ret( + err, CL_INVALID_COMMAND_QUEUE, + "clRetainCommandQueue should return CL_INVALID_COMMAND_QUEUE when: " + "\"command_queue is not a valid command-queue\" using a nullptr", + TEST_FAIL); + + return TEST_PASS; +} + +REGISTER_TEST(negative_release_command_queue) +{ + cl_int err = clReleaseCommandQueue(nullptr); + test_failure_error_ret( + err, CL_INVALID_COMMAND_QUEUE, + "clReleaseCommandQueue should return CL_INVALID_COMMAND_QUEUE when: " + "\"command_queue is not a valid command-queue\" using a nullptr", + TEST_FAIL); + + return TEST_PASS; +} + +static bool device_supports_on_device_queue(cl_device_id deviceID) +{ + cl_command_queue_properties device_queue_properties = 0; + if (get_device_cl_version(deviceID) >= Version(2, 0)) + { + cl_int err = clGetDeviceInfo( + deviceID, CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES, + sizeof(device_queue_properties), &device_queue_properties, nullptr); + test_error(err, "clGetDeviceInfo"); + return (device_queue_properties + & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE); + } + return false; +} + +REGISTER_TEST(negative_get_command_queue_info) +{ + cl_int err = + clGetCommandQueueInfo(nullptr, CL_QUEUE_CONTEXT, 0, nullptr, nullptr); + test_failure_error_ret( + err, CL_INVALID_COMMAND_QUEUE, + "clGetCommandQueueInfo should return CL_INVALID_COMMAND_QUEUE when: " + "\"command_queue is not a valid command-queue\" using a nullptr", + TEST_FAIL); + + if (device_supports_on_device_queue(device)) + { + const cl_queue_properties properties[] = { + CL_QUEUE_PROPERTIES, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, 0 + }; + cl_int err = CL_INVALID_VALUE; + clCommandQueueWrapper cmd_queue = clCreateCommandQueueWithProperties( + context, device, properties, &err); + test_error(err, "clCreateCommandQueueWithProperties"); + cl_uint queue_size = -1; + err = clGetCommandQueueInfo(cmd_queue, CL_QUEUE_SIZE, + sizeof(queue_size), &queue_size, nullptr); + test_failure_error_ret(err, CL_INVALID_COMMAND_QUEUE, + "clGetCommandQueueInfo should return " + "CL_INVALID_COMMAND_QUEUE when: \"command_queue " + "is not a valid command-queue for param_name\"", + TEST_FAIL); + } + + constexpr cl_command_queue_info invalid_param = -1; + err = clGetCommandQueueInfo(queue, invalid_param, 0, nullptr, nullptr); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clGetCommandQueueInfo should return CL_INVALID_VALUE when: " + "\"param_name is not one of the supported values\"", + TEST_FAIL); + + + cl_uint ref_count = -1; + err = clGetCommandQueueInfo(queue, CL_QUEUE_REFERENCE_COUNT, 0, &ref_count, + nullptr); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clGetCommandQueueInfo should return CL_INVALID_VALUE when: \"size in " + "bytes specified by param_value_size is < size of return type and " + "param_value is not a NULL value\"", + TEST_FAIL); + + return TEST_PASS; +} + +REGISTER_TEST_VERSION(negative_set_command_queue_property, Version(1, 0)) +{ + auto version = get_device_cl_version(device); + if (version >= Version(1, 1)) + { + // Implementations are allowed to return an error for + // non-OpenCL 1.0 devices. In which case, skip the test. + return TEST_SKIPPED_ITSELF; + } + + cl_queue_properties property{ CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE }; + cl_int err = clSetCommandQueueProperty(nullptr, property, CL_TRUE, nullptr); + test_failure_error_ret( + err, CL_INVALID_COMMAND_QUEUE, + "clSetCommandQueueProperty should return CL_INVALID_COMMAND_QUEUE " + "when: \"command_queue is not a valid command-queue\" using a nullptr", + TEST_FAIL); + + property = -1; + err = clSetCommandQueueProperty(queue, property, CL_TRUE, nullptr); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clSetCommandQueueProperty should return CL_INVALID_VALUE when: " + "\"values specified in properties are not valid\"", + TEST_FAIL); + + return TEST_PASS; +} From a0e32ea64f1a27feafe93e30ee6158c8c2f78b6c Mon Sep 17 00:00:00 2001 From: Karol Herbst Date: Tue, 14 Oct 2025 17:50:10 +0200 Subject: [PATCH 13/33] Fix listing tests in gl testing (#2530) --- test_conformance/gl/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_conformance/gl/main.cpp b/test_conformance/gl/main.cpp index 43df1d19..37205589 100644 --- a/test_conformance/gl/main.cpp +++ b/test_conformance/gl/main.cpp @@ -200,7 +200,7 @@ int main(int argc, const char *argv[]) } } - if (argc > 1 && strcmp(argv[1], "-list") == 0) + if (gListTests) { log_info("Available 2.x tests:\n"); for (int i = 0; i < test_num; i++) From eded89cdd6931ea5f2ad01ed8df83d56d99dc04e Mon Sep 17 00:00:00 2001 From: Ahmed <36049290+AhmedAmraniAkdi@users.noreply.github.com> Date: Tue, 14 Oct 2025 16:50:55 +0100 Subject: [PATCH 14/33] add cl_khr_command_buffer_mutable_memory_commands to the list of known extensions (#2531) --- .../compiler/test_compiler_defines_for_extensions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test_conformance/compiler/test_compiler_defines_for_extensions.cpp b/test_conformance/compiler/test_compiler_defines_for_extensions.cpp index c3f3993d..b9cfb608 100644 --- a/test_conformance/compiler/test_compiler_defines_for_extensions.cpp +++ b/test_conformance/compiler/test_compiler_defines_for_extensions.cpp @@ -94,6 +94,7 @@ const char *known_extensions[] = { "cl_khr_external_memory_dma_buf", "cl_khr_command_buffer", "cl_khr_command_buffer_mutable_dispatch", + "cl_khr_command_buffer_mutable_memory_commands", "cl_khr_command_buffer_multi_device", "cl_khr_external_memory_android_hardware_buffer", "cl_khr_unified_svm", From bfa96c77d86c277d83f02513901a910ab83fd8a1 Mon Sep 17 00:00:00 2001 From: Ewan Crawford Date: Tue, 14 Oct 2025 18:15:20 +0200 Subject: [PATCH 15/33] CTS updates for reworked simultaneous use (#2477) Actions test plan from https://github.com/KhronosGroup/OpenCL-CTS/issues/2473 to update CTS tests to reflect changes from cl_khr_command_buffer PR https://github.com/KhronosGroup/OpenCL-Docs/pull/1411 * Adds new test in`command_buffer_pipelined_enqueue.cpp` for multiple enqueues without blocking in-between, but serialized execution. * Removed test for `CL_COMMAND_BUFFER_STATE_PENDING_KHR` state query. * Remove negative test for `clEnqueueCommandBuffer` pending state error. * Simplify `cl_khr_command_buffer` tests that stress simultaneous-use by testing multiple serialized enqueues of the same command-buffer, which doesn't now require the device imultaneous-use capability * Remove simultaneous-use command-buffer creation in base class to off, and require tests do it themselves if they require it. * Rewrite mutable dispatch simultaneous test to test updating both pipelined enqueues, and updating the new definition of simultaneous-use --------- Co-authored-by: Ewan Crawford --- .../cl_khr_command_buffer/CMakeLists.txt | 1 + .../basic_command_buffer.cpp | 42 +- .../basic_command_buffer.h | 13 +- .../mutable_command_basic.h | 12 +- .../mutable_command_iterative_arg_update.cpp | 4 +- .../mutable_command_multiple_dispatches.cpp | 6 +- .../mutable_command_overwrite_update.cpp | 4 +- .../mutable_command_simultaneous.cpp | 665 +++++++++--------- .../command_buffer_event_sync.cpp | 9 +- ...command_buffer_get_command_buffer_info.cpp | 60 +- .../command_buffer_out_of_order.cpp | 181 +---- .../command_buffer_pipelined_enqueue.cpp | 321 +++++++++ .../command_buffer_printf.cpp | 229 +----- .../command_buffer_profiling.cpp | 157 +---- .../command_buffer_queue_substitution.cpp | 138 +--- .../command_buffer_set_kernel_arg.cpp | 118 ++-- .../negative_command_buffer_create.cpp | 16 +- .../negative_command_buffer_enqueue.cpp | 104 --- .../negative_command_buffer_finalize.cpp | 41 +- 19 files changed, 866 insertions(+), 1255 deletions(-) create mode 100644 test_conformance/extensions/cl_khr_command_buffer/command_buffer_pipelined_enqueue.cpp diff --git a/test_conformance/extensions/cl_khr_command_buffer/CMakeLists.txt b/test_conformance/extensions/cl_khr_command_buffer/CMakeLists.txt index a4983da0..9e54fecc 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/CMakeLists.txt +++ b/test_conformance/extensions/cl_khr_command_buffer/CMakeLists.txt @@ -17,6 +17,7 @@ set(${MODULE_NAME}_SOURCES command_buffer_test_barrier.cpp command_buffer_test_event_info.cpp command_buffer_finalize.cpp + command_buffer_pipelined_enqueue.cpp negative_command_buffer_finalize.cpp negative_command_buffer_svm_mem.cpp negative_command_buffer_copy_image.cpp diff --git a/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer.cpp b/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer.cpp index 9c3a402b..667eecf9 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer.cpp @@ -27,9 +27,6 @@ BasicCommandBufferTest::BasicCommandBufferTest(cl_device_id device, : CommandBufferTestBase(device), context(context), queue(nullptr), num_elements(0), simultaneous_use_support(false), out_of_order_support(false), queue_out_of_order_support(false), - // try to use simultaneous path by default - simultaneous_use_requested(true), - // due to simultaneous cases extend buffer size buffer_size_multiplier(1), command_buffer(this) { cl_int error = clRetainCommandQueue(queue); @@ -72,9 +69,8 @@ bool BasicCommandBufferTest::Skip() sizeof(capabilities), &capabilities, NULL); test_error(error, "Unable to query CL_DEVICE_COMMAND_BUFFER_CAPABILITIES_KHR"); - simultaneous_use_support = simultaneous_use_requested - && (capabilities & CL_COMMAND_BUFFER_CAPABILITY_SIMULTANEOUS_USE_KHR) - != 0; + simultaneous_use_support = + (capabilities & CL_COMMAND_BUFFER_CAPABILITY_SIMULTANEOUS_USE_KHR) != 0; out_of_order_support = supported_properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE; device_side_enqueue_support = @@ -167,19 +163,7 @@ cl_int BasicCommandBufferTest::SetUp(int elements) error = SetUpKernelArgs(); test_error(error, "SetUpKernelArgs failed"); - if (simultaneous_use_support) - { - cl_command_buffer_properties_khr properties[3] = { - CL_COMMAND_BUFFER_FLAGS_KHR, CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR, - 0 - }; - command_buffer = - clCreateCommandBufferKHR(1, &queue, properties, &error); - } - else - { - command_buffer = clCreateCommandBufferKHR(1, &queue, nullptr, &error); - } + command_buffer = clCreateCommandBufferKHR(1, &queue, nullptr, &error); test_error(error, "clCreateCommandBufferKHR failed"); return CL_SUCCESS; @@ -192,11 +176,6 @@ cl_int MultiFlagCreationTest::Run() // First try to find multiple flags that are supported by the driver and // device. - if (simultaneous_use_support) - { - flags |= CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR; - } - if (is_extension_available( device, CL_KHR_COMMAND_BUFFER_MULTI_DEVICE_EXTENSION_NAME)) { @@ -207,6 +186,11 @@ cl_int MultiFlagCreationTest::Run() device, CL_KHR_COMMAND_BUFFER_MUTABLE_DISPATCH_EXTENSION_NAME)) { flags |= CL_COMMAND_BUFFER_MUTABLE_KHR; + + if (simultaneous_use_support) + { + flags |= CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR; + } } cl_command_buffer_properties_khr props[] = { CL_COMMAND_BUFFER_FLAGS_KHR, @@ -381,11 +365,6 @@ cl_int ExplicitFlushTest::Run() return CL_SUCCESS; } -bool ExplicitFlushTest::Skip() -{ - return BasicCommandBufferTest::Skip() || !simultaneous_use_support; -} - cl_int InterleavedEnqueueTest::Run() { cl_int error = clCommandNDRangeKernelKHR( @@ -431,11 +410,6 @@ cl_int InterleavedEnqueueTest::Run() return CL_SUCCESS; } -bool InterleavedEnqueueTest::Skip() -{ - return BasicCommandBufferTest::Skip() || !simultaneous_use_support; -} - cl_int EnqueueAndReleaseTest::Run() { cl_int error = clCommandNDRangeKernelKHR( diff --git a/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer.h b/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer.h index 241a08c5..be33d3ad 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer.h +++ b/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer.h @@ -78,8 +78,11 @@ protected: bool queue_out_of_order_support; bool device_side_enqueue_support; - // user request for simultaneous use - bool simultaneous_use_requested; + // Extends size of created 'in_mem' & 'out_mem' buffers, such that the same + // cl_mem buffer can be used across multiple enqueues of a command-buffer. + // Accessed in the kernel at an offset for each enqueue which is passed as + // a kernel parameter through the 'off_mem' buffer. + // See BasicCommandBufferTest::SetUpKernel() definition. unsigned buffer_size_multiplier; clCommandBufferWrapper command_buffer; }; @@ -116,7 +119,6 @@ struct ExplicitFlushTest : public BasicCommandBufferTest using BasicCommandBufferTest::BasicCommandBufferTest; cl_int Run() override; - bool Skip() override; }; // Test enqueueing a command-buffer twice separated by another enqueue operation @@ -125,7 +127,6 @@ struct InterleavedEnqueueTest : public BasicCommandBufferTest using BasicCommandBufferTest::BasicCommandBufferTest; cl_int Run() override; - bool Skip() override; }; // Test releasing a command-buffer after it has been submitted for execution, @@ -156,9 +157,9 @@ int MakeAndRunTest(cl_device_id device, cl_context context, cl_version extension_version = get_extension_version(device, "cl_khr_command_buffer"); - if (extension_version != CL_MAKE_VERSION(0, 9, 7)) + if (extension_version != CL_MAKE_VERSION(0, 9, 8)) { - log_info("cl_khr_command_buffer version 0.9.7 is required to run " + log_info("cl_khr_command_buffer version 0.9.8 is required to run " "the test, skipping.\n "); return TEST_SKIPPED_ITSELF; } diff --git a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_basic.h b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_basic.h index b0bd31d2..59f07dd7 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_basic.h +++ b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_basic.h @@ -50,13 +50,14 @@ struct BasicMutableCommandBufferTest : BasicCommandBufferTest virtual cl_int SetUp(int elements) override { - BasicCommandBufferTest::SetUp(elements); + cl_int error = BasicCommandBufferTest::SetUp(elements); + test_error(error, "BasicCommandBufferTest::SetUp failed"); - cl_int error = init_extension_functions(); + error = init_extension_functions(); test_error(error, "Unable to initialise extension functions"); cl_command_buffer_properties_khr prop = CL_COMMAND_BUFFER_MUTABLE_KHR; - if (simultaneous_use_support) + if (simultaneous_use_requested) { prop |= CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR; } @@ -90,10 +91,10 @@ struct BasicMutableCommandBufferTest : BasicCommandBufferTest cl_version extension_version = get_extension_version( device, "cl_khr_command_buffer_mutable_dispatch"); - if (extension_version != CL_MAKE_VERSION(0, 9, 3)) + if (extension_version != CL_MAKE_VERSION(0, 9, 4)) { log_info("cl_khr_command_buffer_mutable_dispatch version " - "0.9.3 is " + "0.9.4 is " "required to run the test, skipping.\n "); extension_avaliable = false; } @@ -128,6 +129,7 @@ struct BasicMutableCommandBufferTest : BasicCommandBufferTest } clUpdateMutableCommandsKHR_fn clUpdateMutableCommandsKHR = nullptr; + bool simultaneous_use_requested = false; const char* kernelString = "__kernel void empty() {}"; const size_t global_work_size = 4 * 16; diff --git a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_iterative_arg_update.cpp b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_iterative_arg_update.cpp index 1107d015..b9f27b8e 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_iterative_arg_update.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_iterative_arg_update.cpp @@ -35,9 +35,7 @@ struct IterativeArgUpdateDispatch : BasicMutableCommandBufferTest cl_command_queue queue) : BasicMutableCommandBufferTest(device, context, queue), command(nullptr) - { - simultaneous_use_requested = false; - } + {} bool Skip() override { diff --git a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_multiple_dispatches.cpp b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_multiple_dispatches.cpp index 08d1fc9f..c7dc50d4 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_multiple_dispatches.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_multiple_dispatches.cpp @@ -33,9 +33,7 @@ struct MultipleCommandsDispatch : BasicMutableCommandBufferTest cl_command_queue queue) : BasicMutableCommandBufferTest(device, context, queue), command_pri(nullptr), command_sec(nullptr) - { - simultaneous_use_requested = false; - } + {} bool Skip() override { @@ -47,7 +45,7 @@ struct MultipleCommandsDispatch : BasicMutableCommandBufferTest sizeof(mutable_capabilities), &mutable_capabilities, nullptr) && mutable_capabilities & CL_MUTABLE_DISPATCH_ARGUMENTS_KHR; - // require mutable arguments capabillity + // require mutable arguments capability return !mutable_support; } diff --git a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_overwrite_update.cpp b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_overwrite_update.cpp index 4a4b8b31..d78ff419 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_overwrite_update.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_overwrite_update.cpp @@ -34,9 +34,7 @@ struct OverwriteUpdateDispatch : BasicMutableCommandBufferTest cl_command_queue queue) : BasicMutableCommandBufferTest(device, context, queue), command(nullptr) - { - simultaneous_use_requested = false; - } + {} bool Skip() override { diff --git a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_simultaneous.cpp b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_simultaneous.cpp index 4b1610f5..cee477f1 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_simultaneous.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_simultaneous.cpp @@ -21,10 +21,12 @@ #include #include //////////////////////////////////////////////////////////////////////////////// -// mutable dispatch tests which handle following cases: -// - out-of-order queue use +// mutable dispatch tests which handles +// - out-of-order queue with dependencies between command-buffer enqueues // - out-of-order queue with simultaneous use +// - in-order queue with dependencies between command-buffer enqueues // - in-order queue with simultaneous use +// - cross queue with dependencies between command-buffer enqueues // - cross-queue with simultaneous use namespace { @@ -35,11 +37,10 @@ struct SimultaneousMutableDispatchTest : public BasicMutableCommandBufferTest SimultaneousMutableDispatchTest(cl_device_id device, cl_context context, cl_command_queue queue) : BasicMutableCommandBufferTest(device, context, queue), - work_queue(nullptr), work_command_buffer(this), user_event(nullptr), - wait_pass_event(nullptr), command(nullptr) + work_queue(nullptr), work_command_buffer(this), new_in_mem(nullptr), + command(nullptr) { simultaneous_use_requested = simultaneous_request; - if (simultaneous_request) buffer_size_multiplier = 2; } cl_int SetUpKernel() override @@ -48,26 +49,36 @@ struct SimultaneousMutableDispatchTest : public BasicMutableCommandBufferTest test_error(error, "BasicCommandBufferTest::SetUpKernel failed"); // create additional kernel to properly prepare output buffer for test - const char* kernel_str = + const char *kernel_str = R"( - __kernel void fill(int pattern, __global int* out, __global int* - offset) + __kernel void mul(__global int* out, __global int* in, int mul_val) { size_t id = get_global_id(0); - size_t ind = offset[0] + id ; - out[ind] = pattern; + out[id] = in[id] * mul_val; })"; error = create_single_kernel_helper_create_program( - context, &program_fill, 1, &kernel_str); + context, &program_mul, 1, &kernel_str); test_error(error, "Failed to create program with source"); error = - clBuildProgram(program_fill, 1, &device, nullptr, nullptr, nullptr); + clBuildProgram(program_mul, 1, &device, nullptr, nullptr, nullptr); test_error(error, "Failed to build program"); - kernel_fill = clCreateKernel(program_fill, "fill", &error); - test_error(error, "Failed to create copy kernel"); + kernel_mul = clCreateKernel(program_mul, "mul", &error); + test_error(error, "Failed to create multiply kernel"); + + new_out_mem = clCreateBuffer(context, CL_MEM_WRITE_ONLY, + sizeof(cl_int) * num_elements + * buffer_size_multiplier, + nullptr, &error); + test_error(error, "clCreateBuffer failed"); + + new_in_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, + sizeof(cl_int) * num_elements + * buffer_size_multiplier, + nullptr, &error); + test_error(error, "clCreateBuffer failed"); return CL_SUCCESS; } @@ -77,14 +88,13 @@ struct SimultaneousMutableDispatchTest : public BasicMutableCommandBufferTest cl_int error = BasicCommandBufferTest::SetUpKernelArgs(); test_error(error, "BasicCommandBufferTest::SetUpKernelArgs failed"); - error = clSetKernelArg(kernel_fill, 0, sizeof(cl_int), - &overwritten_pattern); + error = clSetKernelArg(kernel_mul, 0, sizeof(out_mem), &out_mem); test_error(error, "clSetKernelArg failed"); - error = clSetKernelArg(kernel_fill, 1, sizeof(out_mem), &out_mem); + error = clSetKernelArg(kernel_mul, 1, sizeof(off_mem), &in_mem); test_error(error, "clSetKernelArg failed"); - error = clSetKernelArg(kernel_fill, 2, sizeof(off_mem), &off_mem); + error = clSetKernelArg(kernel_mul, 2, sizeof(cl_int), &pattern_pri); test_error(error, "clSetKernelArg failed"); return CL_SUCCESS; @@ -101,30 +111,28 @@ struct SimultaneousMutableDispatchTest : public BasicMutableCommandBufferTest context, device, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &error); test_error(error, "Unable to create command queue to test with"); - - cl_command_buffer_properties_khr prop = - CL_COMMAND_BUFFER_MUTABLE_KHR; - if (simultaneous_use_support) - { - prop |= CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR; - } - - const cl_command_buffer_properties_khr props[] = { - CL_COMMAND_BUFFER_FLAGS_KHR, - prop, - 0, - }; - - work_command_buffer = - clCreateCommandBufferKHR(1, &work_queue, props, &error); - test_error(error, "clCreateCommandBufferKHR failed"); } else { work_queue = queue; - work_command_buffer = command_buffer; } + cl_command_buffer_properties_khr prop = CL_COMMAND_BUFFER_MUTABLE_KHR; + + if (simultaneous_use_requested) + { + prop |= CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR; + } + + const cl_command_buffer_properties_khr props[] = { + CL_COMMAND_BUFFER_FLAGS_KHR, + prop, + 0, + }; + + work_command_buffer = + clCreateCommandBufferKHR(1, &work_queue, props, &error); + test_error(error, "clCreateCommandBufferKHR failed"); return CL_SUCCESS; } @@ -145,293 +153,245 @@ struct SimultaneousMutableDispatchTest : public BasicMutableCommandBufferTest || !mutable_support; } + cl_int RecordCommandBuffer() + { + cl_int error = clCommandNDRangeKernelKHR( + work_command_buffer, nullptr, nullptr, kernel_mul, 1, nullptr, + &num_elements, nullptr, 0, nullptr, nullptr, &command); + test_error(error, "clCommandNDRangeKernelKHR failed"); + + error = clFinalizeCommandBufferKHR(work_command_buffer); + test_error(error, "clFinalizeCommandBufferKHR failed"); + + return CL_SUCCESS; + } + + cl_int RunSerializedPass(std::vector &first_enqueue_output, + std::vector &second_enqueue_output) + { + /* Serialize command-buffer enqueue, is a linear sequence of + * commands, with dependencies enforced using an in-order queue + * or cl_event dependencies. + * + * 1. Fill input buffer + * 2. Enqueue command-buffer doing: `output = a * input; + * 3. Read output buffer to host data so it can be verified later + * - Update command to new input buffer, new `a` val and use output + * buffer from previous invocation as new input buffer. + * 4. Enqueue command-buffer again. + * 5. Read new output buffer back to host data so it can be verified + * later + * + */ + clEventWrapper E[4]; + cl_int error = clEnqueueFillBuffer( + work_queue, in_mem, &pattern_fill, sizeof(cl_int), 0, data_size(), + 0, nullptr, (out_of_order_request ? &E[0] : nullptr)); + test_error(error, "clEnqueueFillBuffer failed"); + + error = clEnqueueCommandBufferKHR( + 0, nullptr, work_command_buffer, (out_of_order_request ? 1 : 0), + (out_of_order_request ? &E[0] : nullptr), + (out_of_order_request ? &E[1] : nullptr)); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + error = clEnqueueReadBuffer(work_queue, out_mem, CL_FALSE, 0, + data_size(), first_enqueue_output.data(), + (out_of_order_request ? 1 : 0), + (out_of_order_request ? &E[1] : nullptr), + (out_of_order_request ? &E[2] : nullptr)); + test_error(error, "clEnqueueReadBuffer failed"); + + cl_mutable_dispatch_arg_khr arg_1{ 0, sizeof(new_out_mem), + &new_out_mem }; + + cl_mutable_dispatch_arg_khr arg_2{ 1, sizeof(cl_mem), &out_mem }; + cl_mutable_dispatch_arg_khr arg_3{ 2, sizeof(cl_int), &pattern_sec }; + + cl_mutable_dispatch_arg_khr args[] = { arg_1, arg_2, arg_3 }; + cl_mutable_dispatch_config_khr dispatch_config{ + command, + 3 /* num_args */, + 0 /* num_svm_arg */, + 0 /* num_exec_infos */, + 0 /* work_dim - 0 means no change to dimensions */, + args /* arg_list */, + nullptr /* arg_svm_list - nullptr means no change*/, + nullptr /* exec_info_list */, + nullptr /* global_work_offset */, + nullptr /* global_work_size */, + nullptr /* local_work_size */ + }; + + cl_uint num_configs = 1; + cl_command_buffer_update_type_khr config_types[1] = { + CL_STRUCTURE_TYPE_MUTABLE_DISPATCH_CONFIG_KHR + }; + const void* configs[1] = { &dispatch_config }; + error = clUpdateMutableCommandsKHR(work_command_buffer, num_configs, + config_types, configs); + test_error(error, "clUpdateMutableCommandsKHR failed"); + + error = clEnqueueCommandBufferKHR( + 0, nullptr, work_command_buffer, (out_of_order_request ? 1 : 0), + (out_of_order_request ? &E[2] : nullptr), + (out_of_order_request ? &E[3] : nullptr)); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + error = clEnqueueReadBuffer( + work_queue, new_out_mem, CL_FALSE, 0, data_size(), + second_enqueue_output.data(), (out_of_order_request ? 1 : 0), + (out_of_order_request ? &E[3] : nullptr), nullptr); + test_error(error, "clEnqueueReadBuffer failed"); + return CL_SUCCESS; + } + + cl_int RunSimultaneousPass(std::vector &first_enqueue_output, + std::vector &second_enqueue_output) + { + /* Simultaneous command-buffer pass enqueues a command-buffer twice + * without dependencies between the enqueues, but an update so that + * all the parameters are different to avoid race conditions in the + * kernel execution. The asynchronous task graph looks like: + * + * (Fill input A buffer) (Fill input B buffer) + * | | + * (Enqueue command_buffer) (Enqueue updated command_buffer) + * | | + * (Read output A buffer) (Read output B buffer) + */ + clEventWrapper E[4]; + cl_int error = clEnqueueFillBuffer( + work_queue, in_mem, &pattern_fill, sizeof(cl_int), 0, data_size(), + 0, nullptr, (out_of_order_request ? &E[0] : nullptr)); + test_error(error, "clEnqueueFillBuffer failed"); + + error = clEnqueueFillBuffer(work_queue, new_in_mem, &pattern_fill_2, + sizeof(cl_int), 0, data_size(), 0, nullptr, + (out_of_order_request ? &E[1] : nullptr)); + test_error(error, "clEnqueueFillBuffer failed"); + + error = clEnqueueCommandBufferKHR( + 0, nullptr, work_command_buffer, (out_of_order_request ? 1 : 0), + (out_of_order_request ? &E[0] : nullptr), + (out_of_order_request ? &E[2] : nullptr)); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + cl_mutable_dispatch_arg_khr arg_1{ 0, sizeof(new_out_mem), + &new_out_mem }; + cl_mutable_dispatch_arg_khr arg_2{ 1, sizeof(cl_mem), &new_in_mem }; + cl_mutable_dispatch_arg_khr arg_3{ 2, sizeof(cl_int), &pattern_sec }; + + cl_mutable_dispatch_arg_khr args[] = { arg_1, arg_2, arg_3 }; + cl_mutable_dispatch_config_khr dispatch_config{ + command, + 3 /* num_args */, + 0 /* num_svm_arg */, + 0 /* num_exec_infos */, + 0 /* work_dim - 0 means no change to dimensions */, + args /* arg_list */, + nullptr /* arg_svm_list - nullptr means no change*/, + nullptr /* exec_info_list */, + nullptr /* global_work_offset */, + nullptr /* global_work_size */, + nullptr /* local_work_size */ + }; + + cl_uint num_configs = 1; + cl_command_buffer_update_type_khr config_types[1] = { + CL_STRUCTURE_TYPE_MUTABLE_DISPATCH_CONFIG_KHR + }; + const void* configs[1] = { &dispatch_config }; + error = clUpdateMutableCommandsKHR(work_command_buffer, num_configs, + config_types, configs); + test_error(error, "clUpdateMutableCommandsKHR failed"); + + error = clEnqueueCommandBufferKHR( + 0, nullptr, work_command_buffer, (out_of_order_request ? 1 : 0), + (out_of_order_request ? &E[1] : nullptr), + (out_of_order_request ? &E[3] : nullptr)); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + error = clEnqueueReadBuffer( + work_queue, out_mem, CL_FALSE, 0, data_size(), + first_enqueue_output.data(), (out_of_order_request ? 1 : 0), + (out_of_order_request ? &E[2] : nullptr), nullptr); + test_error(error, "clEnqueueReadBuffer failed"); + + error = clEnqueueReadBuffer( + work_queue, new_out_mem, CL_FALSE, 0, data_size(), + second_enqueue_output.data(), (out_of_order_request ? 1 : 0), + (out_of_order_request ? &E[3] : nullptr), nullptr); + test_error(error, "clEnqueueReadBuffer failed"); + return CL_SUCCESS; + } + + cl_int VerifySerializedPass(std::vector &first_enqueue_output, + std::vector &second_enqueue_output) + { + const cl_int first_enqueue_ref = pattern_pri * pattern_fill; + const cl_int second_enqueue_ref = pattern_sec * first_enqueue_ref; + for (size_t i = 0; i < num_elements; i++) + { + CHECK_VERIFICATION_ERROR(first_enqueue_ref, first_enqueue_output[i], + i); + CHECK_VERIFICATION_ERROR(second_enqueue_ref, + second_enqueue_output[i], i); + } + return CL_SUCCESS; + } + + cl_int VerifySimultaneousPass(std::vector &first_enqueue_output, + std::vector &second_enqueue_output) + { + const cl_int first_enqueue_ref = pattern_pri * pattern_fill; + const cl_int second_enqueue_ref = pattern_sec * pattern_fill_2; + for (size_t i = 0; i < num_elements; i++) + { + CHECK_VERIFICATION_ERROR(first_enqueue_ref, first_enqueue_output[i], + i); + CHECK_VERIFICATION_ERROR(second_enqueue_ref, + second_enqueue_output[i], i); + } + return CL_SUCCESS; + } + cl_int Run() override { - cl_int error = CL_SUCCESS; + cl_int error = RecordCommandBuffer(); + test_error(error, "RecordCommandBuffer failed"); - if (simultaneous_use_support) + std::vector first_enqueue_output(num_elements); + std::vector second_enqueue_output(num_elements); + + if (simultaneous_use_requested) { - // enqueue simultaneous command-buffers with out-of-order calls - error = RunSimultaneous(); - test_error(error, "RunSimultaneous failed"); + error = RunSimultaneousPass(first_enqueue_output, + second_enqueue_output); + test_error(error, "RunSimultaneousPass failed"); } else { - // enqueue single command-buffer with out-of-order calls - error = RunSingle(); - test_error(error, "RunSingle failed"); + error = + RunSerializedPass(first_enqueue_output, second_enqueue_output); + test_error(error, "RunSerializedPass failed"); } - return CL_SUCCESS; - } - - cl_int RecordCommandBuffer() - { - cl_sync_point_khr sync_points[2]; - const cl_int pattern = pattern_pri; - cl_int error = clCommandFillBufferKHR( - work_command_buffer, nullptr, nullptr, in_mem, &pattern, - sizeof(cl_int), 0, data_size(), 0, nullptr, &sync_points[0], - nullptr); - test_error(error, "clCommandFillBufferKHR failed"); - - error = clCommandFillBufferKHR(work_command_buffer, nullptr, nullptr, - out_mem, &overwritten_pattern, - sizeof(cl_int), 0, data_size(), 0, - nullptr, &sync_points[1], nullptr); - test_error(error, "clCommandFillBufferKHR failed"); - - error = clCommandNDRangeKernelKHR( - work_command_buffer, nullptr, nullptr, kernel, 1, nullptr, - &num_elements, nullptr, 2, sync_points, nullptr, &command); - test_error(error, "clCommandNDRangeKernelKHR failed"); - - error = clFinalizeCommandBufferKHR(work_command_buffer); - test_error(error, "clFinalizeCommandBufferKHR failed"); - - return CL_SUCCESS; - } - - cl_int RunSingle() - { - cl_int error; - - error = RecordCommandBuffer(); - test_error(error, "RecordCommandBuffer failed"); - - error = clEnqueueCommandBufferKHR(0, nullptr, work_command_buffer, 0, - nullptr, &single_event); - test_error(error, "clEnqueueCommandBufferKHR failed"); - - std::vector output_data(num_elements); - error = - clEnqueueReadBuffer(work_queue, out_mem, CL_TRUE, 0, data_size(), - output_data.data(), 1, &single_event, nullptr); - test_error(error, "clEnqueueReadBuffer failed"); - - for (size_t i = 0; i < num_elements; i++) - { - CHECK_VERIFICATION_ERROR(pattern_pri, output_data[i], i); - } - - clMemWrapper new_out_mem = clCreateBuffer(context, CL_MEM_WRITE_ONLY, - sizeof(cl_int) * num_elements - * buffer_size_multiplier, - nullptr, &error); - test_error(error, "clCreateBuffer failed"); - - cl_mutable_dispatch_arg_khr arg_1{ 1, sizeof(new_out_mem), - &new_out_mem }; - cl_mutable_dispatch_arg_khr args[] = { arg_1 }; - - cl_mutable_dispatch_config_khr dispatch_config{ - command, - 1 /* num_args */, - 0 /* num_svm_arg */, - 0 /* num_exec_infos */, - 0 /* work_dim - 0 means no change to dimensions */, - args /* arg_list */, - nullptr /* arg_svm_list - nullptr means no change*/, - nullptr /* exec_info_list */, - nullptr /* global_work_offset */, - nullptr /* global_work_size */, - nullptr /* local_work_size */ - }; - - cl_uint num_configs = 1; - cl_command_buffer_update_type_khr config_types[1] = { - CL_STRUCTURE_TYPE_MUTABLE_DISPATCH_CONFIG_KHR - }; - const void* configs[1] = { &dispatch_config }; - error = clUpdateMutableCommandsKHR(work_command_buffer, num_configs, - config_types, configs); - test_error(error, "clUpdateMutableCommandsKHR failed"); - - error = clEnqueueCommandBufferKHR(0, nullptr, work_command_buffer, 0, - nullptr, &single_event); - test_error(error, "clEnqueueCommandBufferKHR failed"); - - error = clEnqueueReadBuffer(work_queue, new_out_mem, CL_TRUE, 0, - data_size(), output_data.data(), 1, - &single_event, nullptr); - test_error(error, "clEnqueueReadBuffer failed"); - - for (size_t i = 0; i < num_elements; i++) - { - CHECK_VERIFICATION_ERROR(pattern_pri, output_data[i], i); - } - - return CL_SUCCESS; - } - - cl_int RecordSimultaneousCommandBuffer() - { - cl_sync_point_khr sync_points[2]; - // for both simultaneous passes this call will fill entire in_mem buffer - cl_int error = clCommandFillBufferKHR( - work_command_buffer, nullptr, nullptr, in_mem, &pattern_pri, - sizeof(cl_int), 0, data_size() * buffer_size_multiplier, 0, nullptr, - &sync_points[0], nullptr); - test_error(error, "clCommandFillBufferKHR failed"); - - // to avoid overwriting the entire result buffer instead of filling - // only relevant part this additional kernel was introduced - - error = clCommandNDRangeKernelKHR( - work_command_buffer, nullptr, nullptr, kernel_fill, 1, nullptr, - &num_elements, nullptr, 0, nullptr, &sync_points[1], &command); - test_error(error, "clCommandNDRangeKernelKHR failed"); - - error = clCommandNDRangeKernelKHR( - work_command_buffer, nullptr, nullptr, kernel, 1, nullptr, - &num_elements, nullptr, 2, sync_points, nullptr, &command); - test_error(error, "clCommandNDRangeKernelKHR failed"); - - error = clFinalizeCommandBufferKHR(work_command_buffer); - test_error(error, "clFinalizeCommandBufferKHR failed"); - - return CL_SUCCESS; - } - - struct SimulPassData - { - cl_int offset; - std::vector output_buffer; - std::vector updated_output_buffer; - // 0:user event, 1:offset-buffer fill event, 2:kernel done event - clEventWrapper wait_events[3]; - }; - - cl_int EnqueueSimultaneousPass(SimulPassData& pd) - { - cl_int error = CL_SUCCESS; - if (!user_event) - { - user_event = clCreateUserEvent(context, &error); - test_error(error, "clCreateUserEvent failed"); - } - - pd.wait_events[0] = user_event; - - // filling offset buffer must wait for previous pass completeness - error = clEnqueueFillBuffer( - work_queue, off_mem, &pd.offset, sizeof(cl_int), 0, sizeof(cl_int), - (wait_pass_event != nullptr ? 1 : 0), - (wait_pass_event != nullptr ? &wait_pass_event : nullptr), - &pd.wait_events[1]); - test_error(error, "clEnqueueFillBuffer failed"); - - // command buffer execution must wait for two wait-events - error = - clEnqueueCommandBufferKHR(0, nullptr, work_command_buffer, 2, - &pd.wait_events[0], &pd.wait_events[2]); - test_error(error, "clEnqueueCommandBufferKHR failed"); - - error = clEnqueueReadBuffer(work_queue, out_mem, CL_FALSE, - pd.offset * sizeof(cl_int), data_size(), - pd.output_buffer.data(), 1, - &pd.wait_events[2], nullptr); - test_error(error, "clEnqueueReadBuffer failed"); - - clMemWrapper new_out_mem = clCreateBuffer(context, CL_MEM_WRITE_ONLY, - sizeof(cl_int) * num_elements - * buffer_size_multiplier, - nullptr, &error); - test_error(error, "clCreateBuffer failed"); - // Retain new output memory object until the end of the test. - retained_output_buffers.push_back(new_out_mem); - - cl_mutable_dispatch_arg_khr arg_1{ 1, sizeof(new_out_mem), - &new_out_mem }; - cl_mutable_dispatch_arg_khr args[] = { arg_1 }; - - cl_mutable_dispatch_config_khr dispatch_config{ - command, - 1 /* num_args */, - 0 /* num_svm_arg */, - 0 /* num_exec_infos */, - 0 /* work_dim - 0 means no change to dimensions */, - args /* arg_list */, - nullptr /* arg_svm_list - nullptr means no change*/, - nullptr /* exec_info_list */, - nullptr /* global_work_offset */, - nullptr /* global_work_size */, - nullptr /* local_work_size */ - }; - - cl_uint num_configs = 1; - cl_command_buffer_update_type_khr config_types[1] = { - CL_STRUCTURE_TYPE_MUTABLE_DISPATCH_CONFIG_KHR - }; - const void* configs[1] = { &dispatch_config }; - error = clUpdateMutableCommandsKHR(work_command_buffer, num_configs, - config_types, configs); - test_error(error, "clUpdateMutableCommandsKHR failed"); - - // command buffer execution must wait for two wait-events - error = - clEnqueueCommandBufferKHR(0, nullptr, work_command_buffer, 2, - &pd.wait_events[0], &pd.wait_events[2]); - test_error(error, "clEnqueueCommandBufferKHR failed"); - - error = clEnqueueReadBuffer(work_queue, new_out_mem, CL_FALSE, - pd.offset * sizeof(cl_int), data_size(), - pd.updated_output_buffer.data(), 1, - &pd.wait_events[2], nullptr); - test_error(error, "clEnqueueReadBuffer failed"); - - return CL_SUCCESS; - } - - cl_int RunSimultaneous() - { - cl_int error = RecordSimultaneousCommandBuffer(); - test_error(error, "RecordSimultaneousCommandBuffer failed"); - - cl_int offset = static_cast(num_elements); - - std::vector simul_passes = { - { 0, std::vector(num_elements), - std::vector(num_elements) }, - { offset, std::vector(num_elements), - std::vector(num_elements) } - }; - - for (auto&& pass : simul_passes) - { - error = EnqueueSimultaneousPass(pass); - test_error(error, "EnqueueSimultaneousPass failed"); - - wait_pass_event = pass.wait_events[2]; - } - - error = clSetUserEventStatus(user_event, CL_COMPLETE); - test_error(error, "clSetUserEventStatus failed"); - error = clFinish(work_queue); test_error(error, "clFinish failed"); // verify the result buffers - auto& first_pass_output = simul_passes[0].output_buffer; - auto& first_pass_updated_output = simul_passes[0].updated_output_buffer; - auto& second_pass_output = simul_passes[1].output_buffer; - auto& second_pass_updated_output = - simul_passes[1].updated_output_buffer; - for (size_t i = 0; i < num_elements; i++) + if (simultaneous_use_requested) { - // First pass: - // Before updating, out_mem is copied from in_mem (pattern_pri) - CHECK_VERIFICATION_ERROR(pattern_pri, first_pass_output[i], i); - // After updating, new_out_mem is copied from in_mem (pattern_pri) - CHECK_VERIFICATION_ERROR(pattern_pri, first_pass_updated_output[i], - i); - // Second pass: - // Before updating, out_mem is filled with overwritten_pattern - CHECK_VERIFICATION_ERROR(overwritten_pattern, second_pass_output[i], - i); - // After updating, new_out_mem is copied from in_mem (pattern_pri) - CHECK_VERIFICATION_ERROR(pattern_pri, second_pass_updated_output[i], - i); + error = VerifySimultaneousPass(first_enqueue_output, + second_enqueue_output); + test_error(error, "VerifySimultaneousPass failed"); + } + else + { + error = VerifySerializedPass(first_enqueue_output, + second_enqueue_output); + test_error(error, "VerifySerializedPass failed"); } return CL_SUCCESS; @@ -440,22 +400,20 @@ struct SimultaneousMutableDispatchTest : public BasicMutableCommandBufferTest clCommandQueueWrapper work_queue; clCommandBufferWrapper work_command_buffer; - clEventWrapper user_event; - clEventWrapper single_event; - clEventWrapper wait_pass_event; + clKernelWrapper kernel_mul; + clProgramWrapper program_mul; - clKernelWrapper kernel_fill; - clProgramWrapper program_fill; + clMemWrapper new_out_mem, new_in_mem; - std::vector retained_output_buffers; - - const size_t test_global_work_size = 3 * sizeof(cl_int); const cl_int pattern_pri = 42; + const cl_int pattern_sec = 0xACDC; + const cl_int pattern_fill = 0xA; + const cl_int pattern_fill_2 = -3; - const cl_int overwritten_pattern = 0xACDC; cl_mutable_command_khr command; }; +template struct CrossQueueSimultaneousMutableDispatchTest : public BasicMutableCommandBufferTest { @@ -463,9 +421,9 @@ struct CrossQueueSimultaneousMutableDispatchTest cl_context context, cl_command_queue queue) : BasicMutableCommandBufferTest(device, context, queue), - queue_sec(nullptr), command(nullptr) + queue_sec(nullptr), new_out_mem(nullptr), command(nullptr) { - simultaneous_use_requested = true; + simultaneous_use_requested = simultaneous_use_request; } cl_int SetUpKernel() override @@ -488,6 +446,11 @@ struct CrossQueueSimultaneousMutableDispatchTest kernel = clCreateKernel(program, "fill", &error); test_error(error, "Failed to create copy kernel"); + new_out_mem = + clCreateBuffer(context, CL_MEM_WRITE_ONLY, + sizeof(cl_int) * num_elements, nullptr, &error); + test_error(error, "clCreateBuffer failed"); + return CL_SUCCESS; } @@ -530,24 +493,18 @@ struct CrossQueueSimultaneousMutableDispatchTest sizeof(mutable_capabilities), &mutable_capabilities, nullptr) && mutable_capabilities & CL_MUTABLE_DISPATCH_ARGUMENTS_KHR; - return !simultaneous_use_support || !mutable_support; + return (simultaneous_use_requested && !simultaneous_use_support) + || !mutable_support; } cl_int Run() override { - // record command buffer - cl_int pattern = 0; - cl_int error = clCommandFillBufferKHR( - command_buffer, nullptr, nullptr, out_mem, &pattern, sizeof(cl_int), - 0, data_size(), 0, nullptr, nullptr, nullptr); - test_error(error, "clCommandFillBufferKHR failed"); - cl_command_properties_khr props[] = { CL_MUTABLE_DISPATCH_UPDATABLE_FIELDS_KHR, CL_MUTABLE_DISPATCH_ARGUMENTS_KHR, 0 }; - error = clCommandNDRangeKernelKHR( + cl_int error = clCommandNDRangeKernelKHR( command_buffer, nullptr, props, kernel, 1, nullptr, &num_elements, nullptr, 0, nullptr, nullptr, &command); test_error(error, "clCommandNDRangeKernelKHR failed"); @@ -555,16 +512,15 @@ struct CrossQueueSimultaneousMutableDispatchTest error = clFinalizeCommandBufferKHR(command_buffer); test_error(error, "clFinalizeCommandBufferKHR failed"); - // enqueue command buffer to default queue - error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0, - nullptr, nullptr); + // If we are testing not using simultaneous-use then we need to use + // an event to serialize the execution order to the command-buffer + // submission to each queue. + clEventWrapper E; + error = clEnqueueCommandBufferKHR( + 0, nullptr, command_buffer, 0, nullptr, + (simultaneous_use_requested ? nullptr : &E)); test_error(error, "clEnqueueCommandBufferKHR failed"); - // update mutable parameters - clMemWrapper new_out_mem = clCreateBuffer(context, CL_MEM_WRITE_ONLY, - data_size(), nullptr, &error); - test_error(error, "clCreateBuffer failed"); - cl_mutable_dispatch_arg_khr arg_0{ 0, sizeof(cl_int), &pattern_sec }; cl_mutable_dispatch_arg_khr arg_1{ 1, sizeof(new_out_mem), &new_out_mem }; @@ -594,30 +550,35 @@ struct CrossQueueSimultaneousMutableDispatchTest test_error(error, "clUpdateMutableCommandsKHR failed"); // enqueue command buffer to non-default queue - error = clEnqueueCommandBufferKHR(1, &queue_sec, command_buffer, 0, - nullptr, nullptr); + error = clEnqueueCommandBufferKHR( + 1, &queue_sec, command_buffer, (simultaneous_use_requested ? 0 : 1), + (simultaneous_use_requested ? nullptr : &E), nullptr); test_error(error, "clEnqueueCommandBufferKHR failed"); - error = clFinish(queue_sec); - test_error(error, "clFinish failed"); - // read result of command buffer execution std::vector output_data(num_elements); + error = clEnqueueReadBuffer(queue, out_mem, CL_TRUE, 0, data_size(), + output_data.data(), 0, nullptr, nullptr); + test_error(error, "clEnqueueReadBuffer failed"); + + std::vector sec_output_data(num_elements); error = clEnqueueReadBuffer(queue_sec, new_out_mem, CL_TRUE, 0, data_size(), - output_data.data(), 0, nullptr, nullptr); + sec_output_data.data(), 0, nullptr, nullptr); test_error(error, "clEnqueueReadBuffer failed"); // verify the result for (size_t i = 0; i < num_elements; i++) { - CHECK_VERIFICATION_ERROR(pattern_sec, output_data[i], i); + CHECK_VERIFICATION_ERROR(pattern_pri, output_data[i], i); + CHECK_VERIFICATION_ERROR(pattern_sec, sec_output_data[i], i); } return CL_SUCCESS; } clCommandQueueWrapper queue_sec; + clMemWrapper new_out_mem; const cl_int pattern_pri = 42; const cl_int pattern_sec = 0xACDC; cl_mutable_command_khr command; @@ -637,14 +598,26 @@ REGISTER_TEST(mutable_dispatch_simultaneous_out_of_order) device, context, queue, num_elements); } +REGISTER_TEST(mutable_dispatch_in_order) +{ + return MakeAndRunTest>( + device, context, queue, num_elements); +} + REGISTER_TEST(mutable_dispatch_simultaneous_in_order) { return MakeAndRunTest>( device, context, queue, num_elements); } -REGISTER_TEST(mutable_dispatch_simultaneous_cross_queue) +REGISTER_TEST(mutable_dispatch_cross_queue) { - return MakeAndRunTest( + return MakeAndRunTest>( + device, context, queue, num_elements); +} + +REGISTER_TEST(mutable_dispatch_simultaneous_cross_queue) +{ + return MakeAndRunTest>( device, context, queue, num_elements); } diff --git a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_event_sync.cpp b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_event_sync.cpp index 00fda4dd..47aa2899 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_event_sync.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_event_sync.cpp @@ -79,11 +79,7 @@ struct CommandBufferEventSync : public BasicCommandBufferTest : BasicCommandBufferTest(device, context, queue), command_buffer_sec(this), kernel_sec(nullptr), in_mem_sec(nullptr), out_mem_sec(nullptr), off_mem_sec(nullptr), test_event(nullptr) - { - simultaneous_use_requested = - (event_mode == EventMode::RET_COMBUF_WAIT_FOR_COMBUF) ? true - : false; - } + {} //-------------------------------------------------------------------------- cl_int SetUpKernel() override @@ -159,9 +155,6 @@ struct CommandBufferEventSync : public BasicCommandBufferTest { if (BasicCommandBufferTest::Skip()) return true; - if (simultaneous_use_requested && !simultaneous_use_support) - return true; - if (out_of_order_requested && !out_of_order_support) return true; return false; diff --git a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_get_command_buffer_info.cpp b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_get_command_buffer_info.cpp index add0a531..cb2c2b46 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_get_command_buffer_info.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_get_command_buffer_info.cpp @@ -48,6 +48,39 @@ struct CommandBufferGetCommandBufferInfo : public BasicCommandBufferTest : BasicCommandBufferTest(device, context, queue) {} + bool Skip() override + { + if (BasicCommandBufferTest::Skip()) return true; + + if (test_mode == CombufInfoTestMode::CITM_PROP_ARRAY) + { + return !simultaneous_use_support + || !(is_extension_available( + device, + CL_KHR_COMMAND_BUFFER_MUTABLE_DISPATCH_EXTENSION_NAME)); + } + + return false; + } + + cl_int SetUp(int elements) override + { + + cl_int error = BasicCommandBufferTest::SetUp(elements); + test_error(error, "BasicCommandBufferTest::SetUp() failed"); + if (test_mode == CombufInfoTestMode::CITM_PROP_ARRAY) + { + cl_command_buffer_properties_khr properties[3] = { + CL_COMMAND_BUFFER_FLAGS_KHR, + CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR, 0 + }; + command_buffer = + clCreateCommandBufferKHR(1, &queue, properties, &error); + test_error(error, "clCreateCommandBufferKHR failed"); + } + return CL_SUCCESS; + } + //-------------------------------------------------------------------------- cl_int Run() override { @@ -237,33 +270,6 @@ struct CommandBufferGetCommandBufferInfo : public BasicCommandBufferTest error = verify_state(CL_COMMAND_BUFFER_STATE_EXECUTABLE_KHR); test_error(error, "verify_state failed"); - error = clEnqueueFillBuffer(queue, out_mem, &pattern, sizeof(cl_int), 0, - data_size(), 0, nullptr, nullptr); - test_error(error, "clEnqueueFillBuffer failed"); - - clEventWrapper trigger_event = clCreateUserEvent(context, &error); - test_error(error, "clCreateUserEvent failed"); - - clEventWrapper execute_event; - // enqueued command buffer blocked on user event - error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 1, - &trigger_event, &execute_event); - test_error(error, "clEnqueueCommandBufferKHR failed"); - - // execute command buffer - cl_int signal_error = clSetUserEventStatus(trigger_event, CL_COMPLETE); - - test_error(error, "verify_state failed"); - - test_error(signal_error, "clSetUserEventStatus failed"); - - error = clWaitForEvents(1, &execute_event); - test_error(error, "Unable to wait for execute event"); - - // verify executable state - error = verify_state(CL_COMMAND_BUFFER_STATE_EXECUTABLE_KHR); - test_error(error, "verify_state failed"); - return CL_SUCCESS; } diff --git a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_out_of_order.cpp b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_out_of_order.cpp index 60c43c8c..8516e16c 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_out_of_order.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_out_of_order.cpp @@ -21,11 +21,9 @@ namespace { //////////////////////////////////////////////////////////////////////////////// -// out-of-order tests for cl_khr_command_buffer which handles below cases: -// -test case for out-of-order command-buffer -// -test an out-of-order command-buffer with simultaneous use +// Tests for cl_khr_command_buffer which handles submitting a command-buffer to +// an out-of-order queue. -template struct OutOfOrderTest : public BasicCommandBufferTest { OutOfOrderTest(cl_device_id device, cl_context context, @@ -35,18 +33,11 @@ struct OutOfOrderTest : public BasicCommandBufferTest user_event(nullptr), wait_pass_event(nullptr), kernel_fill(nullptr), program_fill(nullptr) { - simultaneous_use_requested = simultaneous_request; - if (simultaneous_request) buffer_size_multiplier = 2; + buffer_size_multiplier = 2; // two enqueues of command-buffer } - //-------------------------------------------------------------------------- cl_int SetUpKernel() override { - // if device doesn't support simultaneous use which was requested - // we can skip creation of OCL resources - if (simultaneous_use_requested && !simultaneous_use_support) - return CL_SUCCESS; - cl_int error = BasicCommandBufferTest::SetUpKernel(); test_error(error, "BasicCommandBufferTest::SetUpKernel failed"); @@ -74,14 +65,8 @@ struct OutOfOrderTest : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- cl_int SetUpKernelArgs() override { - // if device doesn't support simultaneous use which was requested - // we can skip creation of OCL resources - if (simultaneous_use_requested && !simultaneous_use_support) - return CL_SUCCESS; - cl_int error = BasicCommandBufferTest::SetUpKernelArgs(); test_error(error, "BasicCommandBufferTest::SetUpKernelArgs failed"); @@ -98,7 +83,6 @@ struct OutOfOrderTest : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- cl_int SetUp(int elements) override { cl_int error = BasicCommandBufferTest::SetUp(elements); @@ -108,110 +92,23 @@ struct OutOfOrderTest : public BasicCommandBufferTest context, device, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &error); test_error(error, "Unable to create command queue to test with"); - cl_command_buffer_properties_khr properties[3] = { - CL_COMMAND_BUFFER_FLAGS_KHR, 0, 0 - }; - - if (simultaneous_use_requested && simultaneous_use_support) - properties[1] = CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR; - - out_of_order_command_buffer = clCreateCommandBufferKHR( - 1, &out_of_order_queue, properties, &error); + out_of_order_command_buffer = + clCreateCommandBufferKHR(1, &out_of_order_queue, nullptr, &error); test_error(error, "clCreateCommandBufferKHR failed"); return CL_SUCCESS; } - //-------------------------------------------------------------------------- bool Skip() override { if (BasicCommandBufferTest::Skip()) return true; - - if (!out_of_order_support - || (simultaneous_use_requested && !simultaneous_use_support)) - return true; - - return false; + return !out_of_order_support; } - //-------------------------------------------------------------------------- - cl_int Run() override - { - cl_int error = CL_SUCCESS; - - if (simultaneous_use_support) - { - // enqueue simultaneous command-buffers with out-of-order calls - error = RunSimultaneous(); - test_error(error, "RunSimultaneous failed"); - } - else - { - // enqueue single command-buffer with out-of-order calls - error = RunSingle(); - test_error(error, "RunSingle failed"); - } - - return CL_SUCCESS; - } - - //-------------------------------------------------------------------------- - cl_int RecordCommandBuffer() + cl_int RecordCommandBuffer() const { cl_sync_point_khr sync_points[2]; - const cl_int pattern = pattern_pri; - cl_int error = clCommandFillBufferKHR( - out_of_order_command_buffer, nullptr, nullptr, in_mem, &pattern, - sizeof(cl_int), 0, data_size(), 0, nullptr, &sync_points[0], - nullptr); - test_error(error, "clCommandFillBufferKHR failed"); - - error = clCommandFillBufferKHR(out_of_order_command_buffer, nullptr, - nullptr, out_mem, &overwritten_pattern, - sizeof(cl_int), 0, data_size(), 0, - nullptr, &sync_points[1], nullptr); - test_error(error, "clCommandFillBufferKHR failed"); - - error = clCommandNDRangeKernelKHR( - out_of_order_command_buffer, nullptr, nullptr, kernel, 1, nullptr, - &num_elements, nullptr, 2, sync_points, nullptr, nullptr); - test_error(error, "clCommandNDRangeKernelKHR failed"); - - error = clFinalizeCommandBufferKHR(out_of_order_command_buffer); - test_error(error, "clFinalizeCommandBufferKHR failed"); - - return CL_SUCCESS; - } - - //-------------------------------------------------------------------------- - cl_int RunSingle() - { - cl_int error = RecordCommandBuffer(); - test_error(error, "RecordCommandBuffer failed"); - - error = clEnqueueCommandBufferKHR( - 0, nullptr, out_of_order_command_buffer, 0, nullptr, &user_event); - test_error(error, "clEnqueueCommandBufferKHR failed"); - - std::vector output_data(num_elements); - error = clEnqueueReadBuffer(out_of_order_queue, out_mem, CL_TRUE, 0, - data_size(), output_data.data(), 1, - &user_event, nullptr); - test_error(error, "clEnqueueReadBuffer failed"); - - for (size_t i = 0; i < num_elements; i++) - { - CHECK_VERIFICATION_ERROR(pattern_pri, output_data[i], i); - } - - return CL_SUCCESS; - } - - //-------------------------------------------------------------------------- - cl_int RecordSimultaneousCommandBuffer() const - { - cl_sync_point_khr sync_points[2]; - // for both simultaneous passes this call will fill entire in_mem buffer + // fill entire in_mem buffer cl_int error = clCommandFillBufferKHR( out_of_order_command_buffer, nullptr, nullptr, in_mem, &pattern_pri, sizeof(cl_int), 0, data_size() * buffer_size_multiplier, 0, nullptr, @@ -236,79 +133,63 @@ struct OutOfOrderTest : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- - struct SimulPassData + struct EnqueuePassData { cl_int offset; std::vector output_buffer; - // 0:user event, 1:offset-buffer fill event, 2:kernel done event - clEventWrapper wait_events[3]; + // 0: offset-buffer fill event, 2:kernel done event + clEventWrapper wait_events[2]; }; - //-------------------------------------------------------------------------- - cl_int EnqueueSimultaneousPass(SimulPassData& pd) + cl_int EnqueuePass(EnqueuePassData& pd) { - cl_int error = CL_SUCCESS; - if (!user_event) - { - user_event = clCreateUserEvent(context, &error); - test_error(error, "clCreateUserEvent failed"); - } - - pd.wait_events[0] = user_event; - // filling offset buffer must wait for previous pass completeness - error = clEnqueueFillBuffer( + cl_int error = clEnqueueFillBuffer( out_of_order_queue, off_mem, &pd.offset, sizeof(cl_int), 0, sizeof(cl_int), (wait_pass_event != nullptr ? 1 : 0), (wait_pass_event != nullptr ? &wait_pass_event : nullptr), - &pd.wait_events[1]); + &pd.wait_events[0]); test_error(error, "clEnqueueFillBuffer failed"); // command buffer execution must wait for two wait-events error = clEnqueueCommandBufferKHR( - 0, nullptr, out_of_order_command_buffer, 2, &pd.wait_events[0], - &pd.wait_events[2]); + 0, nullptr, out_of_order_command_buffer, 1, &pd.wait_events[0], + &pd.wait_events[1]); test_error(error, "clEnqueueCommandBufferKHR failed"); error = clEnqueueReadBuffer(out_of_order_queue, out_mem, CL_FALSE, pd.offset * sizeof(cl_int), data_size(), pd.output_buffer.data(), 1, - &pd.wait_events[2], nullptr); + &pd.wait_events[1], nullptr); test_error(error, "clEnqueueReadBuffer failed"); return CL_SUCCESS; } - //-------------------------------------------------------------------------- - cl_int RunSimultaneous() + cl_int Run() override { - cl_int error = RecordSimultaneousCommandBuffer(); - test_error(error, "RecordSimultaneousCommandBuffer failed"); + cl_int error = RecordCommandBuffer(); + test_error(error, "RecordCommandBuffer failed"); cl_int offset = static_cast(num_elements); - - std::vector simul_passes = { + std::vector enqueue_passes = { { 0, std::vector(num_elements) }, { offset, std::vector(num_elements) } }; - for (auto&& pass : simul_passes) + for (auto&& pass : enqueue_passes) { - error = EnqueueSimultaneousPass(pass); - test_error(error, "EnqueueSimultaneousPass failed"); + error = EnqueuePass(pass); + test_error(error, "EnqueuePass failed"); - wait_pass_event = pass.wait_events[2]; + wait_pass_event = pass.wait_events[1]; } - error = clSetUserEventStatus(user_event, CL_COMPLETE); - test_error(error, "clSetUserEventStatus failed"); - error = clFinish(out_of_order_queue); test_error(error, "clFinish failed"); // verify the result buffers - for (auto&& pass : simul_passes) + for (auto&& pass : enqueue_passes) { auto& res_data = pass.output_buffer; for (size_t i = 0; i < num_elements; i++) @@ -320,7 +201,6 @@ struct OutOfOrderTest : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- clCommandQueueWrapper out_of_order_queue; clCommandBufferWrapper out_of_order_command_buffer; @@ -338,12 +218,5 @@ struct OutOfOrderTest : public BasicCommandBufferTest REGISTER_TEST(out_of_order) { - return MakeAndRunTest>(device, context, queue, - num_elements); -} - -REGISTER_TEST(simultaneous_out_of_order) -{ - return MakeAndRunTest>(device, context, queue, - num_elements); + return MakeAndRunTest(device, context, queue, num_elements); } diff --git a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_pipelined_enqueue.cpp b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_pipelined_enqueue.cpp new file mode 100644 index 00000000..e6611748 --- /dev/null +++ b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_pipelined_enqueue.cpp @@ -0,0 +1,321 @@ +// +// 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 "basic_command_buffer.h" + +#include + +namespace { + +//////////////////////////////////////////////////////////////////////////////// +// Tests for multiple sequential submissions of a command-buffer without a +// blocking wait between them, but using the following mechanisms to serialize +// execution of the submissions. +// * In-order queue dependencies +// * Event dependencies in command-buffer submissions to an out-of-order queue +// * Barrier submissions between command-buffer submissions to an out-of-order +// queue + +// Base class that individual test fixtures are derived from +struct CommandBufferPipelined : public BasicCommandBufferTest +{ + CommandBufferPipelined(cl_device_id device, cl_context context, + cl_command_queue queue) + : BasicCommandBufferTest(device, context, queue) + {} + + cl_int SetUpKernel() override + { + const char* mul_kernel_str = + R"( + __kernel void mul_by_val(int in, __global int* data) + { + size_t id = get_global_id(0); + data[id] *= in; + } + + __kernel void increment(__global int* data) + { + size_t id = get_global_id(0); + data[id]++; + })"; + + cl_int error = create_single_kernel_helper_create_program( + context, &program, 1, &mul_kernel_str); + test_error(error, "Failed to create program with source"); + + error = clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr); + test_error(error, "Failed to build program"); + + mul_kernel = clCreateKernel(program, "mul_by_val", &error); + test_error(error, "Failed to create mul_by_val kernel"); + + inc_kernel = clCreateKernel(program, "increment", &error); + test_error(error, "Failed to create increment kernel"); + + return CL_SUCCESS; + } + + cl_int SetUpKernelArgs() override + { + cl_int error = CL_SUCCESS; + out_mem = clCreateBuffer(context, CL_MEM_READ_WRITE, + num_elements * buffer_size_multiplier + * sizeof(cl_int), + nullptr, &error); + test_error(error, "clCreateBuffer failed"); + + cl_int val_arg = pattern; + error = clSetKernelArg(mul_kernel, 0, sizeof(cl_int), &val_arg); + test_error(error, "clSetKernelArg failed"); + + error = clSetKernelArg(mul_kernel, 1, sizeof(out_mem), &out_mem); + test_error(error, "clSetKernelArg failed"); + + error = clSetKernelArg(inc_kernel, 0, sizeof(out_mem), &out_mem); + test_error(error, "clSetKernelArg failed"); + + return CL_SUCCESS; + } + + cl_int RecordCommandBuffer(clCommandBufferWrapper& cmd_buf) + { + cl_int error = clCommandNDRangeKernelKHR( + cmd_buf, nullptr, nullptr, inc_kernel, 1, nullptr, &num_elements, + nullptr, 0, nullptr, nullptr, nullptr); + test_error(error, "clCommandNDRangeKernelKHR failed"); + + error = clFinalizeCommandBufferKHR(cmd_buf); + test_error(error, "clFinalizeCommandBufferKHR failed"); + + // Zero initialize buffer before starting test + cl_int zero_pattern = 0; + error = + clEnqueueFillBuffer(queue, out_mem, &zero_pattern, sizeof(cl_int), + 0, data_size(), 0, nullptr, nullptr); + test_error(error, "clEnqueueFillBuffer failed"); + + error = clFinish(queue); + test_error(error, "clFinish failed"); + + return CL_SUCCESS; + } + + const cl_int pattern = 42; + + clKernelWrapper inc_kernel = nullptr; + clKernelWrapper mul_kernel = nullptr; +}; + +struct InOrderPipelined : public CommandBufferPipelined +{ + InOrderPipelined(cl_device_id device, cl_context context, + cl_command_queue queue) + : CommandBufferPipelined(device, context, queue) + {} + + cl_int Run() override + { + cl_int error = RecordCommandBuffer(command_buffer); + test_error(error, "RecordCommandBuffer failed"); + + error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0, + nullptr, nullptr); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + error = + clEnqueueNDRangeKernel(queue, mul_kernel, 1, nullptr, &num_elements, + nullptr, 0, nullptr, nullptr); + test_error(error, "clEnqueueNDRangeKernel failed"); + + error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0, + nullptr, nullptr); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + std::vector output_data(num_elements); + error = clEnqueueReadBuffer(queue, out_mem, CL_TRUE, 0, data_size(), + output_data.data(), 0, nullptr, nullptr); + test_error(error, "clEnqueueReadBuffer failed"); + + // Verify + const cl_int ref = pattern + 1; + for (size_t i = 0; i < num_elements; i++) + { + CHECK_VERIFICATION_ERROR(ref, output_data[i], i); + } + return CL_SUCCESS; + } +}; + +struct EventPipelined : public CommandBufferPipelined +{ + EventPipelined(cl_device_id device, cl_context context, + cl_command_queue queue) + : CommandBufferPipelined(device, context, queue), + out_of_order_queue(nullptr), out_of_order_command_buffer(this) + {} + + bool Skip() override + { + return CommandBufferPipelined::Skip() || !out_of_order_support; + } + + cl_int SetUp(int elements) override + { + cl_int error = CommandBufferPipelined::SetUp(elements); + test_error(error, "EventPipelined::SetUp failed"); + + out_of_order_queue = clCreateCommandQueue( + context, device, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &error); + test_error(error, "Unable to create command queue to test with"); + + out_of_order_command_buffer = + clCreateCommandBufferKHR(1, &out_of_order_queue, nullptr, &error); + test_error(error, "clCreateCommandBufferKHR failed"); + + return CL_SUCCESS; + } + + cl_int Run() override + { + cl_int error = RecordCommandBuffer(out_of_order_command_buffer); + test_error(error, "RecordCommandBuffer failed"); + + error = clEnqueueCommandBufferKHR( + 0, nullptr, out_of_order_command_buffer, 0, nullptr, &events[0]); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + error = clEnqueueNDRangeKernel(out_of_order_queue, mul_kernel, 1, + nullptr, &num_elements, nullptr, 1, + &events[0], &events[1]); + test_error(error, "clEnqueueNDRangeKernel failed"); + + error = clEnqueueCommandBufferKHR( + 0, nullptr, out_of_order_command_buffer, 1, &events[1], &events[2]); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + std::vector output_data(num_elements); + error = clEnqueueReadBuffer(out_of_order_queue, out_mem, CL_TRUE, 0, + data_size(), output_data.data(), 1, + &events[2], nullptr); + test_error(error, "clEnqueueReadBuffer failed"); + + // Verify + const cl_int ref = pattern + 1; + for (size_t i = 0; i < num_elements; i++) + { + CHECK_VERIFICATION_ERROR(ref, output_data[i], i); + } + return CL_SUCCESS; + } + + clCommandQueueWrapper out_of_order_queue; + clCommandBufferWrapper out_of_order_command_buffer; + clEventWrapper events[3] = { nullptr, nullptr, nullptr }; +}; + +struct BarrierPipelined : public CommandBufferPipelined +{ + BarrierPipelined(cl_device_id device, cl_context context, + cl_command_queue queue) + : CommandBufferPipelined(device, context, queue), + out_of_order_queue(nullptr), out_of_order_command_buffer(this) + {} + + bool Skip() override + { + return CommandBufferPipelined::Skip() || !out_of_order_support; + } + + cl_int SetUp(int elements) override + { + cl_int error = CommandBufferPipelined::SetUp(elements); + test_error(error, "EventPipelined::SetUp failed"); + + out_of_order_queue = clCreateCommandQueue( + context, device, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &error); + test_error(error, "Unable to create command queue to test with"); + + out_of_order_command_buffer = + clCreateCommandBufferKHR(1, &out_of_order_queue, nullptr, &error); + test_error(error, "clCreateCommandBufferKHR failed"); + + return CL_SUCCESS; + } + + cl_int Run() override + { + cl_int error = RecordCommandBuffer(out_of_order_command_buffer); + test_error(error, "RecordCommandBuffer failed"); + + error = clEnqueueCommandBufferKHR( + 0, nullptr, out_of_order_command_buffer, 0, nullptr, nullptr); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + error = clEnqueueBarrier(out_of_order_queue); + test_error(error, "clEnqueueBarrier failed"); + + error = + clEnqueueNDRangeKernel(out_of_order_queue, mul_kernel, 1, nullptr, + &num_elements, nullptr, 0, nullptr, nullptr); + test_error(error, "clEnqueueNDRangeKernel failed"); + + error = clEnqueueBarrier(out_of_order_queue); + test_error(error, "clEnqueueBarrier failed"); + + error = clEnqueueCommandBufferKHR( + 0, nullptr, out_of_order_command_buffer, 0, nullptr, nullptr); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + error = clEnqueueBarrier(out_of_order_queue); + test_error(error, "clEnqueueBarrier failed"); + + std::vector output_data(num_elements); + error = clEnqueueReadBuffer(out_of_order_queue, out_mem, CL_TRUE, 0, + data_size(), output_data.data(), 0, nullptr, + nullptr); + test_error(error, "clEnqueueReadBuffer failed"); + + // Verify + const cl_int ref = pattern + 1; + for (size_t i = 0; i < num_elements; i++) + { + CHECK_VERIFICATION_ERROR(ref, output_data[i], i); + } + return CL_SUCCESS; + } + + clCommandQueueWrapper out_of_order_queue; + clCommandBufferWrapper out_of_order_command_buffer; +}; +} // anonymous namespace + +REGISTER_TEST(pipeline_in_order_deps) +{ + return MakeAndRunTest(device, context, queue, + num_elements); +} + +REGISTER_TEST(pipeline_event_deps) +{ + return MakeAndRunTest(device, context, queue, num_elements); +} + +REGISTER_TEST(pipeline_barrier_deps) +{ + return MakeAndRunTest(device, context, queue, + num_elements); +} diff --git a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_printf.cpp b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_printf.cpp index 0621ba1d..fee14571 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_printf.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_printf.cpp @@ -44,27 +44,18 @@ namespace { //////////////////////////////////////////////////////////////////////////////// -// printf tests for cl_khr_command_buffer which handles below cases: -// -test cases for device side printf -// -test cases for device side printf with a simultaneous use command-buffer +// Test for cl_khr_command_buffer which handles a command-buffer containing a +// printf kernel being repeatedly enqueued. -template struct CommandBufferPrintfTest : public BasicCommandBufferTest { CommandBufferPrintfTest(cl_device_id device, cl_context context, cl_command_queue queue) - : BasicCommandBufferTest(device, context, queue), - trigger_event(nullptr), wait_event(nullptr), file_descriptor(0), - printf_use_support(false) + : BasicCommandBufferTest(device, context, queue), file_descriptor(0) { - simultaneous_use_requested = simul_use; - if (simul_use) - { - buffer_size_multiplier = num_test_iters; - } + buffer_size_multiplier = num_test_iters; } - //-------------------------------------------------------------------------- void ReleaseOutputStream(int fd) { fflush(stdout); @@ -72,7 +63,6 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest close(fd); } - //-------------------------------------------------------------------------- int AcquireOutputStream(int* error) { int fd = streamDup(fileno(stdout)); @@ -85,7 +75,6 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest return fd; } - //-------------------------------------------------------------------------- void GetAnalysisBuffer(std::stringstream& buffer) { std::ifstream fp(temp_filename, std::ios::in); @@ -95,7 +84,6 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest } } - //-------------------------------------------------------------------------- void PurgeTempFile() { std::ofstream ofs(temp_filename, @@ -103,9 +91,10 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest ofs.close(); } - //-------------------------------------------------------------------------- bool Skip() override { + if (BasicCommandBufferTest::Skip()) return true; + // Query if device supports kernel printf use cl_device_command_buffer_capabilities_khr capabilities; cl_int error = @@ -114,16 +103,13 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest test_error(error, "Unable to query CL_DEVICE_COMMAND_BUFFER_CAPABILITIES_KHR"); - printf_use_support = + const bool printf_use_support = (capabilities & CL_COMMAND_BUFFER_CAPABILITY_KERNEL_PRINTF_KHR) != 0; - if (!printf_use_support) return true; - return BasicCommandBufferTest::Skip() - || (simultaneous_use_requested && !simultaneous_use_support); + return !printf_use_support; } - //-------------------------------------------------------------------------- cl_int SetUpKernel() override { cl_int error = CL_SUCCESS; @@ -153,14 +139,12 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- size_t data_size() const override { return sizeof(cl_char) * num_elements * buffer_size_multiplier * max_pattern_length; } - //-------------------------------------------------------------------------- cl_int SetUpKernelArgs() override { cl_int error = CL_SUCCESS; @@ -192,7 +176,6 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- cl_int SetUp(int elements) override { auto pcFname = get_temp_filename(); @@ -209,39 +192,10 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest return BasicCommandBufferTest::SetUp(elements); } - //-------------------------------------------------------------------------- - cl_int Run() override - { - cl_int error = CL_SUCCESS; - // record command buffer with primary queue - error = RecordCommandBuffer(); - test_error(error, "RecordCommandBuffer failed"); - - if (simultaneous_use_support) - { - // enqueue simultaneous command-buffers with printf calls - error = RunSimultaneous(); - test_error(error, "RunSimultaneous failed"); - } - else - { - // enqueue single command-buffer with printf calls - error = RunSingle(); - test_error(error, "RunSingle failed"); - } - - std::remove(temp_filename.c_str()); - - return CL_SUCCESS; - } - - //-------------------------------------------------------------------------- cl_int RecordCommandBuffer() { - cl_int error = CL_SUCCESS; - - error = clCommandNDRangeKernelKHR( + cl_int error = clCommandNDRangeKernelKHR( command_buffer, nullptr, nullptr, kernel, 1, nullptr, &num_elements, nullptr, 0, nullptr, nullptr, nullptr); test_error(error, "clCommandNDRangeKernelKHR failed"); @@ -251,7 +205,6 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- #define test_error_release_stdout(errCode, msg) \ { \ auto errCodeResult = errCode; \ @@ -263,96 +216,7 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest } \ } - //-------------------------------------------------------------------------- - cl_int EnqueueSinglePass(const std::vector& pattern, - std::vector& output_data) - { - cl_int error = CL_SUCCESS; - auto in_mem_size = sizeof(cl_char) * pattern.size(); - error = clEnqueueWriteBuffer(queue, in_mem, CL_TRUE, 0, in_mem_size, - &pattern[0], 0, nullptr, nullptr); - test_error(error, "clEnqueueWriteBuffer failed"); - - test_assert_error(pattern.size() - 1 <= CL_UINT_MAX, - "pattern.size() - 1 does not fit in a cl_uint"); - cl_uint offset[] = { 0, static_cast(pattern.size() - 1) }; - error = clEnqueueWriteBuffer(queue, off_mem, CL_TRUE, 0, sizeof(offset), - offset, 0, nullptr, nullptr); - test_error(error, "clEnqueueWriteBuffer failed"); - - // redirect output stream to temporary file - file_descriptor = AcquireOutputStream(&error); - if (error != 0) - { - log_error("Error while redirection stdout to file"); - return TEST_FAIL; - } - - // enqueue command buffer with kernel containing printf command - error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0, - nullptr, &wait_event); - test_error_release_stdout(error, "clEnqueueCommandBufferKHR failed"); - - fflush(stdout); - - // Wait until kernel finishes its execution and (thus) the output - // printed from the kernel is immediately printed - error = clWaitForEvents(1, &wait_event); - test_error(error, "clWaitForEvents failed"); - - // output buffer contains pattern to be compared with printout - error = clEnqueueReadBuffer(queue, out_mem, CL_FALSE, 0, data_size(), - output_data.data(), 0, nullptr, nullptr); - test_error_release_stdout(error, "clEnqueueReadBuffer failed"); - - error = clFinish(queue); - test_error_release_stdout(error, "clFinish failed"); - - ReleaseOutputStream(file_descriptor); - - // copy content of temporary file into string stream - std::stringstream sstr; - GetAnalysisBuffer(sstr); - if (sstr.str().size() != num_elements * offset[1]) - { - log_error("GetAnalysisBuffer failed\n"); - return TEST_FAIL; - } - - // verify the result - compare printout and output buffer - for (size_t i = 0; i < num_elements * offset[1]; i++) - { - CHECK_VERIFICATION_ERROR(sstr.str().at(i), output_data[i], i); - } - - return CL_SUCCESS; - } - - //-------------------------------------------------------------------------- - cl_int RunSingle() - { - cl_int error = CL_SUCCESS; - std::vector output_data(num_elements * max_pattern_length); - - for (unsigned i = 0; i < num_test_iters; i++) - { - unsigned pattern_length = - std::max(min_pattern_length, rand() % max_pattern_length); - char pattern_character = 'a' + rand() % 26; - std::vector pattern(pattern_length + 1, pattern_character); - pattern[pattern_length] = '\0'; - error = EnqueueSinglePass(pattern, output_data); - test_error(error, "EnqueueSinglePass failed"); - - output_data.assign(output_data.size(), 0); - PurgeTempFile(); - } - - return CL_SUCCESS; - } - - //-------------------------------------------------------------------------- - struct SimulPassData + struct EnqueuePassData { // null terminated character buffer std::vector pattern; @@ -361,8 +225,7 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest std::vector output_buffer; }; - //-------------------------------------------------------------------------- - cl_int EnqueueSimultaneousPass(SimulPassData& pd) + cl_int EnqueuePass(EnqueuePassData& pd) { // write current pattern to device memory auto in_mem_size = sizeof(cl_char) * pd.pattern.size(); @@ -377,15 +240,8 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest pd.offset, 0, nullptr, nullptr); test_error_release_stdout(error, "clEnqueueWriteBuffer failed"); - // create user event to block simultaneous command buffers - if (!trigger_event) - { - trigger_event = clCreateUserEvent(context, &error); - test_error_release_stdout(error, "clCreateUserEvent failed"); - } - - error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 1, - &trigger_event, nullptr); + error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0, + nullptr, nullptr); test_error_release_stdout(error, "clEnqueueCommandBufferKHR failed"); // output buffer contains pattern to be compared with printout @@ -398,14 +254,14 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest return CL_SUCCESS; } - - //-------------------------------------------------------------------------- - cl_int RunSimultaneous() + cl_int Run() override { - cl_int error = CL_SUCCESS; + cl_int error = RecordCommandBuffer(); + test_error(error, "RecordCommandBuffer failed"); + cl_int offset = static_cast(num_elements * max_pattern_length); - std::vector simul_passes(num_test_iters); + std::vector enqueue_passes(num_test_iters); const int pattern_chars_range = 26; std::list pattern_chars; @@ -413,7 +269,7 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest pattern_chars.push_back(cl_char('a' + i)); test_assert_error(pattern_chars.size() >= num_test_iters, - "Number of simultaneous launches must be lower than " + "Number of launches must be lower than " "size of characters container"); cl_int total_pattern_coverage = 0; @@ -428,11 +284,12 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest std::vector pattern(pattern_length + 1, pattern_character); pattern.back() = '\0'; - simul_passes[i] = { pattern, - { cl_int(i * offset), cl_int(pattern_length) }, - std::vector(num_elements - * pattern_length) }; - total_pattern_coverage += simul_passes[i].output_buffer.size(); + enqueue_passes[i] = { + pattern, + { cl_int(i * offset), cl_int(pattern_length) }, + std::vector(num_elements * pattern_length) + }; + total_pattern_coverage += enqueue_passes[i].output_buffer.size(); pattern_chars.erase(it); }; @@ -444,17 +301,14 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest return TEST_FAIL; } - // enqueue read/write and command buffer operations - for (auto&& pass : simul_passes) + // enqueue read/write and command buffer operations, serialized + // by in-order queue + for (auto&& pass : enqueue_passes) { - error = EnqueueSimultaneousPass(pass); - test_error_release_stdout(error, "EnqueueSimultaneousPass failed"); + error = EnqueuePass(pass); + test_error_release_stdout(error, "EnqueuePass failed"); } - // execute command buffers - error = clSetUserEventStatus(trigger_event, CL_COMPLETE); - test_error_release_stdout(error, "clSetUserEventStatus failed"); - // flush streams fflush(stdout); @@ -477,13 +331,13 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest for (int i = 0; i < total_pattern_coverage; i++) counters_map[sstr.str().at(i)]++; - if (counters_map.size() != simul_passes.size()) + if (counters_map.size() != enqueue_passes.size()) { log_error("printout inconsistent with input data\n"); return TEST_FAIL; } - for (auto&& pass : simul_passes) + for (auto&& pass : enqueue_passes) { auto& res_data = pass.output_buffer; @@ -501,18 +355,13 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest } } + std::remove(temp_filename.c_str()); return CL_SUCCESS; } - //-------------------------------------------------------------------------- - clEventWrapper trigger_event = nullptr; - clEventWrapper wait_event = nullptr; - std::string temp_filename; int file_descriptor; - bool printf_use_support; - // specifies max test length for printf pattern const unsigned max_pattern_length = 6; // specifies min test length for printf pattern @@ -523,14 +372,8 @@ struct CommandBufferPrintfTest : public BasicCommandBufferTest } // anonymous namespace -REGISTER_TEST(basic_printf) +REGISTER_TEST(printf) { - return MakeAndRunTest>(device, context, - queue, num_elements); -} - -REGISTER_TEST(simultaneous_printf) -{ - return MakeAndRunTest>(device, context, queue, - num_elements); + return MakeAndRunTest(device, context, queue, + num_elements); } diff --git a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_profiling.cpp b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_profiling.cpp index 2c7f4b3c..5a91855d 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_profiling.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_profiling.cpp @@ -86,21 +86,17 @@ cl_int VerifyResult(const clEventWrapper& event) } //////////////////////////////////////////////////////////////////////////////// -// Command-buffer profiling test cases: -// -all commands are recorded to a single command-queue -// -profiling a command-buffer with simultaneous use -template +// Command-buffer profiling test for enqueuing command-buffer twice and checking +// the profiling counters on the events returned. struct CommandBufferProfiling : public BasicCommandBufferTest { CommandBufferProfiling(cl_device_id device, cl_context context, cl_command_queue queue) - : BasicCommandBufferTest(device, context, queue), wait_event(nullptr) + : BasicCommandBufferTest(device, context, queue) { - simultaneous_use_requested = simultaneous_request; - if (simultaneous_request) buffer_size_multiplier = 2; + buffer_size_multiplier = 2; // Do two enqueues of command-buffer } - //-------------------------------------------------------------------------- bool Skip() override { if (BasicCommandBufferTest::Skip()) return true; @@ -127,10 +123,9 @@ struct CommandBufferProfiling : public BasicCommandBufferTest "Queue property CL_QUEUE_PROFILING_ENABLE not supported \n"); return true; } - return (simultaneous_use_requested && !simultaneous_use_support); + return false; } - //-------------------------------------------------------------------------- cl_int SetUp(int elements) override { @@ -156,37 +151,45 @@ struct CommandBufferProfiling : public BasicCommandBufferTest return BasicCommandBufferTest::SetUp(elements); } - //-------------------------------------------------------------------------- + struct EnqueuePassData + { + cl_int offset; + clEventWrapper query_event; + }; + cl_int Run() override { - cl_int error = CL_SUCCESS; - - // record command buffer - error = RecordCommandBuffer(); + cl_int error = RecordCommandBuffer(); test_error(error, "RecordCommandBuffer failed"); - if (simultaneous_use_requested) + cl_int offset = static_cast(num_elements); + + std::vector enqueue_passes = { + { 0, clEventWrapper() }, { offset, clEventWrapper() } + }; + + // In-order queue serialized the command-buffer submissions + for (auto&& pass : enqueue_passes) { - // enqueue simultaneous command-buffers with profiling command queue - error = RunSimultaneous(); - test_error(error, "RunSimultaneous failed"); + error = EnqueuePass(pass); + test_error(error, "EnqueueSerializedPass failed"); } - else + + error = clFinish(queue); + test_error(error, "clFinish failed"); + + for (auto&& pass : enqueue_passes) { - // enqueue single command-buffer with profiling command queue - error = RunSingle(); - test_error(error, "RunSingle failed"); + error = VerifyResult(pass.query_event); + test_error(error, "VerifyResult failed"); } return CL_SUCCESS; } - //-------------------------------------------------------------------------- cl_int RecordCommandBuffer() { - cl_int error = CL_SUCCESS; - - error = clCommandNDRangeKernelKHR( + cl_int error = clCommandNDRangeKernelKHR( command_buffer, nullptr, nullptr, kernel, 1, nullptr, &num_elements, nullptr, 0, nullptr, nullptr, nullptr); test_error(error, "clCommandNDRangeKernelKHR failed"); @@ -196,41 +199,7 @@ struct CommandBufferProfiling : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- - cl_int RunSingle() - { - cl_int error = CL_SUCCESS; - std::vector output_data(num_elements); - - error = clEnqueueFillBuffer(queue, in_mem, &pattern, sizeof(cl_int), 0, - data_size(), 0, nullptr, nullptr); - test_error(error, "clEnqueueFillBuffer failed"); - - clEventWrapper query_event; - error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0, - nullptr, &query_event); - test_error(error, "clEnqueueCommandBufferKHR failed"); - - error = clEnqueueReadBuffer(queue, out_mem, CL_TRUE, 0, data_size(), - output_data.data(), 0, nullptr, nullptr); - test_error(error, "clEnqueueReadBuffer failed"); - - error = VerifyResult(query_event); - test_error(error, "VerifyResult failed"); - - return CL_SUCCESS; - } - - //-------------------------------------------------------------------------- - struct SimulPassData - { - cl_int offset; - std::vector output_buffer; - clEventWrapper query_event; - }; - - //-------------------------------------------------------------------------- - cl_int EnqueueSimultaneousPass(SimulPassData& pd) + cl_int EnqueuePass(EnqueuePassData& pd) { cl_int error = clEnqueueFillBuffer( queue, out_mem, &pattern, sizeof(cl_int), @@ -241,59 +210,13 @@ struct CommandBufferProfiling : public BasicCommandBufferTest 0, sizeof(cl_int), 0, nullptr, nullptr); test_error(error, "clEnqueueFillBuffer failed"); - if (!wait_event) - { - wait_event = clCreateUserEvent(context, &error); - test_error(error, "clCreateUserEvent failed"); - } - - error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 1, - &wait_event, &pd.query_event); + error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0, + nullptr, &pd.query_event); test_error(error, "clEnqueueCommandBufferKHR failed"); - error = clEnqueueReadBuffer( - queue, out_mem, CL_FALSE, pd.offset * sizeof(cl_int), data_size(), - pd.output_buffer.data(), 0, nullptr, nullptr); - test_error(error, "clEnqueueReadBuffer failed"); - return CL_SUCCESS; } - //-------------------------------------------------------------------------- - cl_int RunSimultaneous() - { - cl_int error = CL_SUCCESS; - cl_int offset = static_cast(num_elements); - - std::vector simul_passes = { - { 0, std::vector(num_elements) }, - { offset, std::vector(num_elements) } - }; - - for (auto&& pass : simul_passes) - { - error = EnqueueSimultaneousPass(pass); - test_error(error, "EnqueueSimultaneousPass failed"); - } - - error = clSetUserEventStatus(wait_event, CL_COMPLETE); - test_error(error, "clSetUserEventStatus failed"); - - error = clFinish(queue); - test_error(error, "clFinish failed"); - - for (auto&& pass : simul_passes) - { - error = VerifyResult(pass.query_event); - test_error(error, "VerifyResult failed"); - } - - return CL_SUCCESS; - } - - //-------------------------------------------------------------------------- - clEventWrapper wait_event; - const cl_int pattern = 0xA; }; @@ -356,19 +279,13 @@ struct CommandBufferSubstituteQueueProfiling : public BasicCommandBufferTest }; } // anonymous namespace -REGISTER_TEST(basic_profiling) +REGISTER_TEST(profiling) { - return MakeAndRunTest>(device, context, queue, - num_elements); + return MakeAndRunTest(device, context, queue, + num_elements); } -REGISTER_TEST(simultaneous_profiling) -{ - return MakeAndRunTest>(device, context, queue, - num_elements); -} - -REGISTER_TEST(substitute_queue_profiling) +REGISTER_TEST(profiling_substitute_queue) { return MakeAndRunTest( device, context, queue, num_elements); diff --git a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_queue_substitution.cpp b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_queue_substitution.cpp index 22d19719..0081d79c 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_queue_substitution.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_queue_substitution.cpp @@ -23,21 +23,16 @@ namespace { // Command-queue substitution tests which handles below cases: // -substitution on queue without properties // -substitution on queue with properties -// -simultaneous use queue substitution -template +template struct SubstituteQueueTest : public BasicCommandBufferTest { SubstituteQueueTest(cl_device_id device, cl_context context, cl_command_queue queue) : BasicCommandBufferTest(device, context, queue), - properties_use_requested(prop_use), user_event(nullptr) - { - simultaneous_use_requested = simul_use; - if (simul_use) buffer_size_multiplier = 2; - } + properties_use_requested(prop_use) + {} - //-------------------------------------------------------------------------- bool Skip() override { if (properties_use_requested) @@ -57,11 +52,9 @@ struct SubstituteQueueTest : public BasicCommandBufferTest return true; } - return BasicCommandBufferTest::Skip() - || (simultaneous_use_requested && !simultaneous_use_support); + return BasicCommandBufferTest::Skip(); } - //-------------------------------------------------------------------------- cl_int SetUp(int elements) override { // By default command queue is created without properties, @@ -81,7 +74,6 @@ struct SubstituteQueueTest : public BasicCommandBufferTest return BasicCommandBufferTest::SetUp(elements); } - //-------------------------------------------------------------------------- cl_int Run() override { // record command buffer with primary queue @@ -106,23 +98,14 @@ struct SubstituteQueueTest : public BasicCommandBufferTest test_error(error, "clCreateCommandQueue failed"); } - if (simultaneous_use_support) - { - // enque simultaneous command-buffers with substitute queue - error = RunSimultaneous(new_queue); - test_error(error, "RunSimultaneous failed"); - } - else - { - // enque single command-buffer with substitute queue - error = RunSingle(new_queue); - test_error(error, "RunSingle failed"); - } + + // enqueue single command-buffer with substitute queue + error = RunSingle(new_queue); + test_error(error, "RunSingle failed"); return CL_SUCCESS; } - //-------------------------------------------------------------------------- cl_int RecordCommandBuffer() { cl_int error = clCommandNDRangeKernelKHR( @@ -135,14 +118,13 @@ struct SubstituteQueueTest : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- cl_int RunSingle(const cl_command_queue& q) { - cl_int error = CL_SUCCESS; std::vector output_data(num_elements); - error = clEnqueueFillBuffer(q, in_mem, &pattern_pri, sizeof(cl_int), 0, - data_size(), 0, nullptr, nullptr); + cl_int error = + clEnqueueFillBuffer(q, in_mem, &pattern_pri, sizeof(cl_int), 0, + data_size(), 0, nullptr, nullptr); test_error(error, "clEnqueueFillBuffer failed"); cl_command_queue queues[] = { q }; @@ -165,90 +147,8 @@ struct SubstituteQueueTest : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- - struct SimulPassData - { - cl_int pattern; - cl_int offset; - cl_command_queue queue; - std::vector output_buffer; - }; - - //-------------------------------------------------------------------------- - cl_int EnqueueSimultaneousPass(SimulPassData& pd) - { - cl_int error = clEnqueueFillBuffer( - pd.queue, in_mem, &pd.pattern, sizeof(cl_int), - pd.offset * sizeof(cl_int), data_size(), 0, nullptr, nullptr); - test_error(error, "clEnqueueFillBuffer failed"); - - error = - clEnqueueFillBuffer(pd.queue, off_mem, &pd.offset, sizeof(cl_int), - 0, sizeof(cl_int), 0, nullptr, nullptr); - test_error(error, "clEnqueueFillBuffer failed"); - - if (!user_event) - { - user_event = clCreateUserEvent(context, &error); - test_error(error, "clCreateUserEvent failed"); - } - - cl_command_queue queues[] = { pd.queue }; - error = clEnqueueCommandBufferKHR(1, queues, command_buffer, 1, - &user_event, nullptr); - test_error(error, "clEnqueueCommandBufferKHR failed"); - - error = clEnqueueReadBuffer( - pd.queue, out_mem, CL_FALSE, pd.offset * sizeof(cl_int), - data_size(), pd.output_buffer.data(), 0, nullptr, nullptr); - - test_error(error, "clEnqueueReadBuffer failed"); - - return CL_SUCCESS; - } - - //-------------------------------------------------------------------------- - cl_int RunSimultaneous(const cl_command_queue& q) - { - cl_int error = CL_SUCCESS; - cl_int offset = static_cast(num_elements); - - std::vector simul_passes = { - { pattern_pri, 0, q, std::vector(num_elements) }, - { pattern_sec, offset, q, std::vector(num_elements) } - }; - - for (auto&& pass : simul_passes) - { - error = EnqueueSimultaneousPass(pass); - test_error(error, "EnqueuePass failed"); - } - - error = clSetUserEventStatus(user_event, CL_COMPLETE); - test_error(error, "clSetUserEventStatus failed"); - - for (auto&& pass : simul_passes) - { - error = clFinish(pass.queue); - test_error(error, "clFinish failed"); - - auto& res_data = pass.output_buffer; - - for (size_t i = 0; i < num_elements; i++) - { - CHECK_VERIFICATION_ERROR(pass.pattern, res_data[i], i); - } - } - - return CL_SUCCESS; - } - - //-------------------------------------------------------------------------- const cl_int pattern_pri = 0xB; - const cl_int pattern_sec = 0xC; - bool properties_use_requested; - clEventWrapper user_event; }; // Command-queue substitution tests which handles below cases: @@ -397,20 +297,14 @@ struct QueueOrderTest : public BasicCommandBufferTest REGISTER_TEST(queue_substitution) { - return MakeAndRunTest>( - device, context, queue, num_elements); + return MakeAndRunTest>(device, context, queue, + num_elements); } -REGISTER_TEST(properties_queue_substitution) +REGISTER_TEST(queue_substitution_properties) { - return MakeAndRunTest>( - device, context, queue, num_elements); -} - -REGISTER_TEST(simultaneous_queue_substitution) -{ - return MakeAndRunTest>( - device, context, queue, num_elements); + return MakeAndRunTest>(device, context, queue, + num_elements); } REGISTER_TEST(queue_substitute_in_order) diff --git a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_set_kernel_arg.cpp b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_set_kernel_arg.cpp index 44954ce6..69926921 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/command_buffer_set_kernel_arg.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/command_buffer_set_kernel_arg.cpp @@ -22,25 +22,22 @@ namespace { //////////////////////////////////////////////////////////////////////////////// // clSetKernelArg tests for cl_khr_command_buffer which handles below cases: -// -test interactions of clSetKernelArg with command-buffers -// -test interactions of clSetKernelArg on a command-buffer pending execution +// -test interactions of clSetKernelArg after command-buffer finalize but +// before enqueue +// -test interactions of clSetKernelArg between command-buffer enqueue -template +template struct CommandBufferSetKernelArg : public BasicCommandBufferTest { CommandBufferSetKernelArg(cl_device_id device, cl_context context, cl_command_queue queue) - : BasicCommandBufferTest(device, context, queue), trigger_event(nullptr) + : BasicCommandBufferTest(device, context, queue) { - simultaneous_use_requested = simul_use; - if (simul_use) buffer_size_multiplier = 2; + if (enqueue_test) buffer_size_multiplier = 2; } - //-------------------------------------------------------------------------- cl_int SetUpKernel() override { - cl_int error = CL_SUCCESS; - const char* kernel_str = R"( __kernel void copy(int in, __global int* out, __global int* offset) @@ -50,8 +47,8 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest out[ind] = in; })"; - error = create_single_kernel_helper_create_program(context, &program, 1, - &kernel_str); + cl_int error = create_single_kernel_helper_create_program( + context, &program, 1, &kernel_str); test_error(error, "Failed to create program with source"); error = clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr); @@ -63,7 +60,6 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- cl_int SetUpKernelArgs() override { cl_int error = CL_SUCCESS; @@ -99,15 +95,14 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- cl_int Run() override { cl_int error = CL_SUCCESS; - if (simultaneous_use_requested) + if (enqueue_test) { - // enqueue simultaneous command-buffers with clSetKernelArg calls - error = RunSimultaneous(); - test_error(error, "RunSimultaneous failed"); + // enqueue command-buffers with clSetKernelArg calls in between + error = RunMultipleEnqueue(); + test_error(error, "RunMultipleEnqueue failed"); } else { @@ -119,12 +114,9 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- cl_int RecordCommandBuffer() { - cl_int error = CL_SUCCESS; - - error = clCommandNDRangeKernelKHR( + cl_int error = clCommandNDRangeKernelKHR( command_buffer, nullptr, nullptr, kernel, 1, nullptr, &num_elements, nullptr, 0, nullptr, nullptr, nullptr); test_error(error, "clCommandNDRangeKernelKHR failed"); @@ -148,14 +140,12 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- cl_int RunSingle() { - cl_int error = CL_SUCCESS; std::vector output_data(num_elements); // record command buffer - error = RecordCommandBuffer(); + cl_int error = RecordCommandBuffer(); test_error(error, "RecordCommandBuffer failed"); const cl_int pattern_base = 0; @@ -187,20 +177,16 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- - struct SimulPassData + struct EnqueuePassData { cl_int pattern; cl_int offset; std::vector output_buffer; }; - //-------------------------------------------------------------------------- - cl_int RecordSimultaneousCommandBuffer() const + cl_int RecordEnqueueCommandBuffer() const { - cl_int error = CL_SUCCESS; - - error = clCommandNDRangeKernelKHR( + cl_int error = clCommandNDRangeKernelKHR( command_buffer, nullptr, nullptr, kernel, 1, nullptr, &num_elements, nullptr, 0, nullptr, nullptr, nullptr); test_error(error, "clCommandNDRangeKernelKHR failed"); @@ -210,8 +196,7 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- - cl_int EnqueueSimultaneousPass(SimulPassData& pd) + cl_int EnqueuePass(EnqueuePassData& pd) { cl_int error = clEnqueueFillBuffer( queue, out_mem, &pd.pattern, sizeof(cl_int), @@ -222,14 +207,8 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest 0, sizeof(cl_int), 0, nullptr, nullptr); test_error(error, "clEnqueueFillBuffer failed"); - if (!trigger_event) - { - trigger_event = clCreateUserEvent(context, &error); - test_error(error, "clCreateUserEvent failed"); - } - - error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 1, - &trigger_event, nullptr); + error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0, + nullptr, nullptr); test_error(error, "clEnqueueCommandBufferKHR failed"); error = clEnqueueReadBuffer( @@ -240,49 +219,39 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- - cl_int RunSimultaneous() + cl_int RunMultipleEnqueue() { - cl_int error = CL_SUCCESS; - // record command buffer with primary queue - error = RecordSimultaneousCommandBuffer(); - test_error(error, "RecordSimultaneousCommandBuffer failed"); + cl_int error = RecordEnqueueCommandBuffer(); + test_error(error, "RecordEnqueeuCommandBuffer failed"); - std::vector simul_passes = { - { 0, 0, std::vector(num_elements) } + cl_int offset = static_cast(num_elements); + std::vector enqueue_passes = { + { 0, 0, std::vector(num_elements) }, + { 1, offset, std::vector(num_elements) } }; - error = EnqueueSimultaneousPass(simul_passes.front()); - test_error(error, "EnqueueSimultaneousPass 1 failed"); - - // changing kernel args at this point should have no effect, - // test will verify if clSetKernelArg didn't affect command-buffer - cl_int in_arg = pattern_sec; - error = clSetKernelArg(kernel, 0, sizeof(cl_int), &in_arg); - test_error(error, "clSetKernelArg failed"); - - error = clSetKernelArg(kernel, 1, sizeof(out_mem_k2), &out_mem_k2); - test_error(error, "clSetKernelArg failed"); - - if (simultaneous_use_support) + for (auto&& pass : enqueue_passes) { - cl_int offset = static_cast(num_elements); - simul_passes.push_back( - { 1, offset, std::vector(num_elements) }); + // changing kernel args at this point should have no effect, + // test will verify if clSetKernelArg didn't affect command-buffer + cl_int in_arg = pattern_sec; + error = clSetKernelArg(kernel, 0, sizeof(cl_int), &in_arg); + test_error(error, "clSetKernelArg failed"); - error = EnqueueSimultaneousPass(simul_passes.back()); - test_error(error, "EnqueueSimultaneousPass 2 failed"); + error = clSetKernelArg(kernel, 1, sizeof(out_mem_k2), &out_mem_k2); + test_error(error, "clSetKernelArg failed"); + + + error = EnqueuePass(pass); + test_error(error, "EnqueuePass failed"); } - error = clSetUserEventStatus(trigger_event, CL_COMPLETE); - test_error(error, "clSetUserEventStatus failed"); - error = clFinish(queue); test_error(error, "clFinish failed"); // verify the result buffer - for (auto&& pass : simul_passes) + for (auto&& pass : enqueue_passes) { auto& res_data = pass.output_buffer; for (size_t i = 0; i < num_elements; i++) @@ -294,9 +263,6 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest return CL_SUCCESS; } - //-------------------------------------------------------------------------- - clEventWrapper trigger_event = nullptr; - const cl_int pattern_pri = 2; const cl_int pattern_sec = 3; @@ -305,13 +271,13 @@ struct CommandBufferSetKernelArg : public BasicCommandBufferTest } // anonymous namespace -REGISTER_TEST(basic_set_kernel_arg) +REGISTER_TEST(set_kernel_arg_after_finalize) { return MakeAndRunTest>( device, context, queue, num_elements); } -REGISTER_TEST(pending_set_kernel_arg) +REGISTER_TEST(set_kernel_arg_after_enqueue) { return MakeAndRunTest>(device, context, queue, num_elements); diff --git a/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_create.cpp b/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_create.cpp index fa087930..601eb7a6 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_create.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_create.cpp @@ -131,9 +131,10 @@ struct CreateCommandBufferRepeatedProperties : public BasicCommandBufferTest if (BasicCommandBufferTest::Skip()) return true; bool skip = true; - if (simultaneous_use_support) + if (is_extension_available( + device, CL_KHR_COMMAND_BUFFER_MUTABLE_DISPATCH_EXTENSION_NAME)) { - rep_prop = CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR; + rep_prop = CL_COMMAND_BUFFER_MUTABLE_KHR; skip = false; } else if (is_extension_available( @@ -142,13 +143,6 @@ struct CreateCommandBufferRepeatedProperties : public BasicCommandBufferTest rep_prop = CL_COMMAND_BUFFER_DEVICE_SIDE_SYNC_KHR; skip = false; } - else if (is_extension_available( - device, - CL_KHR_COMMAND_BUFFER_MUTABLE_DISPATCH_EXTENSION_NAME)) - { - rep_prop = CL_COMMAND_BUFFER_MUTABLE_KHR; - skip = false; - } return skip; } @@ -185,7 +179,9 @@ struct CreateCommandBufferNotSupportedProperties : public BasicCommandBufferTest if (BasicCommandBufferTest::Skip()) return true; bool skip = true; - if (!simultaneous_use_support) + if (is_extension_available( + device, CL_KHR_COMMAND_BUFFER_MUTABLE_DISPATCH_EXTENSION_NAME) + && !simultaneous_use_support) { unsupported_prop = CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR; skip = false; diff --git a/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_enqueue.cpp b/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_enqueue.cpp index ae14b87b..aac579cb 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_enqueue.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_enqueue.cpp @@ -66,102 +66,6 @@ struct EnqueueCommandBufferNotFinalized : public BasicCommandBufferTest } }; -// CL_INVALID_OPERATION if command_buffer was not created with the -// CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR flag and is in the Pending state. -struct EnqueueCommandBufferWithoutSimultaneousUseNotInPendingState - : public BasicCommandBufferTest -{ - EnqueueCommandBufferWithoutSimultaneousUseNotInPendingState( - cl_device_id device, cl_context context, cl_command_queue queue) - : BasicCommandBufferTest(device, context, queue), user_event(nullptr) - {} - - cl_int Run() override - { - cl_int error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0, - nullptr, nullptr); - - test_failure_error_ret(error, CL_INVALID_OPERATION, - "clEnqueueCommandBufferKHR should return " - "CL_INVALID_OPERATION", - TEST_FAIL); - - error = clSetUserEventStatus(user_event, CL_COMPLETE); - test_error(error, "clSetUserEventStatus failed"); - clFinish(queue); - - return CL_SUCCESS; - } - - cl_int SetUp(int elements) override - { - auto verify_state = [&](const cl_command_buffer_state_khr &expected) { - cl_command_buffer_state_khr state = ~cl_command_buffer_state_khr(0); - - cl_int error = clGetCommandBufferInfoKHR( - command_buffer, CL_COMMAND_BUFFER_STATE_KHR, sizeof(state), - &state, nullptr); - test_error_ret(error, "clGetCommandBufferInfoKHR failed", - TEST_FAIL); - - test_assert_error( - state == expected, - "Unexpected result of CL_COMMAND_BUFFER_STATE_KHR query!"); - - return TEST_PASS; - }; - - cl_int error = BasicCommandBufferTest::SetUp(elements); - test_error(error, "BasicCommandBufferTest::SetUp failed"); - - command_buffer = clCreateCommandBufferKHR(1, &queue, nullptr, &error); - test_error(error, "clCreateCommandBufferKHR failed"); - - error = RecordCommandBuffer(); - test_error(error, "RecordCommandBuffer failed"); - error = verify_state(CL_COMMAND_BUFFER_STATE_EXECUTABLE_KHR); - test_error(error, "State is not Executable"); - - error = EnqueueCommandBuffer(); - test_error(error, "EnqueueCommandBuffer failed"); - - return CL_SUCCESS; - } - - cl_int RecordCommandBuffer() - { - cl_int error = clCommandNDRangeKernelKHR( - command_buffer, nullptr, nullptr, kernel, 1, nullptr, &num_elements, - nullptr, 0, nullptr, nullptr, nullptr); - test_error(error, "clCommandNDRangeKernelKHR failed"); - - error = clFinalizeCommandBufferKHR(command_buffer); - test_error(error, "clFinalizeCommandBufferKHR failed"); - - return CL_SUCCESS; - } - - cl_int EnqueueCommandBuffer() - { - cl_int pattern = 0xE; - - cl_int error = - clEnqueueFillBuffer(queue, out_mem, &pattern, sizeof(cl_int), 0, - data_size(), 0, nullptr, nullptr); - test_error(error, "clEnqueueFillBuffer failed"); - - user_event = clCreateUserEvent(context, &error); - test_error(error, "clCreateUserEvent failed"); - - error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 1, - &user_event, nullptr); - test_error(error, "clEnqueueCommandBufferKHR failed"); - - return CL_SUCCESS; - } - clEventWrapper user_event; -}; - // CL_INVALID_VALUE if queues is NULL and num_queues is > 0, or queues is not // NULL and num_queues is 0. struct EnqueueCommandBufferNullQueuesNumQueues : public BasicCommandBufferTest @@ -623,14 +527,6 @@ REGISTER_TEST(negative_enqueue_command_buffer_not_finalized) device, context, queue, num_elements); } -REGISTER_TEST( - negative_enqueue_command_buffer_without_simultaneous_no_pending_state) -{ - return MakeAndRunTest< - EnqueueCommandBufferWithoutSimultaneousUseNotInPendingState>( - device, context, queue, num_elements); -} - REGISTER_TEST(negative_enqueue_command_buffer_null_queues_num_queues) { return MakeAndRunTest( diff --git a/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_finalize.cpp b/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_finalize.cpp index 05f43506..85b4ef4d 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_finalize.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/negative_command_buffer_finalize.cpp @@ -44,20 +44,9 @@ struct FinalizeCommandBufferNotRecordingState : public BasicCommandBufferTest FinalizeCommandBufferNotRecordingState(cl_device_id device, cl_context context, cl_command_queue queue) - : BasicCommandBufferTest(device, context, queue), user_event(nullptr) + : BasicCommandBufferTest(device, context, queue) {} - cl_int SetUp(int elements) override - { - cl_int error = BasicCommandBufferTest::SetUp(elements); - test_error(error, "BasicCommandBufferTest::SetUp failed"); - - user_event = clCreateUserEvent(context, &error); - test_error(error, "clCreateUserEvent failed"); - - return CL_SUCCESS; - } - cl_int Run() override { auto verify_state = [&](const cl_command_buffer_state_khr &expected) { @@ -87,18 +76,6 @@ struct FinalizeCommandBufferNotRecordingState : public BasicCommandBufferTest "CL_INVALID_OPERATION", TEST_FAIL); - error = EnqueueCommandBuffer(); - test_error(error, "EnqueueCommandBuffer failed"); - - error = clFinalizeCommandBufferKHR(command_buffer); - test_failure_error_ret(error, CL_INVALID_OPERATION, - "clFinalizeCommandBufferKHR should return " - "CL_INVALID_OPERATION", - TEST_FAIL); - - clSetUserEventStatus(user_event, CL_COMPLETE); - clFinish(queue); - return CL_SUCCESS; } @@ -114,22 +91,6 @@ struct FinalizeCommandBufferNotRecordingState : public BasicCommandBufferTest return CL_SUCCESS; } - - cl_int EnqueueCommandBuffer() - { - cl_int pattern = 0xE; - cl_int error = - clEnqueueFillBuffer(queue, out_mem, &pattern, sizeof(cl_int), 0, - data_size(), 0, nullptr, nullptr); - test_error(error, "clEnqueueFillBuffer failed"); - - error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 1, - &user_event, nullptr); - test_error(error, "clEnqueueCommandBufferKHR failed"); - - return CL_SUCCESS; - } - clEventWrapper user_event; }; }; From 34745bd9368e2a37544c1f878c29381e703f8fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Petit?= Date: Tue, 14 Oct 2025 17:58:40 +0100 Subject: [PATCH 16/33] Disable {svm_,}atomic_fence tests (#2545) As agreed in 2025/10/14 teleconference. See #2544. Signed-off-by: Kevin Petit --- test_conformance/c11_atomics/test_atomics.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test_conformance/c11_atomics/test_atomics.cpp b/test_conformance/c11_atomics/test_atomics.cpp index 9a0a306d..40cc8d27 100644 --- a/test_conformance/c11_atomics/test_atomics.cpp +++ b/test_conformance/c11_atomics/test_atomics.cpp @@ -3961,6 +3961,9 @@ private: struct TestDefinition _subCase; }; +#if 0 +// The tests below are likely incorrect and have been disabled. +// See https://github.com/KhronosGroup/OpenCL-CTS/issues/2544 static int test_atomic_fence_generic(cl_device_id deviceID, cl_context context, cl_command_queue queue, int num_elements, bool useSVM) @@ -4040,3 +4043,4 @@ REGISTER_TEST(svm_atomic_fence) return test_atomic_fence_generic(device, context, queue, num_elements, true); } +#endif From 940c8bb973692ad227a20d7a79694282f527ec2d Mon Sep 17 00:00:00 2001 From: Marcin Hajder Date: Tue, 21 Oct 2025 17:43:05 +0200 Subject: [PATCH 17/33] Added support for cl_ext_float_atomics in CBasicTestFetchMin/Max with atomic_half (#2357) Related to #2142, according to the work plan, extending CBasicTestFetchMin/CBasicTestFetchMax with support for atomic_half. --- test_conformance/c11_atomics/common.cpp | 18 ++- test_conformance/c11_atomics/common.h | 5 +- test_conformance/c11_atomics/host_atomics.h | 16 +- test_conformance/c11_atomics/test_atomics.cpp | 153 +++++++++++++++--- 4 files changed, 167 insertions(+), 25 deletions(-) diff --git a/test_conformance/c11_atomics/common.cpp b/test_conformance/c11_atomics/common.cpp index 414d877b..4838c347 100644 --- a/test_conformance/c11_atomics/common.cpp +++ b/test_conformance/c11_atomics/common.cpp @@ -194,14 +194,28 @@ template<> cl_int AtomicTypeExtendedInfo::MinValue() {return CL_INT_MIN; template<> cl_uint AtomicTypeExtendedInfo::MinValue() {return 0;} template<> cl_long AtomicTypeExtendedInfo::MinValue() {return CL_LONG_MIN;} template<> cl_ulong AtomicTypeExtendedInfo::MinValue() {return 0;} -template<> cl_float AtomicTypeExtendedInfo::MinValue() {return CL_FLT_MIN;} +template <> cl_half AtomicTypeExtendedInfo::MinValue() +{ + return cl_half_from_float(CL_HALF_MIN, gHalfRoundingMode); +} +template <> cl_float AtomicTypeExtendedInfo::MinValue() +{ + return CL_FLT_MIN; +} template<> cl_double AtomicTypeExtendedInfo::MinValue() {return CL_DBL_MIN;} template<> cl_int AtomicTypeExtendedInfo::MaxValue() {return CL_INT_MAX;} template<> cl_uint AtomicTypeExtendedInfo::MaxValue() {return CL_UINT_MAX;} template<> cl_long AtomicTypeExtendedInfo::MaxValue() {return CL_LONG_MAX;} template<> cl_ulong AtomicTypeExtendedInfo::MaxValue() {return CL_ULONG_MAX;} -template<> cl_float AtomicTypeExtendedInfo::MaxValue() {return CL_FLT_MAX;} +template <> cl_half AtomicTypeExtendedInfo::MaxValue() +{ + return cl_half_from_float(CL_HALF_MAX, gHalfRoundingMode); +} +template <> cl_float AtomicTypeExtendedInfo::MaxValue() +{ + return CL_FLT_MAX; +} template<> cl_double AtomicTypeExtendedInfo::MaxValue() {return CL_DBL_MAX;} cl_int getSupportedMemoryOrdersAndScopes( diff --git a/test_conformance/c11_atomics/common.h b/test_conformance/c11_atomics/common.h index fe2bd37d..aee5173d 100644 --- a/test_conformance/c11_atomics/common.h +++ b/test_conformance/c11_atomics/common.h @@ -894,15 +894,16 @@ CBasicTest::ProgramHeader(cl_uint maxNumDestItems) header += std::string("__global volatile ") + aTypeName + " destMemory[" + ss.str() + "] = {\n"; ss.str(""); + if (CBasicTest::DataType()._type == TYPE_ATOMIC_FLOAT) ss << std::setprecision(10) << _startValue; else if (CBasicTest::DataType()._type == TYPE_ATOMIC_HALF) - ss << static_cast( - cl_half_to_float(static_cast(_startValue))); + ss << cl_half_to_float(static_cast(_startValue)); else ss << _startValue; + for (cl_uint i = 0; i < maxNumDestItems; i++) { if (aTypeName == "atomic_flag") diff --git a/test_conformance/c11_atomics/host_atomics.h b/test_conformance/c11_atomics/host_atomics.h index aabbfdde..4471897b 100644 --- a/test_conformance/c11_atomics/host_atomics.h +++ b/test_conformance/c11_atomics/host_atomics.h @@ -176,7 +176,20 @@ bool host_atomic_compare_exchange(volatile AtomicType *a, CorrespondingType *exp TExplicitMemoryOrderType order_failure) { CorrespondingType tmp; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + static std::mutex mtx; + std::lock_guard lock(mtx); + tmp = *reinterpret_cast(a); + + if (cl_half_to_float(tmp) == cl_half_to_float(*expected)) + { + *reinterpret_cast(a) = desired; + return true; + } + *expected = tmp; + } + else if constexpr (std::is_same_v) { static std::mutex mtx; std::lock_guard lock(mtx); @@ -191,7 +204,6 @@ bool host_atomic_compare_exchange(volatile AtomicType *a, CorrespondingType *exp else { #if defined(_MSC_VER) || (defined(__INTEL_COMPILER) && defined(WIN32)) - tmp = InterlockedCompareExchange(a, desired, *expected); #elif defined(__GNUC__) tmp = __sync_val_compare_and_swap(a, *expected, desired); diff --git a/test_conformance/c11_atomics/test_atomics.cpp b/test_conformance/c11_atomics/test_atomics.cpp index 40cc8d27..d73bb6b8 100644 --- a/test_conformance/c11_atomics/test_atomics.cpp +++ b/test_conformance/c11_atomics/test_atomics.cpp @@ -2687,7 +2687,10 @@ public: min_range(-999.0), max_range(999.0) { StartValue(DataType().MaxValue()); - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { CBasicTestMemOrderScope::OldValueCheck(false); @@ -2697,7 +2700,10 @@ public: { std::string memoryOrderScope = MemoryOrderScopeStr(); std::string postfix(memoryOrderScope.empty() ? "" : "_explicit"); - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { return " atomic_fetch_min" + postfix + "(&destMemory[0], oldValues[tid] " + memoryOrderScope + ");\n" @@ -2716,7 +2722,10 @@ public: volatile HostAtomicType *destMemory, HostDataType *oldValues) override { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { host_atomic_fetch_min(&destMemory[0], oldValues[tid], MemoryOrder()); @@ -2732,7 +2741,16 @@ public: bool GenerateRefs(cl_uint threadCount, HostDataType *startRefValues, MTdata d) override { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + for (cl_uint i = 0; i < threadCount; i++) + { + startRefValues[i] = cl_half_from_float( + get_random_float(min_range, max_range, d), + gHalfRoundingMode); + } + } + else if constexpr (std::is_same_v) { for (cl_uint i = 0; i < threadCount; i++) { @@ -2759,7 +2777,19 @@ public: cl_uint whichDestValue) override { expected = StartValue(); - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (whichDestValue == 0) + { + for (cl_uint i = 0; i < threadCount; i++) + { + if (cl_half_to_float(startRefValues[i]) + < cl_half_to_float(expected)) + expected = startRefValues[i]; + } + } + } + else if constexpr (std::is_same_v) { if (whichDestValue == 0) for (cl_uint i = 0; i < threadCount; i++) @@ -2779,7 +2809,9 @@ public: const std::vector &testValues, cl_uint whichDestValue) override { - if (std::is_same::value) + if (std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same::value) { if (whichDestValue == 0) return CBasicTestMemOrderScope:: @@ -2794,7 +2826,9 @@ public: bool VerifyRefs(bool &correct, cl_uint threadCount, HostDataType *refValues, HostAtomicType *finalValues) override { - if (std::is_same::value) + if (std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same::value) { correct = true; for (cl_uint i = 1; i < threadCount; i++) @@ -2817,7 +2851,19 @@ public: int ExecuteSingleTest(cl_device_id deviceID, cl_context context, cl_command_queue queue) override { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (LocalMemory() + && (gHalfAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_MIN_MAX_EXT) + == 0) + return 0; // skip test - not applicable + + if (!LocalMemory() + && (gHalfAtomicCaps & CL_DEVICE_GLOBAL_FP_ATOMIC_MIN_MAX_EXT) + == 0) + return 0; + } + else if constexpr (std::is_same_v) { if (LocalMemory() && (gFloatAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_MIN_MAX_EXT) @@ -2835,7 +2881,10 @@ public: } cl_uint NumResults(cl_uint threadCount, cl_device_id deviceID) override { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { return threadCount; } @@ -2870,6 +2919,11 @@ static int test_atomic_fetch_min_generic(cl_device_id deviceID, if (gFloatAtomicsSupported) { + CBasicTestFetchMin test_half( + TYPE_ATOMIC_HALF, useSVM); + EXECUTE_TEST(error, + test_half.Execute(deviceID, context, queue, num_elements)); + CBasicTestFetchMin test_float( TYPE_ATOMIC_FLOAT, useSVM); EXECUTE_TEST( @@ -2953,18 +3007,31 @@ public: useSVM), min_range(-999.0), max_range(999.0) { - StartValue(DataType().MinValue()); - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { CBasicTestMemOrderScope::OldValueCheck(false); + if constexpr (std::is_same_v) + StartValue(cl_half_from_float(-CL_HALF_MAX, gHalfRoundingMode)); + else + StartValue(-DataType().MaxValue()); + } + else + { + StartValue(DataType().MinValue()); } } std::string ProgramCore() override { std::string memoryOrderScope = MemoryOrderScopeStr(); std::string postfix(memoryOrderScope.empty() ? "" : "_explicit"); - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { return " atomic_fetch_max" + postfix + "(&destMemory[0], oldValues[tid] " + memoryOrderScope + ");\n" @@ -2983,7 +3050,10 @@ public: volatile HostAtomicType *destMemory, HostDataType *oldValues) override { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { host_atomic_fetch_max(&destMemory[0], oldValues[tid], MemoryOrder()); @@ -2999,7 +3069,16 @@ public: bool GenerateRefs(cl_uint threadCount, HostDataType *startRefValues, MTdata d) override { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + for (cl_uint i = 0; i < threadCount; i++) + { + startRefValues[i] = cl_half_from_float( + get_random_float(min_range, max_range, d), + gHalfRoundingMode); + } + } + else if constexpr (std::is_same_v) { for (cl_uint i = 0; i < threadCount; i++) { @@ -3026,7 +3105,19 @@ public: cl_uint whichDestValue) override { expected = StartValue(); - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (whichDestValue == 0) + { + for (cl_uint i = 0; i < threadCount; i++) + { + if (cl_half_to_float(startRefValues[i]) + > cl_half_to_float(expected)) + expected = startRefValues[i]; + } + } + } + else if constexpr (std::is_same_v) { if (whichDestValue == 0) for (cl_uint i = 0; i < threadCount; i++) @@ -3046,7 +3137,9 @@ public: const std::vector &testValues, cl_uint whichDestValue) override { - if (std::is_same::value) + if (std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same::value) { if (whichDestValue == 0) return CBasicTestMemOrderScope:: @@ -3061,7 +3154,9 @@ public: bool VerifyRefs(bool &correct, cl_uint threadCount, HostDataType *refValues, HostAtomicType *finalValues) override { - if (std::is_same::value) + if (std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same::value) { correct = true; for (cl_uint i = 1; i < threadCount; i++) @@ -3084,7 +3179,19 @@ public: int ExecuteSingleTest(cl_device_id deviceID, cl_context context, cl_command_queue queue) override { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (LocalMemory() + && (gHalfAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_MIN_MAX_EXT) + == 0) + return 0; // skip test - not applicable + + if (!LocalMemory() + && (gHalfAtomicCaps & CL_DEVICE_GLOBAL_FP_ATOMIC_MIN_MAX_EXT) + == 0) + return 0; + } + else if constexpr (std::is_same_v) { if (LocalMemory() && (gFloatAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_MIN_MAX_EXT) @@ -3102,7 +3209,10 @@ public: } cl_uint NumResults(cl_uint threadCount, cl_device_id deviceID) override { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { return threadCount; } @@ -3137,6 +3247,11 @@ static int test_atomic_fetch_max_generic(cl_device_id deviceID, if (gFloatAtomicsSupported) { + CBasicTestFetchMax test_half( + TYPE_ATOMIC_HALF, useSVM); + EXECUTE_TEST(error, + test_half.Execute(deviceID, context, queue, num_elements)); + CBasicTestFetchMax test_float( TYPE_ATOMIC_FLOAT, useSVM); EXECUTE_TEST( From a1d8c3e41930a2fc81fa62fce14d954e6b424ac9 Mon Sep 17 00:00:00 2001 From: Ahmed <36049290+AhmedAmraniAkdi@users.noreply.github.com> Date: Tue, 21 Oct 2025 17:59:13 +0100 Subject: [PATCH 18/33] Remove spir half vloada_half tests as this builtin does not exist in OpenCL (#2552) The test was using vloada_half which does not exist for scalars. Also removed the files test.vloada_half_*.* from half.zip. For test.vloada_half3_global, changed the OpenCL kernel to use vload_half instead of vloada_half. Build failures will return a proper failure now, before, the test was passing in this case. More Info: https://github.com/KhronosGroup/OpenCL-Docs/issues/648 --- test_conformance/spir/half.zip | Bin 1270377 -> 1257839 bytes test_conformance/spir/main.cpp | 1286 +++++++++++----------- test_conformance/spir/run_build_test.cpp | 279 ++--- 3 files changed, 803 insertions(+), 762 deletions(-) mode change 100644 => 100755 test_conformance/spir/half.zip diff --git a/test_conformance/spir/half.zip b/test_conformance/spir/half.zip old mode 100644 new mode 100755 index 0b1deca1f18179bfd38378a156b5ba5bce5add0c..2f3f9bbd3a264e10f1291f247e800d605e7461d7 GIT binary patch delta 60336 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 delta 71298 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 0 && c < 256) ss.put(c); + if (c > 0 && c < 256) ss.put(c); return c; } @@ -77,12 +76,11 @@ private: } }; -class custom_cerr : public std::streambuf -{ +class custom_cerr : public std::streambuf { private: std::stringstream ss; - std::streamsize xsputn (const char* s, std::streamsize n) + std::streamsize xsputn(const char *s, std::streamsize n) { ss.write(s, n); return n; @@ -90,7 +88,7 @@ private: int overflow(int c) { - if(c > 0 && c < 256) ss.put(c); + if (c > 0 && c < 256) ss.put(c); return c; } @@ -102,29 +100,25 @@ private: } }; -class override_buff -{ - std::ostream* stream; - std::streambuf* buff; +class override_buff { + std::ostream *stream; + std::streambuf *buff; public: - override_buff(std::ostream& s, std::streambuf& b) + override_buff(std::ostream &s, std::streambuf &b) { stream = &s; buff = stream->rdbuf(); stream->rdbuf(&b); } - ~override_buff() - { - stream->rdbuf(buff); - } + ~override_buff() { stream->rdbuf(buff); } }; -typedef bool (*testfn)(cl_device_id device, cl_uint size_t_width, const char *folder); +typedef bool (*testfn)(cl_device_id device, cl_uint size_t_width, + const char *folder); -template -void dealloc(T *p) +template void dealloc(T *p) { if (p) delete p; } @@ -132,15 +126,15 @@ void dealloc(T *p) static void get_spir_version(cl_device_id device, std::vector &versions) { - char version[64] = {0}; + char version[64] = { 0 }; cl_int err; size_t size = 0; if ((err = clGetDeviceInfo(device, CL_DEVICE_SPIR_VERSIONS, sizeof(version), (void *)version, &size))) { - log_error( "Error: failed to obtain SPIR version at %s:%d (err = %d)\n", - __FILE__, __LINE__, err ); + log_error("Error: failed to obtain SPIR version at %s:%d (err = %d)\n", + __FILE__, __LINE__, err); return; } @@ -160,126 +154,133 @@ static void get_spir_version(cl_device_id device, } } -struct CounterEventHandler: EventHandler{ - unsigned int& Counter; +struct CounterEventHandler : EventHandler +{ + unsigned int &Counter; unsigned int TN; std::string LastTest; - //N - counter of successful tests. - //T - total number of tests in the suite - CounterEventHandler(unsigned int& N, unsigned int T): Counter(N), TN(T){} + // N - counter of successful tests. + // T - total number of tests in the suite + CounterEventHandler(unsigned int &N, unsigned int T): Counter(N), TN(T) {} - void operator ()(const std::string& testName, const std::string& kernelName) { - if (testName != LastTest){ + void operator()(const std::string &testName, const std::string &kernelName) + { + if (testName != LastTest) + { ++Counter; LastTest = testName; } } }; -class AccumulatorEventHandler: public EventHandler{ - std::list& m_list; - const std::string m_name; -public: - AccumulatorEventHandler(std::list& L, const std::string N): - m_list(L), m_name(N){} +class AccumulatorEventHandler : public EventHandler { + std::list &m_list; + const std::string m_name; - void operator ()(const std::string& T, const std::string& K){ - std::cerr << "\nTest " << T << "\t Kernel " << K << " failed" << std::endl; - m_list.push_back(m_name + "\t" + T + "\t" + K); - } +public: + AccumulatorEventHandler(std::list &L, const std::string N) + : m_list(L), m_name(N) + {} + + void operator()(const std::string &T, const std::string &K) + { + std::cerr << "\nTest " << T << "\t Kernel " << K << " failed" + << std::endl; + m_list.push_back(m_name + "\t" + T + "\t" + K); + } }; -static void printError(const std::string& S){ - std::cerr << S << std::endl; -} +static void printError(const std::string &S) { std::cerr << S << std::endl; } // Extracts suite with the given name, and saves it to disk. static void extract_suite(const char *suiteName) { - mz_zip_archive zip_archive; + mz_zip_archive zip_archive; - // Composing the name of the archive. - char* dir = get_exe_dir(); - std::string archiveName(dir); - archiveName.append(dir_sep()); - archiveName.append(suiteName); - archiveName.append(".zip"); - free(dir); + // Composing the name of the archive. + char *dir = get_exe_dir(); + std::string archiveName(dir); + archiveName.append(dir_sep()); + archiveName.append(suiteName); + archiveName.append(".zip"); + free(dir); #if defined(_WIN32) - _mkdir(suiteName); + _mkdir(suiteName); #else - mkdir(suiteName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + mkdir(suiteName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); #endif - memset(&zip_archive, 0, sizeof(zip_archive)); - if (!mz_zip_reader_init_file(&zip_archive, archiveName.c_str(), 0)) - throw Exceptions::ArchiveError(MZ_DATA_ERROR); + memset(&zip_archive, 0, sizeof(zip_archive)); + if (!mz_zip_reader_init_file(&zip_archive, archiveName.c_str(), 0)) + throw Exceptions::ArchiveError(MZ_DATA_ERROR); - // Get and print information about each file in the archive. - for (size_t i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) - { - mz_zip_archive_file_stat fileStat; - size_t fileSize = 0; + // Get and print information about each file in the archive. + for (size_t i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) + { + mz_zip_archive_file_stat fileStat; + size_t fileSize = 0; - if (!mz_zip_reader_file_stat(&zip_archive, i, &fileStat)) - { - mz_zip_reader_end(&zip_archive); - throw Exceptions::ArchiveError(MZ_DATA_ERROR); - } - const std::string fileName = fileStat.m_filename; + if (!mz_zip_reader_file_stat(&zip_archive, i, &fileStat)) + { + mz_zip_reader_end(&zip_archive); + throw Exceptions::ArchiveError(MZ_DATA_ERROR); + } + const std::string fileName = fileStat.m_filename; - // If the file is a directory, skip it. We create suite folder at the beggining. - if (mz_zip_reader_is_file_a_directory(&zip_archive, fileStat.m_file_index)) - { - continue; - } + // If the file is a directory, skip it. We create suite folder at the + // beggining. + if (mz_zip_reader_is_file_a_directory(&zip_archive, + fileStat.m_file_index)) + { + continue; + } - // Extracting the file. - void *p = mz_zip_reader_extract_file_to_heap(&zip_archive, - fileName.c_str(), - &fileSize, 0); - if (!p) - { - mz_zip_reader_end(&zip_archive); - throw std::runtime_error("mz_zip_reader_extract_file_to_heap() failed!\n"); - } + // Extracting the file. + void *p = mz_zip_reader_extract_file_to_heap( + &zip_archive, fileName.c_str(), &fileSize, 0); + if (!p) + { + mz_zip_reader_end(&zip_archive); + throw std::runtime_error( + "mz_zip_reader_extract_file_to_heap() failed!\n"); + } - // Writing the file back to the disk - std::fstream file(fileName.c_str(), - std::ios_base::trunc | std::ios_base::in | - std::ios_base::out | std::ios_base::binary); - if (!file.is_open()) - { - std::string msg = "Failed to open "; - msg.append(fileName); - throw Exceptions::TestError(msg); - } + // Writing the file back to the disk + std::fstream file(fileName.c_str(), + std::ios_base::trunc | std::ios_base::in + | std::ios_base::out | std::ios_base::binary); + if (!file.is_open()) + { + std::string msg = "Failed to open "; + msg.append(fileName); + throw Exceptions::TestError(msg); + } - file.write((const char*)p, fileSize); - if (file.bad()) - { - std::string msg("Failed to write into "); - msg.append(fileName); - throw Exceptions::TestError(msg); - } + file.write((const char *)p, fileSize); + if (file.bad()) + { + std::string msg("Failed to write into "); + msg.append(fileName); + throw Exceptions::TestError(msg); + } - // Cleanup. - file.flush(); - file.close(); - free(p); - } - mz_zip_reader_end(&zip_archive); + // Cleanup. + file.flush(); + file.close(); + free(p); + } + mz_zip_reader_end(&zip_archive); } // // Extracts the given suite package if needed. // return true if the suite was extracted, false otherwise. // -static bool try_extract(const char* suite) +static bool try_extract(const char *suite) { - if(no_unzip == 0) + if (no_unzip == 0) { std::cout << "Extracting test suite " << suite << std::endl; extract_suite(suite); @@ -297,17 +298,22 @@ bool test_suite(cl_device_id device, cl_uint size_t_width, const char *folder, std::cout << "Running tests:" << std::endl; - OclExtensions deviceCapabilities = OclExtensions::getDeviceCapabilities(device); + OclExtensions deviceCapabilities = + OclExtensions::getDeviceCapabilities(device); unsigned int tests_passed = 0; CounterEventHandler SuccE(tests_passed, number_of_tests); std::list ErrList; for (unsigned int i = 0; i < number_of_tests; ++i) { AccumulatorEventHandler FailE(ErrList, test_name[i]); - if((strlen(extension) != 0) && (!is_extension_available(device, extension))) + if ((strlen(extension) != 0) + && (!is_extension_available(device, extension))) { (SuccE)(test_name[i], ""); - std::cout << test_name[i] << "... Skipped. (Cannot run on device due to missing extension: " << extension << " )." << std::endl; + std::cout << test_name[i] + << "... Skipped. (Cannot run on device due to missing " + "extension: " + << extension << " )." << std::endl; continue; } TestRunner testRunner(&SuccE, &FailE, deviceCapabilities); @@ -315,7 +321,9 @@ bool test_suite(cl_device_id device, cl_uint size_t_width, const char *folder, } std::cout << std::endl; - std::cout << "PASSED " << tests_passed << " of " << number_of_tests << " tests.\n" << std::endl; + std::cout << "PASSED " << tests_passed << " of " << number_of_tests + << " tests.\n" + << std::endl; if (!ErrList.empty()) { @@ -328,23 +336,21 @@ bool test_suite(cl_device_id device, cl_uint size_t_width, const char *folder, return true; } -static std::string getTestFolder(const std::string& TS) +static std::string getTestFolder(const std::string &TS) { - const std::string DOUBLE("_double"); - if (TS.size() < DOUBLE.size()) + const std::string DOUBLE("_double"); + if (TS.size() < DOUBLE.size()) return TS; + + const size_t prefixLen = TS.size() - DOUBLE.size(); + const std::string postfix = TS.substr(prefixLen, DOUBLE.size()); + if (DOUBLE == postfix) return TS.substr(0, prefixLen); + return TS; - - const size_t prefixLen = TS.size() - DOUBLE.size(); - const std::string postfix = TS.substr(prefixLen, DOUBLE.size()); - if (DOUBLE == postfix) - return TS.substr(0, prefixLen); - - return TS; } -bool test_api (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_api(cl_device_id device, cl_uint size_t_width, const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "const_derived_d", "const_scalar_d", "const_vector16_d", @@ -668,41 +674,32 @@ bool test_api (cl_device_id device, cl_uint size_t_width, const char *folder) }; log_info("test_api\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_api_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_api_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { - "double_scalar_p", - "double_scalar_p2", - "double_scalar_d", - "double_vector2_p", - "double_vector2_p2", - "double_vector2_d", - "double_vector3_p", - "double_vector3_p2", - "double_vector3_d", - "double_vector4_p", - "double_vector4_p2", - "double_vector4_d", - "double_vector8_p", - "double_vector8_p2", - "double_vector8_d", - "double_vector16_p", - "double_vector16_p2", - "double_vector16_d", + static const char *test_name[] = { + "double_scalar_p", "double_scalar_p2", "double_scalar_d", + "double_vector2_p", "double_vector2_p2", "double_vector2_d", + "double_vector3_p", "double_vector3_p2", "double_vector3_d", + "double_vector4_p", "double_vector4_p2", "double_vector4_d", + "double_vector8_p", "double_vector8_p2", "double_vector8_d", + "double_vector16_p", "double_vector16_p2", "double_vector16_d", }; log_info("test_api_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -bool test_atomics (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_atomics(cl_device_id device, cl_uint size_t_width, const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "test_atomic_fn.atomic_add_global_int", "test_atomic_fn.atomic_add_global_uint", "test_atomic_fn.atomic_sub_global_int", @@ -752,13 +749,14 @@ bool test_atomics (cl_device_id device, cl_uint size_t_width, const char *folder }; log_info("test_atomics\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_basic (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_basic(cl_device_id device, cl_uint size_t_width, const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "sample_kernel.work_item_functions", "test_sizeof.sizeof_char", "test_sizeof.sizeof_uchar", @@ -1424,12 +1422,14 @@ bool test_basic (cl_device_id device, cl_uint size_t_width, const char *folder) }; log_info("test_basic\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_basic_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_basic_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "sample_test.vec_type_hint_double", "sample_test.vec_type_hint_double2", "sample_test.vec_type_hint_double4", @@ -1494,13 +1494,15 @@ bool test_basic_double (cl_device_id device, cl_uint size_t_width, const char *f }; log_info("test_basic_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -bool test_commonfns (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_commonfns(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "test_clamp.test_clamp_float", "test_clamp.test_clamp_float2", "test_clamp.test_clamp_float4", @@ -1565,13 +1567,15 @@ bool test_commonfns (cl_device_id device, cl_uint size_t_width, const char *fold }; log_info("test_commonfns\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_commonfns_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_commonfns_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "test_clamp.test_clamp_double", "test_clamp.test_clamp_double2", "test_clamp.test_clamp_double4", @@ -1617,12 +1621,14 @@ bool test_commonfns_double (cl_device_id device, cl_uint size_t_width, const cha }; log_info("test_commonfns_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -bool test_conversions (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_conversions(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "convert2_type_roundingmode_type_f", "convert3_type_roundingmode_type_f", "convert4_type_roundingmode_type_f", @@ -2482,13 +2488,15 @@ bool test_conversions (cl_device_id device, cl_uint size_t_width, const char *fo }; log_info("test_conversions\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_conversions_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_conversions_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "convert2_type_roundingmode_type_d", "convert3_type_roundingmode_type_d", "convert4_type_roundingmode_type_d", @@ -2651,13 +2659,15 @@ bool test_conversions_double (cl_device_id device, cl_uint size_t_width, const c }; log_info("test_conversions_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -bool test_geometrics (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_geometrics(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "sample_test.geom_cross_float3", "sample_test.geom_cross_float4", "sample_test.geom_dot_float", @@ -2691,13 +2701,15 @@ bool test_geometrics (cl_device_id device, cl_uint size_t_width, const char *fol }; log_info("test_geometrics\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_geometrics_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_geometrics_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "sample_test.geom_cross_double3", "sample_test.geom_cross_double4", "sample_test.geom_dot_double", @@ -2719,13 +2731,14 @@ bool test_geometrics_double (cl_device_id device, cl_uint size_t_width, const ch }; log_info("test_geometrics_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -bool test_half (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_half(cl_device_id device, cl_uint size_t_width, const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "test.vload_half_global", "test.vload_half_private", "test.vload_half_local", @@ -2750,10 +2763,6 @@ bool test_half (cl_device_id device, cl_uint size_t_width, const char *folder) "test.vload_half3_private", "test.vload_half3_local", "test.vload_half3_constant", - "test.vloada_half_global", - "test.vloada_half_private", - "test.vloada_half_local", - "test.vloada_half_constant", "test.vloada_half2_global", "test.vloada_half2_private", "test.vloada_half2_local", @@ -2942,13 +2951,15 @@ bool test_half (cl_device_id device, cl_uint size_t_width, const char *folder) }; log_info("test_half\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_half_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_half_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "test.vstore_half_global_double", "test.vstore_half_private_double", "test.vstore_half_local_double", @@ -3117,13 +3128,15 @@ bool test_half_double (cl_device_id device, cl_uint size_t_width, const char *fo }; log_info("test_half_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -bool test_kernel_image_methods (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_kernel_image_methods(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "sample_kernel.get_image_info_1D", "sample_kernel.get_image_info_2D", "sample_kernel.get_image_info_3D", @@ -3132,13 +3145,15 @@ bool test_kernel_image_methods (cl_device_id device, cl_uint size_t_width, const }; log_info("test_kernel_image_methods\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_images_kernel_read_write (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_images_kernel_read_write(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "sample_kernel.read_image_set_1D_fint", "sample_kernel.read_image_set_1D_ffloat", "sample_kernel.read_image_set_1D_iint", @@ -3184,13 +3199,15 @@ bool test_images_kernel_read_write (cl_device_id device, cl_uint size_t_width, c }; log_info("test_images_kernel_read_write\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_images_samplerless_read (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_images_samplerless_read(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "sample_kernel.read_image_set_1D_float", "sample_kernel.read_image_set_1D_int", "sample_kernel.read_image_set_1D_uint", @@ -3212,13 +3229,15 @@ bool test_images_samplerless_read (cl_device_id device, cl_uint size_t_width, co }; log_info("test_images_samplerless_read\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_integer_ops (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_integer_ops(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "sample_test.integer_clz_char", "sample_test.integer_clz_char2", "sample_test.integer_clz_char3", @@ -4002,13 +4021,15 @@ bool test_integer_ops (cl_device_id device, cl_uint size_t_width, const char *fo }; log_info("test_integer_ops\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_math_brute_force (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_math_brute_force(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "math_kernel.acos_float", "math_kernel3.acos_float3", "math_kernel16.acos_float16", @@ -4583,13 +4604,15 @@ bool test_math_brute_force (cl_device_id device, cl_uint size_t_width, const cha }; log_info("test_math_brute_force\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_math_brute_force_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_math_brute_force_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "math_kernel8.acos_double8", "math_kernel4.acos_double4", "math_kernel16.acos_double16", @@ -4638,7 +4661,7 @@ bool test_math_brute_force_double (cl_device_id device, cl_uint size_t_width, co "math_kernel3.atanh_double3", "math_kernel2.atanh_double2", "math_kernel.atanh_double", - "math_kernel8.atanpi_double8", + "math_kernel8.atanpi_double8", "math_kernel16.atanpi_double16", "math_kernel3.atanpi_double3", "math_kernel4.atanpi_double4", @@ -5067,13 +5090,14 @@ bool test_math_brute_force_double (cl_device_id device, cl_uint size_t_width, co }; log_info("test_math_brute_force_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -bool test_printf (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_printf(cl_device_id device, cl_uint size_t_width, const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "test0.testCaseInt", "test1.testCaseFloat", "test5.testCaseChar", @@ -5089,25 +5113,29 @@ bool test_printf (cl_device_id device, cl_uint size_t_width, const char *folder) }; log_info("test_printf\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_profiling (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_profiling(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "testReadf", "image_filter", }; log_info("test_profiling\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_relationals (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_relationals(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "sample_test.relational_any_char", "sample_test.relational_any_char2", "sample_test.relational_any_char3", @@ -5583,13 +5611,15 @@ bool test_relationals (cl_device_id device, cl_uint size_t_width, const char *fo }; log_info("test_relationals\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_relationals_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_relationals_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "sample_test.relational_bitselect_double", "sample_test.relational_bitselect_double2", "sample_test.relational_bitselect_double3", @@ -5673,152 +5703,97 @@ bool test_relationals_double (cl_device_id device, cl_uint size_t_width, const c }; log_info("test_relationals_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -bool test_select (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_select(cl_device_id device, cl_uint size_t_width, const char *folder) { - static const char* test_name[] = { - "select_uchar_uchar", - "select_uchar2_uchar2", - "select_uchar3_uchar3", - "select_uchar4_uchar4", - "select_uchar8_uchar8", - "select_uchar16_uchar16", - "select_uchar_char", - "select_uchar2_char2", - "select_uchar3_char3", - "select_uchar4_char4", - "select_uchar8_char8", - "select_uchar16_char16", - "select_char_uchar", - "select_char2_uchar2", - "select_char3_uchar3", - "select_char4_uchar4", - "select_char8_uchar8", - "select_char16_uchar16", - "select_char_char", - "select_char2_char2", - "select_char3_char3", - "select_char4_char4", - "select_char8_char8", - "select_char16_char16", - "select_ushort_ushort", - "select_ushort2_ushort2", - "select_ushort3_ushort3", - "select_ushort4_ushort4", - "select_ushort8_ushort8", - "select_ushort16_ushort16", - "select_ushort_short", - "select_ushort2_short2", - "select_ushort3_short3", - "select_ushort4_short4", - "select_ushort8_short8", - "select_ushort16_short16", - "select_short_ushort", - "select_short2_ushort2", - "select_short3_ushort3", - "select_short4_ushort4", - "select_short8_ushort8", - "select_short16_ushort16", - "select_short_short", - "select_short2_short2", - "select_short3_short3", - "select_short4_short4", - "select_short8_short8", - "select_short16_short16", - "select_uint_uint", - "select_uint2_uint2", - "select_uint3_uint3", - "select_uint4_uint4", - "select_uint8_uint8", - "select_uint16_uint16", - "select_uint_int", - "select_uint2_int2", - "select_uint3_int3", - "select_uint4_int4", - "select_uint8_int8", - "select_uint16_int16", - "select_int_uint", - "select_int2_uint2", - "select_int3_uint3", - "select_int4_uint4", - "select_int8_uint8", - "select_int16_uint16", - "select_int_int", - "select_int2_int2", - "select_int3_int3", - "select_int4_int4", - "select_int8_int8", - "select_int16_int16", - "select_float_uint", - "select_float2_uint2", - "select_float3_uint3", - "select_float4_uint4", - "select_float8_uint8", - "select_float16_uint16", - "select_float_int", - "select_float2_int2", - "select_float3_int3", - "select_float4_int4", - "select_float8_int8", - "select_float16_int16", - "select_ulong_ulong", - "select_ulong2_ulong2", - "select_ulong3_ulong3", - "select_ulong4_ulong4", - "select_ulong8_ulong8", - "select_ulong16_ulong16", - "select_ulong_long", - "select_ulong2_long2", - "select_ulong3_long3", - "select_ulong4_long4", - "select_ulong8_long8", - "select_ulong16_long16", - "select_long_ulong", - "select_long2_ulong2", - "select_long3_ulong3", - "select_long4_ulong4", - "select_long8_ulong8", - "select_long16_ulong16", - "select_long_long", - "select_long2_long2", - "select_long3_long3", - "select_long4_long4", - "select_long8_long8", - "select_long16_long16", + static const char *test_name[] = { + "select_uchar_uchar", "select_uchar2_uchar2", + "select_uchar3_uchar3", "select_uchar4_uchar4", + "select_uchar8_uchar8", "select_uchar16_uchar16", + "select_uchar_char", "select_uchar2_char2", + "select_uchar3_char3", "select_uchar4_char4", + "select_uchar8_char8", "select_uchar16_char16", + "select_char_uchar", "select_char2_uchar2", + "select_char3_uchar3", "select_char4_uchar4", + "select_char8_uchar8", "select_char16_uchar16", + "select_char_char", "select_char2_char2", + "select_char3_char3", "select_char4_char4", + "select_char8_char8", "select_char16_char16", + "select_ushort_ushort", "select_ushort2_ushort2", + "select_ushort3_ushort3", "select_ushort4_ushort4", + "select_ushort8_ushort8", "select_ushort16_ushort16", + "select_ushort_short", "select_ushort2_short2", + "select_ushort3_short3", "select_ushort4_short4", + "select_ushort8_short8", "select_ushort16_short16", + "select_short_ushort", "select_short2_ushort2", + "select_short3_ushort3", "select_short4_ushort4", + "select_short8_ushort8", "select_short16_ushort16", + "select_short_short", "select_short2_short2", + "select_short3_short3", "select_short4_short4", + "select_short8_short8", "select_short16_short16", + "select_uint_uint", "select_uint2_uint2", + "select_uint3_uint3", "select_uint4_uint4", + "select_uint8_uint8", "select_uint16_uint16", + "select_uint_int", "select_uint2_int2", + "select_uint3_int3", "select_uint4_int4", + "select_uint8_int8", "select_uint16_int16", + "select_int_uint", "select_int2_uint2", + "select_int3_uint3", "select_int4_uint4", + "select_int8_uint8", "select_int16_uint16", + "select_int_int", "select_int2_int2", + "select_int3_int3", "select_int4_int4", + "select_int8_int8", "select_int16_int16", + "select_float_uint", "select_float2_uint2", + "select_float3_uint3", "select_float4_uint4", + "select_float8_uint8", "select_float16_uint16", + "select_float_int", "select_float2_int2", + "select_float3_int3", "select_float4_int4", + "select_float8_int8", "select_float16_int16", + "select_ulong_ulong", "select_ulong2_ulong2", + "select_ulong3_ulong3", "select_ulong4_ulong4", + "select_ulong8_ulong8", "select_ulong16_ulong16", + "select_ulong_long", "select_ulong2_long2", + "select_ulong3_long3", "select_ulong4_long4", + "select_ulong8_long8", "select_ulong16_long16", + "select_long_ulong", "select_long2_ulong2", + "select_long3_ulong3", "select_long4_ulong4", + "select_long8_ulong8", "select_long16_ulong16", + "select_long_long", "select_long2_long2", + "select_long3_long3", "select_long4_long4", + "select_long8_long8", "select_long16_long16", }; log_info("test_select\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_select_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_select_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { - "select_double_ulong", - "select_double2_ulong2", - "select_double3_ulong3", - "select_double4_ulong4", - "select_double8_ulong8", - "select_double16_ulong16", - "select_double_long", - "select_double2_long2", - "select_double3_long3", - "select_double4_long4", - "select_double8_long8", - "select_double16_long16", + static const char *test_name[] = { + "select_double_ulong", "select_double2_ulong2", + "select_double3_ulong3", "select_double4_ulong4", + "select_double8_ulong8", "select_double16_ulong16", + "select_double_long", "select_double2_long2", + "select_double3_long3", "select_double4_long4", + "select_double8_long8", "select_double16_long16", }; log_info("test_select_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -bool test_vec_align (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_vec_align(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "test_vec_align_packed_struct_arr.vec_align_packed_struct_arr_char2", "test_vec_align_packed_struct_arr.vec_align_packed_struct_arr_char3", "test_vec_align_packed_struct_arr.vec_align_packed_struct_arr_char4", @@ -5877,13 +5852,15 @@ bool test_vec_align (cl_device_id device, cl_uint size_t_width, const char *fold }; log_info("vec_align\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_vec_align_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_vec_align_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { + static const char *test_name[] = { "test_vec_align_packed_struct_arr.vec_align_packed_struct_arr_double2", "test_vec_align_packed_struct_arr.vec_align_packed_struct_arr_double3", "test_vec_align_packed_struct_arr.vec_align_packed_struct_arr_double4", @@ -5893,130 +5870,104 @@ bool test_vec_align_double (cl_device_id device, cl_uint size_t_width, const cha }; log_info("vec_align_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -bool test_vec_step (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_vec_step(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { - "test_step_var.step_var_char", - "test_step_var.step_var_char2", - "test_step_var.step_var_char3", - "test_step_var.step_var_char4", - "test_step_var.step_var_char8", - "test_step_var.step_var_char16", - "test_step_var.step_var_uchar", - "test_step_var.step_var_uchar2", - "test_step_var.step_var_uchar3", - "test_step_var.step_var_uchar4", - "test_step_var.step_var_uchar8", - "test_step_var.step_var_uchar16", - "test_step_var.step_var_short", - "test_step_var.step_var_short2", - "test_step_var.step_var_short3", - "test_step_var.step_var_short4", - "test_step_var.step_var_short8", - "test_step_var.step_var_short16", - "test_step_var.step_var_ushort", - "test_step_var.step_var_ushort2", - "test_step_var.step_var_ushort3", - "test_step_var.step_var_ushort4", - "test_step_var.step_var_ushort8", - "test_step_var.step_var_ushort16", - "test_step_var.step_var_int", - "test_step_var.step_var_int2", - "test_step_var.step_var_int3", - "test_step_var.step_var_int4", - "test_step_var.step_var_int8", - "test_step_var.step_var_int16", - "test_step_var.step_var_uint", - "test_step_var.step_var_uint2", - "test_step_var.step_var_uint3", - "test_step_var.step_var_uint4", - "test_step_var.step_var_uint8", - "test_step_var.step_var_uint16", - "test_step_var.step_var_long", - "test_step_var.step_var_long2", - "test_step_var.step_var_long3", - "test_step_var.step_var_long4", - "test_step_var.step_var_long8", - "test_step_var.step_var_long16", - "test_step_var.step_var_ulong", - "test_step_var.step_var_ulong2", - "test_step_var.step_var_ulong3", - "test_step_var.step_var_ulong4", - "test_step_var.step_var_ulong8", - "test_step_var.step_var_ulong16", - "test_step_var.step_var_float", - "test_step_var.step_var_float2", - "test_step_var.step_var_float3", - "test_step_var.step_var_float4", - "test_step_var.step_var_float8", - "test_step_var.step_var_float16", + static const char *test_name[] = { + "test_step_var.step_var_char", "test_step_var.step_var_char2", + "test_step_var.step_var_char3", "test_step_var.step_var_char4", + "test_step_var.step_var_char8", "test_step_var.step_var_char16", + "test_step_var.step_var_uchar", "test_step_var.step_var_uchar2", + "test_step_var.step_var_uchar3", "test_step_var.step_var_uchar4", + "test_step_var.step_var_uchar8", "test_step_var.step_var_uchar16", + "test_step_var.step_var_short", "test_step_var.step_var_short2", + "test_step_var.step_var_short3", "test_step_var.step_var_short4", + "test_step_var.step_var_short8", "test_step_var.step_var_short16", + "test_step_var.step_var_ushort", "test_step_var.step_var_ushort2", + "test_step_var.step_var_ushort3", "test_step_var.step_var_ushort4", + "test_step_var.step_var_ushort8", "test_step_var.step_var_ushort16", + "test_step_var.step_var_int", "test_step_var.step_var_int2", + "test_step_var.step_var_int3", "test_step_var.step_var_int4", + "test_step_var.step_var_int8", "test_step_var.step_var_int16", + "test_step_var.step_var_uint", "test_step_var.step_var_uint2", + "test_step_var.step_var_uint3", "test_step_var.step_var_uint4", + "test_step_var.step_var_uint8", "test_step_var.step_var_uint16", + "test_step_var.step_var_long", "test_step_var.step_var_long2", + "test_step_var.step_var_long3", "test_step_var.step_var_long4", + "test_step_var.step_var_long8", "test_step_var.step_var_long16", + "test_step_var.step_var_ulong", "test_step_var.step_var_ulong2", + "test_step_var.step_var_ulong3", "test_step_var.step_var_ulong4", + "test_step_var.step_var_ulong8", "test_step_var.step_var_ulong16", + "test_step_var.step_var_float", "test_step_var.step_var_float2", + "test_step_var.step_var_float3", "test_step_var.step_var_float4", + "test_step_var.step_var_float8", "test_step_var.step_var_float16", }; log_info("vec_step\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -bool test_vec_step_double (cl_device_id device, cl_uint size_t_width, const char *folder) +bool test_vec_step_double(cl_device_id device, cl_uint size_t_width, + const char *folder) { - static const char* test_name[] = { - "test_step_var.step_var_double", - "test_step_var.step_var_double2", - "test_step_var.step_var_double3", - "test_step_var.step_var_double4", - "test_step_var.step_var_double8", - "test_step_var.step_var_double16", + static const char *test_name[] = { + "test_step_var.step_var_double", "test_step_var.step_var_double2", + "test_step_var.step_var_double3", "test_step_var.step_var_double4", + "test_step_var.step_var_double8", "test_step_var.step_var_double16", }; log_info("vec_step_double\n"); - return test_suite(device, size_t_width, folder, test_name, sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); + return test_suite(device, size_t_width, folder, test_name, + sizeof(test_name) / sizeof(const char *), "cl_khr_fp64"); } -template -void getT(const TestResult& res, unsigned arg, T& out) +template void getT(const TestResult &res, unsigned arg, T &out) { - out = *(T*)(res.kernelArgs().getArg(arg)->getBuffer()); + out = *(T *)(res.kernelArgs().getArg(arg)->getBuffer()); } class LinkageTestService { - std::vector m_moduleNames; - const char* m_kernelName; + std::vector m_moduleNames; + const char *m_kernelName; int m_expectedResult; const char *m_name; public: LinkageTestService(const char **moduleNames, int numModules, - const char *kernelName) : - m_moduleNames(numModules), - m_kernelName(kernelName), - m_expectedResult(-1), - m_name(NULL) { - std::copy(moduleNames, moduleNames+numModules, m_moduleNames.begin()); + const char *kernelName) + : m_moduleNames(numModules), m_kernelName(kernelName), + m_expectedResult(-1), m_name(NULL) + { + std::copy(moduleNames, moduleNames + numModules, m_moduleNames.begin()); } - void setExpectedResult(int expectedRes) { - m_expectedResult = expectedRes; - } + void setExpectedResult(int expectedRes) { m_expectedResult = expectedRes; } - bool compareResult(cl_device_id dev, cl_uint width) { + bool compareResult(cl_device_id dev, cl_uint width) + { clContextWrapper context; clCommandQueueWrapper queue; size_t num_modules = m_moduleNames.size(); std::vector programs(num_modules); create_context_and_queue(dev, &context, &queue); - for (size_t i=0; i linkageTests; + std::vector linkageTests; linkageTests.push_back(new LinkageTestService(private_files, 2, "k")); - linkageTests.push_back(new LinkageTestService(internal_files, 2, "internal_linkage")); - linkageTests.push_back(new LinkageTestService(external_files, 2, "external_linkage")); - linkageTests.push_back(new LinkageTestService(available_externally_files, 2, "k")); + linkageTests.push_back( + new LinkageTestService(internal_files, 2, "internal_linkage")); + linkageTests.push_back( + new LinkageTestService(external_files, 2, "external_linkage")); + linkageTests.push_back( + new LinkageTestService(available_externally_files, 2, "k")); // Set tests Names. linkageTests[0]->setName("private_linkage"); linkageTests[1]->setName("internal_linkage"); @@ -6081,11 +6033,11 @@ bool test_compile_and_link (cl_device_id device, cl_uint width, const char *fold CounterEventHandler SuccE(tests_passed, linkageTests.size()); std::list ErrList; - for (size_t i=0; igetName()); std::cout << linkageTests[i]->getName() << "..." << std::endl; - if(linkageTests[i]->compareResult(device, width)) + if (linkageTests[i]->compareResult(device, width)) { (SuccE)(linkageTests[i]->getName(), ""); std::cout << linkageTests[i]->getName() << " passed." << std::endl; @@ -6099,54 +6051,52 @@ bool test_compile_and_link (cl_device_id device, cl_uint width, const char *fold } std::cout << std::endl; - std::cout << "PASSED " << tests_passed << " of " << SuccE.TN << " tests.\n" << std::endl; + std::cout << "PASSED " << tests_passed << " of " << SuccE.TN << " tests.\n" + << std::endl; // Deallocating. - std::for_each(linkageTests.begin(), linkageTests.end(), dealloc); + std::for_each(linkageTests.begin(), linkageTests.end(), + dealloc); return tests_passed == SuccE.TN; } -static bool test_sampler_enumeration(cl_device_id device, cl_uint width, const char *folder) +static bool test_sampler_enumeration(cl_device_id device, cl_uint width, + const char *folder) { - static const char* test_name[] = { - "sampler_NormF_AddrC_FilterL", - "sampler_NormF_AddrC_FilterN", - "sampler_NormF_AddrE_FilterL", - "sampler_NormF_AddrE_FilterN", - // "sampler_NormF_AddrM_FilterL" - Invalid combination - // "sampler_NormF_AddrM_FilterN" - Invalid combination - "sampler_NormF_AddrN_FilterL", - "sampler_NormF_AddrN_FilterN", - // "sampler_NormF_AddrR_FilterL" - Invalid combination - // "sampler_NormF_AddrR_FilterN" - Invalid combination - "sampler_NormT_AddrC_FilterL", - "sampler_NormT_AddrC_FilterN", - "sampler_NormT_AddrE_FilterL", - "sampler_NormT_AddrE_FilterN", - "sampler_NormT_AddrM_FilterL", - "sampler_NormT_AddrM_FilterN", - "sampler_NormT_AddrN_FilterL", - "sampler_NormT_AddrN_FilterN", - "sampler_NormT_AddrR_FilterL", - "sampler_NormT_AddrR_FilterN" - }; + static const char *test_name[] = { + "sampler_NormF_AddrC_FilterL", "sampler_NormF_AddrC_FilterN", + "sampler_NormF_AddrE_FilterL", "sampler_NormF_AddrE_FilterN", + // "sampler_NormF_AddrM_FilterL" - Invalid combination + // "sampler_NormF_AddrM_FilterN" - Invalid combination + "sampler_NormF_AddrN_FilterL", "sampler_NormF_AddrN_FilterN", + // "sampler_NormF_AddrR_FilterL" - Invalid combination + // "sampler_NormF_AddrR_FilterN" - Invalid combination + "sampler_NormT_AddrC_FilterL", "sampler_NormT_AddrC_FilterN", + "sampler_NormT_AddrE_FilterL", "sampler_NormT_AddrE_FilterN", + "sampler_NormT_AddrM_FilterL", "sampler_NormT_AddrM_FilterN", + "sampler_NormT_AddrN_FilterL", "sampler_NormT_AddrN_FilterN", + "sampler_NormT_AddrR_FilterL", "sampler_NormT_AddrR_FilterN" + }; - log_info("test_sampler_enum_values\n"); - return test_suite(device, width, folder, test_name, sizeof(test_name) / sizeof(const char *), ""); + log_info("test_sampler_enum_values\n"); + return test_suite(device, width, folder, test_name, + sizeof(test_name) / sizeof(const char *), ""); } -const char* HOSTVAL_SAMPLER = "hostval_sampler"; -const char* HOSTVAL_IMAGE_DESC = "hostval_image_desc"; -const char* HOSTVAL_IMAGE_DESC_3D = "hostval_image_desc_3d"; +const char *HOSTVAL_SAMPLER = "hostval_sampler"; +const char *HOSTVAL_IMAGE_DESC = "hostval_image_desc"; +const char *HOSTVAL_IMAGE_DESC_3D = "hostval_image_desc_3d"; static bool test_image_enumeration(cl_context context, cl_command_queue queue, cl_program prog, cl_device_id device, - CounterEventHandler &SuccE, std::list &ErrList) + CounterEventHandler &SuccE, + std::list &ErrList) { // Creating image descriptor value generator. ImageValuesGenerator imgVals; bool success = true; - for(ImageValuesGenerator::iterator it = imgVals.begin(), e = imgVals.end(); it != e; ++it) + for (ImageValuesGenerator::iterator it = imgVals.begin(), e = imgVals.end(); + it != e; ++it) { bool currentSuccess = true; AccumulatorEventHandler FailE(ErrList, it.toString()); @@ -6166,7 +6116,7 @@ static bool test_image_enumeration(cl_context context, cl_command_queue queue, KernelArgInfo baseInfo; baseInfo.setTypeName(baseGenName.c_str()); DataGenerator *pDataGen = DataGenerator::getInstance(); - KernelArgGenerator* pOrig = pDataGen->getArgGenerator(baseInfo); + KernelArgGenerator *pOrig = pDataGen->getArgGenerator(baseInfo); try { @@ -6174,23 +6124,26 @@ static bool test_image_enumeration(cl_context context, cl_command_queue queue, WorkSizeInfo ws; clKernelWrapper kernel = create_kernel_helper(prog, kernelName); - // Acquiring a reference to the image generator we need for this image - // type. + // Acquiring a reference to the image generator we need for this + // image type. KernelArgInfo typedInfo; const std::string tyName = it.getImageGeneratorName(); typedInfo.setTypeName(tyName.c_str()); - KernelArgGeneratorImage* pImgGen = (KernelArgGeneratorImage*)pDataGen->getArgGenerator(typedInfo); + KernelArgGeneratorImage *pImgGen = + (KernelArgGeneratorImage *)pDataGen->getArgGenerator(typedInfo); // If the channel order is not valid for the current image type, we // continue to the next one. - if (!pImgGen->isValidChannelOrder(context, it.getOpenCLChannelOrder())) + if (!pImgGen->isValidChannelOrder(context, + it.getOpenCLChannelOrder())) continue; - // Due to unknown number of types at the beggining count them on the fly + // Due to unknown number of types at the beggining count them on the + // fly SuccE.TN++; - // Configuring the image generator so it will produce the correct image - // descriptor. + // Configuring the image generator so it will produce the correct + // image descriptor. pImgGen->setChannelOrder(it.getOpenCLChannelOrder()); pDataGen->setArgGenerator(baseInfo, pImgGen); @@ -6205,8 +6158,9 @@ static bool test_image_enumeration(cl_context context, cl_command_queue queue, getT(res, 1U, actualOrder), getT(res, 2U, actualTy); if (actualOrder != it.getSPIRChannelOrder()) { - std::cout << " expected channel order: " << it.getSPIRChannelOrder() - << " but received " << actualOrder << "." << std::endl; + std::cout << " expected channel order: " + << it.getSPIRChannelOrder() << " but received " + << actualOrder << "." << std::endl; success = currentSuccess = false; } @@ -6220,7 +6174,8 @@ static bool test_image_enumeration(cl_context context, cl_command_queue queue, if (currentSuccess) { (SuccE)(it.toString(), kernelName); - std::cout << "enum_" << it.toString() << " passed." << std::endl; + std::cout << "enum_" << it.toString() << " passed." + << std::endl; } else { @@ -6241,15 +6196,18 @@ static bool test_image_enumeration(cl_context context, cl_command_queue queue, return success; } -static bool test_image_enumeration_3d(cl_context context, cl_command_queue queue, - cl_program prog, cl_device_id device, - CounterEventHandler &SuccE, std::list &ErrList) +static bool test_image_enumeration_3d(cl_context context, + cl_command_queue queue, cl_program prog, + cl_device_id device, + CounterEventHandler &SuccE, + std::list &ErrList) { // Creating image descriptor value generator. ImageValuesGenerator imgVals; bool success = true; - for(ImageValuesGenerator::iterator it = imgVals.begin(), e = imgVals.end(); it != e; ++it) + for (ImageValuesGenerator::iterator it = imgVals.begin(), e = imgVals.end(); + it != e; ++it) { bool currentSuccess = true; AccumulatorEventHandler FailE(ErrList, it.toString()); @@ -6269,7 +6227,7 @@ static bool test_image_enumeration_3d(cl_context context, cl_command_queue queue KernelArgInfo baseInfo; baseInfo.setTypeName(baseGenName.c_str()); DataGenerator *pDataGen = DataGenerator::getInstance(); - KernelArgGenerator* pOrig = pDataGen->getArgGenerator(baseInfo); + KernelArgGenerator *pOrig = pDataGen->getArgGenerator(baseInfo); try { @@ -6277,23 +6235,26 @@ static bool test_image_enumeration_3d(cl_context context, cl_command_queue queue WorkSizeInfo ws; clKernelWrapper kernel = create_kernel_helper(prog, kernelName); - // Acquiring a reference to the image generator we need for this image - // type. + // Acquiring a reference to the image generator we need for this + // image type. KernelArgInfo typedInfo; const std::string tyName = it.getImageGeneratorName(); typedInfo.setTypeName(tyName.c_str()); - KernelArgGeneratorImage* pImgGen = (KernelArgGeneratorImage*)pDataGen->getArgGenerator(typedInfo); + KernelArgGeneratorImage *pImgGen = + (KernelArgGeneratorImage *)pDataGen->getArgGenerator(typedInfo); // If the channel order is not valid for the current image type, we // continue to the next one. - if (!pImgGen->isValidChannelOrder(context, it.getOpenCLChannelOrder())) + if (!pImgGen->isValidChannelOrder(context, + it.getOpenCLChannelOrder())) continue; - // Due to unknown number of types at the beggining count them on the fly + // Due to unknown number of types at the beggining count them on the + // fly SuccE.TN++; - // Configuring the image generator so it will produce the correct image - // descriptor. + // Configuring the image generator so it will produce the correct + // image descriptor. pImgGen->setChannelOrder(it.getOpenCLChannelOrder()); pDataGen->setArgGenerator(baseInfo, pImgGen); @@ -6308,8 +6269,9 @@ static bool test_image_enumeration_3d(cl_context context, cl_command_queue queue getT(res, 1U, actualOrder), getT(res, 2U, actualTy); if (actualOrder != it.getSPIRChannelOrder()) { - std::cout << " expected channel order: " << it.getSPIRChannelOrder() - << " but received " << actualOrder << "." << std::endl; + std::cout << " expected channel order: " + << it.getSPIRChannelOrder() << " but received " + << actualOrder << "." << std::endl; success = currentSuccess = false; } @@ -6323,7 +6285,8 @@ static bool test_image_enumeration_3d(cl_context context, cl_command_queue queue if (currentSuccess) { (SuccE)(it.toString(), kernelName); - std::cout << "enum_" << it.toString() << " passed." << std::endl; + std::cout << "enum_" << it.toString() << " passed." + << std::endl; } else { @@ -6344,44 +6307,54 @@ static bool test_image_enumeration_3d(cl_context context, cl_command_queue queue return success; } -static bool test_enum_values(cl_device_id device, cl_uint width, const char *folder) +static bool test_enum_values(cl_device_id device, cl_uint width, + const char *folder) { try_extract(folder); std::cout << "Running tests:" << std::endl; bool success = true; - typedef bool (*EnumTest)(cl_context, cl_command_queue, cl_program, cl_device_id, CounterEventHandler &SuccE, std::list &ErrList); - EnumTest test_functions[] = { test_image_enumeration, test_image_enumeration_3d }; + typedef bool (*EnumTest)(cl_context, cl_command_queue, cl_program, + cl_device_id, CounterEventHandler & SuccE, + std::list & ErrList); + EnumTest test_functions[] = { test_image_enumeration, + test_image_enumeration_3d }; const char *enum_tests[] = { HOSTVAL_IMAGE_DESC, HOSTVAL_IMAGE_DESC_3D }; - const size_t TEST_NUM = sizeof(enum_tests)/sizeof(char*); + const size_t TEST_NUM = sizeof(enum_tests) / sizeof(char *); unsigned int tests_passed = 0; CounterEventHandler SuccE(tests_passed, 0); std::list ErrList; // Composing the name of the CSV file. - char* dir = get_exe_dir(); + char *dir = get_exe_dir(); std::string csvName(dir); csvName.append(dir_sep()); csvName.append("khr.csv"); free(dir); // Figure out whether the test can run on the device. If not, we skip it. - const KhrSupport& khrDb = *KhrSupport::get(csvName); + const KhrSupport &khrDb = *KhrSupport::get(csvName); - for (size_t i=0; i &split(const std::string &s, char delim, std::vector &elems) +std::vector &split(const std::string &s, char delim, + std::vector &elems) { std::stringstream ss(s); std::string item; - while (std::getline(ss, item, delim)) { + while (std::getline(ss, item, delim)) + { elems.push_back(item); } return elems; @@ -6516,7 +6493,8 @@ test_kernel_attributes(cl_device_id device, cl_uint width, const char *folder) } #endif -static bool test_binary_type(cl_device_id device, cl_uint width, const char *folder) +static bool test_binary_type(cl_device_id device, cl_uint width, + const char *folder) { std::string bc_file_path; clContextWrapper context; @@ -6541,23 +6519,24 @@ static bool test_binary_type(cl_device_id device, cl_uint width, const char *fol create_context_and_queue(device, &context, &queue); clProgramWrapper clprog = create_program_from_bc(context, bc_file_path); - // Checking the attribute matches the requierment in Section 9.15.2 of the - // extensions SPEC. + // Checking the attribute matches the requierment in Section 9.15.2 of + // the extensions SPEC. cl_int binary_type = 0; size_t ret_size = 0; - if (cl_int err_code = clGetProgramBuildInfo(clprog, device, CL_PROGRAM_BINARY_TYPE, sizeof(cl_int), &binary_type, &ret_size)) + if (cl_int err_code = + clGetProgramBuildInfo(clprog, device, CL_PROGRAM_BINARY_TYPE, + sizeof(cl_int), &binary_type, &ret_size)) { std::cerr << "Cannot run test_binary_type suite due to the " - << "following build error: " - << err_code << std::endl; + << "following build error: " << err_code << std::endl; throw std::exception(); } assert(ret_size == sizeof(cl_int) && "Return size doesn't match."); if (binary_type != CL_PROGRAM_BINARY_TYPE_INTERMEDIATE) { - std::cerr << "binary type is " << binary_type - << " as opposed to " << CL_PROGRAM_BINARY_TYPE_INTERMEDIATE + std::cerr << "binary type is " << binary_type << " as opposed to " + << CL_PROGRAM_BINARY_TYPE_INTERMEDIATE << " which is the expected value." << std::endl; throw std::exception(); } @@ -6572,7 +6551,8 @@ static bool test_binary_type(cl_device_id device, cl_uint width, const char *fol std::cout << std::endl; - std::cout << "PASSED " << tests_passed << " of " << 1 << " tests.\n" << std::endl; + std::cout << "PASSED " << tests_passed << " of " << 1 << " tests.\n" + << std::endl; if (!ErrList.empty()) { @@ -6635,9 +6615,10 @@ static const sub_suite spir_suites[] = { /** Utility function using to find a specific sub-suite name in the SPIR tests. -Called in case the user asked for running a specific sub-suite or specific tests. +Called in case the user asked for running a specific sub-suite or specific +tests. */ -static int find_suite_name (std::string suite_name) +static int find_suite_name(std::string suite_name) { for (unsigned int i = 0; i < sizeof(spir_suites) / sizeof(sub_suite); ++i) { @@ -6653,7 +6634,9 @@ static int find_suite_name (std::string suite_name) /** Look for the first device from the first platform . */ -cl_device_id get_platform_device (cl_device_type device_type, cl_uint choosen_device_index, cl_uint choosen_platform_index) +cl_device_id get_platform_device(cl_device_type device_type, + cl_uint choosen_device_index, + cl_uint choosen_platform_index) { int error = CL_SUCCESS; cl_uint num_platforms = 0; @@ -6663,51 +6646,58 @@ cl_device_id get_platform_device (cl_device_type device_type, cl_uint choosen_de /* Get the platform */ error = clGetPlatformIDs(0, NULL, &num_platforms); - if ( error != CL_SUCCESS ) + if (error != CL_SUCCESS) { - throw std::runtime_error("clGetPlatformIDs failed: " + std::string(IGetErrorString(error))); + throw std::runtime_error("clGetPlatformIDs failed: " + + std::string(IGetErrorString(error))); } - if ( choosen_platform_index >= num_platforms ) + if (choosen_platform_index >= num_platforms) { throw std::runtime_error("platform index out of range"); } - platforms = (cl_platform_id *) malloc( num_platforms * sizeof( cl_platform_id ) ); - if ( !platforms ) + platforms = + (cl_platform_id *)malloc(num_platforms * sizeof(cl_platform_id)); + if (!platforms) { throw std::runtime_error("platform malloc failed"); } BufferOwningPtr platformsBuf(platforms); error = clGetPlatformIDs(num_platforms, platforms, NULL); - if ( error != CL_SUCCESS ) + if (error != CL_SUCCESS) { - throw std::runtime_error("clGetPlatformIDs failed: " + std::string(IGetErrorString(error))); + throw std::runtime_error("clGetPlatformIDs failed: " + + std::string(IGetErrorString(error))); } /* Get the number of requested devices */ - error = clGetDeviceIDs(platforms[choosen_platform_index], device_type, 0, NULL, &num_devices ); - if ( error != CL_SUCCESS ) + error = clGetDeviceIDs(platforms[choosen_platform_index], device_type, 0, + NULL, &num_devices); + if (error != CL_SUCCESS) { - throw std::runtime_error("clGetDeviceIDs failed: " + std::string(IGetErrorString(error))); + throw std::runtime_error("clGetDeviceIDs failed: " + + std::string(IGetErrorString(error))); } - if ( choosen_device_index >= num_devices ) + if (choosen_device_index >= num_devices) { throw std::runtime_error("device index out of rangen"); } - devices = (cl_device_id *) malloc( num_devices * sizeof( cl_device_id ) ); - if ( !devices ) + devices = (cl_device_id *)malloc(num_devices * sizeof(cl_device_id)); + if (!devices) { throw std::runtime_error("device malloc failed"); } BufferOwningPtr devicesBuf(devices); /* Get the requested device */ - error = clGetDeviceIDs(platforms[choosen_platform_index], device_type, num_devices, devices, NULL ); - if ( error != CL_SUCCESS ) + error = clGetDeviceIDs(platforms[choosen_platform_index], device_type, + num_devices, devices, NULL); + if (error != CL_SUCCESS) { - throw std::runtime_error("clGetDeviceIDs failed: " + std::string(IGetErrorString(error))); + throw std::runtime_error("clGetDeviceIDs failed: " + + std::string(IGetErrorString(error))); } return devices[choosen_device_index]; @@ -6730,54 +6720,67 @@ static void ListTests() b) one argument (tests-suite name) - run one SPIR tests-suite c) two arguments (tests-suite name and test name) - run one SPIR test */ -static int ParseCommandLine (int argc, const char *argv[], - std::string& suite_name, std::string& test_name, cl_device_type *device_type, cl_uint *device_index, cl_uint *platform_index, cl_uint *size_t_width) +static int ParseCommandLine(int argc, const char *argv[], + std::string &suite_name, std::string &test_name, + cl_device_type *device_type, cl_uint *device_index, + cl_uint *platform_index, cl_uint *size_t_width) { int based_on_env_var = 0; /* Check for environment variable to set device type */ - char *env_mode = getenv( "CL_DEVICE_TYPE" ); - if( env_mode != NULL ) + char *env_mode = getenv("CL_DEVICE_TYPE"); + if (env_mode != NULL) { based_on_env_var = 1; - if( strcmp( env_mode, "gpu" ) == 0 || strcmp( env_mode, "CL_DEVICE_TYPE_GPU" ) == 0 ) + if (strcmp(env_mode, "gpu") == 0 + || strcmp(env_mode, "CL_DEVICE_TYPE_GPU") == 0) *device_type = CL_DEVICE_TYPE_GPU; - else if( strcmp( env_mode, "cpu" ) == 0 || strcmp( env_mode, "CL_DEVICE_TYPE_CPU" ) == 0 ) + else if (strcmp(env_mode, "cpu") == 0 + || strcmp(env_mode, "CL_DEVICE_TYPE_CPU") == 0) *device_type = CL_DEVICE_TYPE_CPU; - else if( strcmp( env_mode, "accelerator" ) == 0 || strcmp( env_mode, "CL_DEVICE_TYPE_ACCELERATOR" ) == 0 ) + else if (strcmp(env_mode, "accelerator") == 0 + || strcmp(env_mode, "CL_DEVICE_TYPE_ACCELERATOR") == 0) *device_type = CL_DEVICE_TYPE_ACCELERATOR; - else if( strcmp( env_mode, "default" ) == 0 || strcmp( env_mode, "CL_DEVICE_TYPE_DEFAULT" ) == 0 ) + else if (strcmp(env_mode, "default") == 0 + || strcmp(env_mode, "CL_DEVICE_TYPE_DEFAULT") == 0) *device_type = CL_DEVICE_TYPE_DEFAULT; else { - throw Exceptions::CmdLineError( "Unknown CL_DEVICE_TYPE env variable setting\n"); + throw Exceptions::CmdLineError( + "Unknown CL_DEVICE_TYPE env variable setting\n"); } } - env_mode = getenv( "CL_DEVICE_INDEX" ); - if( env_mode != NULL ) + env_mode = getenv("CL_DEVICE_INDEX"); + if (env_mode != NULL) { *device_index = atoi(env_mode); } - env_mode = getenv( "CL_PLATFORM_INDEX" ); - if( env_mode != NULL ) + env_mode = getenv("CL_PLATFORM_INDEX"); + if (env_mode != NULL) { *platform_index = atoi(env_mode); } - /* Process the command line arguments */ + /* Process the command line arguments */ /* Special case: just list the tests */ if ((argc > 1) && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { - log_info( "Usage: %s [] [pid] [id] [] [w32] [no-unzip]\n", argv[0] ); - log_info( "\t\tOne or more of: (default all)\n"); - log_info( "\tpid\t\tIndicates platform at index should be used (default 0).\n" ); - log_info( "\tid\t\tIndicates device at index should be used (default 0).\n" ); - log_info( "\t\tcpu|gpu|accelerator| (default CL_DEVICE_TYPE_DEFAULT)\n" ); - log_info( "\tw32\t\tIndicates device address bits is 32.\n" ); - log_info( "\tno-unzip\t\tDo not extract test files from Zip; use existing.\n" ); + log_info("Usage: %s [] [pid] [id] [] [w32] [no-unzip]\n", + argv[0]); + log_info("\t\tOne or more of: (default all)\n"); + log_info("\tpid\t\tIndicates platform at index should be " + "used (default 0).\n"); + log_info("\tid\t\tIndicates device at index should be used " + "(default 0).\n"); + log_info("\t\tcpu|gpu|accelerator| " + "(default CL_DEVICE_TYPE_DEFAULT)\n"); + log_info("\tw32\t\tIndicates device address bits is 32.\n"); + log_info("\tno-unzip\t\tDo not extract test files from Zip; use " + "existing.\n"); ListTests(); return 0; @@ -6790,87 +6793,93 @@ static int ParseCommandLine (int argc, const char *argv[], } /* Do we have a CPU/GPU specification? */ - while( argc > 1 ) + while (argc > 1) { - if( strcmp( argv[ argc - 1 ], "gpu" ) == 0 || strcmp( argv[ argc - 1 ], "CL_DEVICE_TYPE_GPU" ) == 0 ) + if (strcmp(argv[argc - 1], "gpu") == 0 + || strcmp(argv[argc - 1], "CL_DEVICE_TYPE_GPU") == 0) { *device_type = CL_DEVICE_TYPE_GPU; argc--; } - else if( strcmp( argv[ argc - 1 ], "cpu" ) == 0 || strcmp( argv[ argc - 1 ], "CL_DEVICE_TYPE_CPU" ) == 0 ) + else if (strcmp(argv[argc - 1], "cpu") == 0 + || strcmp(argv[argc - 1], "CL_DEVICE_TYPE_CPU") == 0) { *device_type = CL_DEVICE_TYPE_CPU; argc--; } - else if( strcmp( argv[ argc - 1 ], "accelerator" ) == 0 || strcmp( argv[ argc - 1 ], "CL_DEVICE_TYPE_ACCELERATOR" ) == 0 ) + else if (strcmp(argv[argc - 1], "accelerator") == 0 + || strcmp(argv[argc - 1], "CL_DEVICE_TYPE_ACCELERATOR") == 0) { *device_type = CL_DEVICE_TYPE_ACCELERATOR; argc--; } - else if( strcmp( argv[ argc - 1 ], "CL_DEVICE_TYPE_DEFAULT" ) == 0 ) + else if (strcmp(argv[argc - 1], "CL_DEVICE_TYPE_DEFAULT") == 0) { *device_type = CL_DEVICE_TYPE_DEFAULT; argc--; } - else if( strcmp( argv[ argc - 1 ], "w32" ) == 0 ) + else if (strcmp(argv[argc - 1], "w32") == 0) { *size_t_width = 32; argc--; } - else if( strcmp( argv[ argc - 1 ], "no-unzip" ) == 0 ) + else if (strcmp(argv[argc - 1], "no-unzip") == 0) { no_unzip = 1; argc--; } - else break; + else + break; } /* Did we choose a specific device index? */ - if( argc > 1 ) + if (argc > 1) { - if( strlen( argv[ argc - 1 ] ) >= 3 && argv[ argc - 1 ][0] == 'i' && argv[ argc - 1 ][1] == 'd' ) + if (strlen(argv[argc - 1]) >= 3 && argv[argc - 1][0] == 'i' + && argv[argc - 1][1] == 'd') { - *device_index = atoi( &(argv[ argc - 1 ][2]) ); + *device_index = atoi(&(argv[argc - 1][2])); argc--; } } /* Did we choose a specific platform index? */ - if( argc > 1 ) + if (argc > 1) { - if( strlen( argv[ argc - 1 ] ) >= 3 && argv[ argc - 1 ][0] == 'p' && argv[ argc - 1 ][1] == 'i' && argv[ argc - 1 ][2] == 'd') + if (strlen(argv[argc - 1]) >= 3 && argv[argc - 1][0] == 'p' + && argv[argc - 1][1] == 'i' && argv[argc - 1][2] == 'd') { - *platform_index = atoi( &(argv[ argc - 1 ][3]) ); + *platform_index = atoi(&(argv[argc - 1][3])); argc--; } } - switch( *device_type ) + switch (*device_type) { - case CL_DEVICE_TYPE_GPU: - log_info( "Requesting GPU device " ); - break; - case CL_DEVICE_TYPE_CPU: - log_info( "Requesting CPU device " ); - break; + case CL_DEVICE_TYPE_GPU: log_info("Requesting GPU device "); break; + case CL_DEVICE_TYPE_CPU: log_info("Requesting CPU device "); break; case CL_DEVICE_TYPE_ACCELERATOR: - log_info( "Requesting Accelerator device " ); + log_info("Requesting Accelerator device "); break; case CL_DEVICE_TYPE_DEFAULT: - log_info( "Requesting Default device " ); + log_info("Requesting Default device "); break; default: - throw Exceptions::CmdLineError( "Requesting unknown device "); + throw Exceptions::CmdLineError("Requesting unknown device "); break; } - log_info( based_on_env_var ? "based on environment variable " : "based on command line " ); - log_info( "for platform index %d and device index %d\n", *platform_index, *device_index); + log_info(based_on_env_var ? "based on environment variable " + : "based on command line "); + log_info("for platform index %d and device index %d\n", *platform_index, + *device_index); if (argc > 3) { - throw Exceptions::CmdLineError("Command line error. Unrecognized token\n"); + throw Exceptions::CmdLineError( + "Command line error. Unrecognized token\n"); } - else { + else + { if (argc > 1) { suite_name.assign(argv[1]); @@ -6884,27 +6893,29 @@ static int ParseCommandLine (int argc, const char *argv[], return 1; } -struct WLMsg: EventHandler +struct WLMsg : EventHandler { - const char* Msg; + const char *Msg; - WLMsg(const char* M): Msg(M){} + WLMsg(const char *M): Msg(M) {} - void operator()(const std::string& T, const std::string& K) + void operator()(const std::string &T, const std::string &K) { - std::cout << "Test " << T << " Kernel " << K << "\t" << Msg << std::endl; + std::cout << "Test " << T << " Kernel " << K << "\t" << Msg + << std::endl; } }; -int main (int argc, const char* argv[]) +int main(int argc, const char *argv[]) { - std::string test_suite_name; // name of the selected tests-suite (NULL for all) - std::string test_file_name; // name of the .selected test (NULL for all) + std::string + test_suite_name; // name of the selected tests-suite (NULL for all) + std::string test_file_name; // name of the .selected test (NULL for all) cl_device_type device_type = CL_DEVICE_TYPE_DEFAULT; cl_uint choosen_device_index = 0; cl_uint choosen_platform_index = 0; - cl_uint size_t_width = 0; // device address bits (32 or 64). + cl_uint size_t_width = 0; // device address bits (32 or 64). cl_int err; int failed = 0; size_t ntests = 0; @@ -6916,10 +6927,14 @@ int main (int argc, const char* argv[]) WLMsg Success("\t\tPassed"), Failure("\t\tFailure"); try { - if (ParseCommandLine(argc, argv, test_suite_name, test_file_name, &device_type, &choosen_device_index, &choosen_platform_index, &size_t_width) == 0) + if (ParseCommandLine(argc, argv, test_suite_name, test_file_name, + &device_type, &choosen_device_index, + &choosen_platform_index, &size_t_width) + == 0) return 0; - cl_device_id device = get_platform_device(device_type, choosen_device_index, choosen_platform_index); + cl_device_id device = get_platform_device( + device_type, choosen_device_index, choosen_platform_index); printDeviceHeader(device); REQUIRE_EXTENSION("cl_khr_spir"); @@ -6930,38 +6945,46 @@ int main (int argc, const char* argv[]) if (std::find(versions.begin(), versions.end(), Version{ 1, 2 }) == versions.end()) { - log_info("Spir extension version 1.2 is not supported by the device\n"); + log_info( + "Spir extension version 1.2 is not supported by the device\n"); return 0; } - // size_t_width <> 0 - device address bits is forced by command line argument - if ((0 == size_t_width) && ((err = clGetDeviceInfo(device, CL_DEVICE_ADDRESS_BITS, sizeof(cl_uint), &size_t_width, NULL)))) + // size_t_width <> 0 - device address bits is forced by command line + // argument + if ((0 == size_t_width) + && ((err = clGetDeviceInfo(device, CL_DEVICE_ADDRESS_BITS, + sizeof(cl_uint), &size_t_width, NULL)))) { - print_error( err, "Unable to obtain device address bits" ); + print_error(err, "Unable to obtain device address bits"); return -1; } - if (! test_suite_name.empty()) + if (!test_suite_name.empty()) { // command line is not empty - do not run all the tests int tsn = find_suite_name(test_suite_name); ntests = 1; if (tsn < 0) { - throw Exceptions::CmdLineError("Command line error. Error in SPIR sub-suite name\n"); + throw Exceptions::CmdLineError( + "Command line error. Error in SPIR sub-suite name\n"); } else if (test_file_name.empty()) { - if (!spir_suites[tsn].test_function(device, size_t_width, spir_suites[tsn].folder)) + if (!spir_suites[tsn].test_function(device, size_t_width, + spir_suites[tsn].folder)) failed++; } else { - OclExtensions devExt = OclExtensions::getDeviceCapabilities(device); + OclExtensions devExt = + OclExtensions::getDeviceCapabilities(device); TestRunner runner(&Success, &Failure, devExt); std::string folder = getTestFolder(test_suite_name.c_str()); try_extract(folder.c_str()); - if (!runner.runBuildTest(device, folder.c_str(), test_file_name.c_str(), size_t_width)) + if (!runner.runBuildTest(device, folder.c_str(), + test_file_name.c_str(), size_t_width)) failed++; } } @@ -6971,30 +6994,31 @@ int main (int argc, const char* argv[]) ntests = (sizeof(spir_suites) / sizeof(spir_suites[0])); for (unsigned int i = 0; i < ntests; ++i) { - if (!spir_suites[i].test_function(device, size_t_width, spir_suites[i].folder)) + if (!spir_suites[i].test_function(device, size_t_width, + spir_suites[i].folder)) failed++; } } if (failed) - std::cout << "FAILED " << failed << " of " << ntests << " test suites.\n" << std::endl; + std::cout << "FAILED " << failed << " of " << ntests + << " test suites.\n" + << std::endl; else - std::cout << "PASSED " << ntests << " of " << ntests << " test suites.\n" << std::endl; + std::cout << "PASSED " << ntests << " of " << ntests + << " test suites.\n" + << std::endl; return failed; - } - catch(const Exceptions::CmdLineError& e) + } catch (const Exceptions::CmdLineError &e) { print_error(1, e.what()); return 1; - } - catch(const std::runtime_error& e) + } catch (const std::runtime_error &e) { print_error(2, e.what()); return 2; - } - catch(const std::exception& e) + } catch (const std::exception &e) { print_error(3, e.what()); return 3; } } - diff --git a/test_conformance/spir/run_build_test.cpp b/test_conformance/spir/run_build_test.cpp index 46f9d022..697cd65c 100644 --- a/test_conformance/spir/run_build_test.cpp +++ b/test_conformance/spir/run_build_test.cpp @@ -1,6 +1,6 @@ // // Copyright (c) 2017 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 @@ -43,35 +43,37 @@ // // Task // -Task::Task(cl_device_id device, const char* options): -m_devid(device) { - if (options) - m_options = options; +Task::Task(cl_device_id device, const char* options): m_devid(device) +{ + if (options) m_options = options; } Task::~Task() {} -const char* Task::getErrorLog() const { - return m_log.c_str(); -} +const char* Task::getErrorLog() const { return m_log.c_str(); } -void Task::setErrorLog(cl_program prog) { +void Task::setErrorLog(cl_program prog) +{ size_t len = 0; std::vector log; - cl_int err_code = clGetProgramBuildInfo(prog, m_devid, CL_PROGRAM_BUILD_LOG, 0, NULL, &len); - if(err_code != CL_SUCCESS) + cl_int err_code = clGetProgramBuildInfo(prog, m_devid, CL_PROGRAM_BUILD_LOG, + 0, NULL, &len); + if (err_code != CL_SUCCESS) { - m_log = "Error: clGetProgramBuildInfo(CL_PROGRAM_BUILD_LOG, &len) failed.\n"; + m_log = "Error: clGetProgramBuildInfo(CL_PROGRAM_BUILD_LOG, &len) " + "failed.\n"; return; } log.resize(len, 0); - err_code = clGetProgramBuildInfo(prog, m_devid, CL_PROGRAM_BUILD_LOG, len, &log[0], NULL); - if(err_code != CL_SUCCESS) + err_code = clGetProgramBuildInfo(prog, m_devid, CL_PROGRAM_BUILD_LOG, len, + &log[0], NULL); + if (err_code != CL_SUCCESS) { - m_log = "Error: clGetProgramBuildInfo(CL_PROGRAM_BUILD_LOG, &log) failed.\n"; + m_log = "Error: clGetProgramBuildInfo(CL_PROGRAM_BUILD_LOG, &log) " + "failed.\n"; return; } m_log.append(&log[0]); @@ -84,10 +86,11 @@ BuildTask::BuildTask(cl_program prog, cl_device_id dev, const char* options) : Task(dev, options), m_program(prog) {} -bool BuildTask::execute() { - cl_int err_code = clBuildProgram(m_program, 0, NULL, m_options.c_str(), NULL, NULL); - if(CL_SUCCESS == err_code) - return true; +bool BuildTask::execute() +{ + cl_int err_code = + clBuildProgram(m_program, 0, NULL, m_options.c_str(), NULL, NULL); + if (CL_SUCCESS == err_code) return true; setErrorLog(m_program); return false; @@ -96,8 +99,10 @@ bool BuildTask::execute() { // // SpirBuildTask // -SpirBuildTask::SpirBuildTask(cl_program prog, cl_device_id dev, const char* options) : - BuildTask(prog, dev, options) {} +SpirBuildTask::SpirBuildTask(cl_program prog, cl_device_id dev, + const char* options) + : BuildTask(prog, dev, options) +{} // // CompileTask @@ -107,47 +112,43 @@ CompileTask::CompileTask(cl_program prog, cl_device_id dev, const char* options) : Task(dev, options), m_program(prog) {} -void CompileTask::addHeader(const char* hname, cl_program hprog) { +void CompileTask::addHeader(const char* hname, cl_program hprog) +{ m_headers.push_back(std::make_pair(hname, hprog)); } -const char* first(std::pair& p) { - return p.first; -} +const char* first(std::pair& p) { return p.first; } -cl_program second(const std::pair& p) { +cl_program second(const std::pair& p) +{ return p.second; } -bool CompileTask::execute() { +bool CompileTask::execute() +{ // Generating the header names vector. std::vector names; std::transform(m_headers.begin(), m_headers.end(), names.begin(), first); // Generating the header programs vector. std::vector programs; - std::transform(m_headers.begin(), m_headers.end(), programs.begin(), second); + std::transform(m_headers.begin(), m_headers.end(), programs.begin(), + second); const char** h_names = NULL; const cl_program* h_programs = NULL; if (!m_headers.empty()) { h_programs = &programs[0]; - h_names = &names[0]; + h_names = &names[0]; } // Compiling with the headers. - cl_int err_code = clCompileProgram( - m_program, - 1U, - &m_devid, - m_options.c_str(), - m_headers.size(), // # of headers - h_programs, - h_names, - NULL, NULL); - if (CL_SUCCESS == err_code) - return true; + cl_int err_code = + clCompileProgram(m_program, 1U, &m_devid, m_options.c_str(), + m_headers.size(), // # of headers + h_programs, h_names, NULL, NULL); + if (CL_SUCCESS == err_code) return true; setErrorLog(m_program); return false; @@ -156,8 +157,10 @@ bool CompileTask::execute() { // // SpirCompileTask // -SpirCompileTask::SpirCompileTask(cl_program prog, cl_device_id dev, const char* options) : - CompileTask(prog, dev, options) {} +SpirCompileTask::SpirCompileTask(cl_program prog, cl_device_id dev, + const char* options) + : CompileTask(prog, dev, options) +{} // @@ -169,13 +172,16 @@ LinkTask::LinkTask(cl_program* programs, int num_programs, cl_context ctxt, m_numPrograms(num_programs), m_context(ctxt) {} -bool LinkTask::execute() { +bool LinkTask::execute() +{ cl_int err_code; int i; - for(i = 0; i < m_numPrograms; ++i) + for (i = 0; i < m_numPrograms; ++i) { - err_code = clCompileProgram(m_programs[i], 1, &m_devid, "-x spir -spir-std=1.2 -cl-kernel-arg-info", 0, NULL, NULL, NULL, NULL); + err_code = clCompileProgram(m_programs[i], 1, &m_devid, + "-x spir -spir-std=1.2 -cl-kernel-arg-info", + 0, NULL, NULL, NULL, NULL); if (CL_SUCCESS != err_code) { setErrorLog(m_programs[i]); @@ -183,91 +189,78 @@ bool LinkTask::execute() { } } - m_executable = clLinkProgram(m_context, 1, &m_devid, m_options.c_str(), m_numPrograms, m_programs, NULL, NULL, &err_code); - if (CL_SUCCESS == err_code) - return true; + m_executable = + clLinkProgram(m_context, 1, &m_devid, m_options.c_str(), m_numPrograms, + m_programs, NULL, NULL, &err_code); + if (CL_SUCCESS == err_code) return true; - if(m_executable) setErrorLog(m_executable); + if (m_executable) setErrorLog(m_executable); return false; } -cl_program LinkTask::getExecutable() const { - return m_executable; -} +cl_program LinkTask::getExecutable() const { return m_executable; } -LinkTask::~LinkTask() { - if(m_executable) clReleaseProgram(m_executable); +LinkTask::~LinkTask() +{ + if (m_executable) clReleaseProgram(m_executable); } // // KernelEnumerator // -void KernelEnumerator::process(cl_program prog) { +void KernelEnumerator::process(cl_program prog) +{ const size_t MAX_KERNEL_NAME = 64; size_t num_kernels; - cl_int err_code = clGetProgramInfo( - prog, - CL_PROGRAM_NUM_KERNELS, - sizeof(size_t), - &num_kernels, - NULL - ); - if (CL_SUCCESS != err_code) - return; + cl_int err_code = clGetProgramInfo(prog, CL_PROGRAM_NUM_KERNELS, + sizeof(size_t), &num_kernels, NULL); + if (CL_SUCCESS != err_code) return; // Querying for the number of kernels. - size_t buffer_len = sizeof(char)*num_kernels*MAX_KERNEL_NAME; + size_t buffer_len = sizeof(char) * num_kernels * MAX_KERNEL_NAME; char* kernel_names = new char[buffer_len]; memset(kernel_names, '\0', buffer_len); size_t str_len = 0; - err_code = clGetProgramInfo( - prog, - CL_PROGRAM_KERNEL_NAMES, - buffer_len, - (void *)kernel_names, - &str_len - ); - if (CL_SUCCESS != err_code) - return; + err_code = clGetProgramInfo(prog, CL_PROGRAM_KERNEL_NAMES, buffer_len, + (void*)kernel_names, &str_len); + if (CL_SUCCESS != err_code) return; - //parsing the names and inserting them to the list + // parsing the names and inserting them to the list std::string names(kernel_names); - assert (str_len == 1+names.size() && "incompatible string lengths"); + assert(str_len == 1 + names.size() && "incompatible string lengths"); size_t offset = 0; - for(size_t i=0 ; iname); if (strstr(test_name, name)) { @@ -336,16 +330,17 @@ static float get_max_ulps(const char *test_name) return ulps; } -TestRunner::TestRunner(EventHandler *success, EventHandler *failure, - const OclExtensions& devExt): - m_successHandler(success), m_failureHandler(failure), m_devExt(&devExt) {} +TestRunner::TestRunner(EventHandler* success, EventHandler* failure, + const OclExtensions& devExt) + : m_successHandler(success), m_failureHandler(failure), m_devExt(&devExt) +{} /** Based on the test name build the cl file name, the bc file name and execute the kernel for both modes (cl and bc). */ -bool TestRunner::runBuildTest(cl_device_id device, const char *folder, - const char *test_name, cl_uint size_t_width) +bool TestRunner::runBuildTest(cl_device_id device, const char* folder, + const char* test_name, cl_uint size_t_width) { int failures = 0; // Composing the name of the CSV file. @@ -365,28 +360,35 @@ bool TestRunner::runBuildTest(cl_device_id device, const char *folder, cl_bool images3D = khrDb.isImages3DRequired(folder, test_name); char deviceProfile[64]; - clGetDeviceInfo(device, CL_DEVICE_PROFILE, sizeof(deviceProfile), &deviceProfile, NULL); + clGetDeviceInfo(device, CL_DEVICE_PROFILE, sizeof(deviceProfile), + &deviceProfile, NULL); std::string device_profile(deviceProfile, 64); - if(images == CL_TRUE && checkForImageSupport(device) != 0) + if (images == CL_TRUE && checkForImageSupport(device) != 0) { (*m_successHandler)(test_name, ""); - std::cout << "Skipped. (Cannot run on device due to Images is not supported)." << std::endl; + std::cout + << "Skipped. (Cannot run on device due to Images is not supported)." + << std::endl; return true; } - if(images3D == CL_TRUE && checkFor3DImageSupport(device) != 0) + if (images3D == CL_TRUE && checkFor3DImageSupport(device) != 0) { (*m_successHandler)(test_name, ""); - std::cout << "Skipped. (Cannot run on device as 3D images are not supported)." << std::endl; + std::cout + << "Skipped. (Cannot run on device as 3D images are not supported)." + << std::endl; return true; } OclExtensions requiredExt = khrDb.getRequiredExtensions(folder, test_name); - if(!m_devExt->supports(requiredExt)) + if (!m_devExt->supports(requiredExt)) { (*m_successHandler)(test_name, ""); - std::cout << "Skipped. (Cannot run on device due to missing extensions: " << m_devExt->get_missing(requiredExt) << " )." << std::endl; + std::cout + << "Skipped. (Cannot run on device due to missing extensions: " + << m_devExt->get_missing(requiredExt) << " )." << std::endl; return true; } @@ -409,17 +411,26 @@ bool TestRunner::runBuildTest(cl_device_id device, const char *folder, cl_device_fp_config gFloatCapabilities = 0; cl_int err; - if ((err = clGetDeviceInfo(device, CL_DEVICE_SINGLE_FP_CONFIG, sizeof(gFloatCapabilities), &gFloatCapabilities, NULL))) + if ((err = clGetDeviceInfo(device, CL_DEVICE_SINGLE_FP_CONFIG, + sizeof(gFloatCapabilities), &gFloatCapabilities, + NULL))) { - log_info("Unable to get device CL_DEVICE_SINGLE_FP_CONFIG. (%d)\n", err); + log_info("Unable to get device CL_DEVICE_SINGLE_FP_CONFIG. (%d)\n", + err); } - if (strstr(test_name, "div_cr") || strstr(test_name, "sqrt_cr")) { - if ((gFloatCapabilities & CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT) == 0) { + if (strstr(test_name, "div_cr") || strstr(test_name, "sqrt_cr")) + { + if ((gFloatCapabilities & CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT) == 0) + { (*m_successHandler)(test_name, ""); - std::cout << "Skipped. (Cannot run on device due to missing CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT property.)" << std::endl; + std::cout << "Skipped. (Cannot run on device due to missing " + "CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT property.)" + << std::endl; return true; - } else { + } + else + { bcoptions += " -cl-fp32-correctly-rounded-divide-sqrt"; cloptions += " -cl-fp32-correctly-rounded-divide-sqrt"; } @@ -427,33 +438,39 @@ bool TestRunner::runBuildTest(cl_device_id device, const char *folder, // Building the programs. BuildTask clBuild(clprog, device, cloptions.c_str()); - if (!clBuild.execute()) { + if (!clBuild.execute()) + { std::cerr << clBuild.getErrorLog() << std::endl; + (*m_failureHandler)(test_name, ""); return false; } SpirBuildTask bcBuild(bcprog, device, bcoptions.c_str()); - if (!bcBuild.execute()) { + if (!bcBuild.execute()) + { std::cerr << bcBuild.getErrorLog() << std::endl; + (*m_failureHandler)(test_name, ""); return false; } - KernelEnumerator clkernel_enumerator(clprog), - bckernel_enumerator(bcprog); - if (clkernel_enumerator.size() != bckernel_enumerator.size()) { + KernelEnumerator clkernel_enumerator(clprog), bckernel_enumerator(bcprog); + if (clkernel_enumerator.size() != bckernel_enumerator.size()) + { std::cerr << "number of kernels in test" << test_name << " doesn't match in bc and cl files" << std::endl; + (*m_failureHandler)(test_name, ""); return false; } KernelEnumerator::iterator it = clkernel_enumerator.begin(), - e = clkernel_enumerator.end(); + e = clkernel_enumerator.end(); while (it != e) { std::string kernel_name = *it++; std::string err; try { - bool success = run_test(context, queue, clprog, bcprog, kernel_name, err, device, ulps); + bool success = run_test(context, queue, clprog, bcprog, kernel_name, + err, device, ulps); if (success) { log_info("kernel '%s' passed.\n", kernel_name.c_str()); @@ -468,7 +485,8 @@ bool TestRunner::runBuildTest(cl_device_id device, const char *folder, } catch (const std::runtime_error& err) { ++failures; - log_info("kernel '%s' failed: %s\n", kernel_name.c_str(), err.what()); + log_info("kernel '%s' failed: %s\n", kernel_name.c_str(), + err.what()); (*m_failureHandler)(test_name, kernel_name); } } @@ -476,4 +494,3 @@ bool TestRunner::runBuildTest(cl_device_id device, const char *folder, log_info("%s %s\n", test_name, failures ? "FAILED" : "passed."); return failures == 0; } - From 79d98433cc168a3918b2163ddf7612125211d387 Mon Sep 17 00:00:00 2001 From: Xin Jin Date: Tue, 21 Oct 2025 18:40:35 +0100 Subject: [PATCH 19/33] Fix release call in AHB image read test (#2548) Ensure clEnqueueReleaseExternalMemObjectsKHR targets imported_image instead of the non-external opencl_image, matching the prior acquire call. Signed-off-by: Xin Jin --- .../extensions/cl_khr_external_memory_ahb/test_ahb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb.cpp b/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb.cpp index f0747d0e..a73af256 100644 --- a/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb.cpp +++ b/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb.cpp @@ -394,7 +394,7 @@ REGISTER_TEST(test_images_read) test_error(err, "clEnqueueNDRangeKernel failed"); err = clEnqueueReleaseExternalMemObjectsKHR( - queue, 1, &opencl_image, 0, nullptr, nullptr); + queue, 1, &imported_image, 0, nullptr, nullptr); test_error(err, "clEnqueueReleaseExternalMemObjectsKHR failed"); // Read buffer and verify From 62972418c3c9c9a06e84738c926f278f59a518f2 Mon Sep 17 00:00:00 2001 From: Marcin Hajder Date: Wed, 22 Oct 2025 16:01:48 +0200 Subject: [PATCH 20/33] Added support for cl_ext_float_atomics in CBasicTestFetchAdd with atomic_half (#2350) Related to https://github.com/KhronosGroup/OpenCL-CTS/issues/2142, according to the work plan, extending CBasicTestFetchAdd with support for atomic_half. I wasn't able to test that PR completely due to missing `CL_DEVICE_LOCAL_FP_ATOMIC_ADD_EXT`/`CL_DEVICE_GLOBAL_FP_ATOMIC_ADD_EXT` capabilities for atomic_half. I appreciate reviewers' attention, thanks. --- test_conformance/c11_atomics/host_atomics.h | 13 +- test_conformance/c11_atomics/test_atomics.cpp | 125 ++++++++++++++++-- 2 files changed, 123 insertions(+), 15 deletions(-) diff --git a/test_conformance/c11_atomics/host_atomics.h b/test_conformance/c11_atomics/host_atomics.h index 4471897b..27560822 100644 --- a/test_conformance/c11_atomics/host_atomics.h +++ b/test_conformance/c11_atomics/host_atomics.h @@ -99,7 +99,16 @@ template CorrespondingType host_atomic_fetch_add(volatile AtomicType *a, CorrespondingType c, TExplicitMemoryOrderType order) { - if constexpr ( + if constexpr (std::is_same_v) + { + static std::mutex mx; + std::lock_guard lock(mx); + CorrespondingType old_value = *a; + *a = cl_half_from_float((cl_half_to_float(*a) + cl_half_to_float(c)), + gHalfRoundingMode); + return old_value; + } + else if constexpr ( std::is_same_v< AtomicType, HOST_ATOMIC_FLOAT> || std::is_same_v) @@ -112,7 +121,7 @@ CorrespondingType host_atomic_fetch_add(volatile AtomicType *a, CorrespondingTyp } else { -#if defined( _MSC_VER ) || (defined( __INTEL_COMPILER ) && defined(WIN32)) +#if defined(_MSC_VER) || (defined(__INTEL_COMPILER) && defined(WIN32)) return InterlockedExchangeAdd(a, c); #elif defined(__GNUC__) return __sync_fetch_and_add(a, c); diff --git a/test_conformance/c11_atomics/test_atomics.cpp b/test_conformance/c11_atomics/test_atomics.cpp index d73bb6b8..0712f109 100644 --- a/test_conformance/c11_atomics/test_atomics.cpp +++ b/test_conformance/c11_atomics/test_atomics.cpp @@ -417,7 +417,7 @@ public: correct = true; for (cl_uint i = 0; i < threadCount; i++) { - if constexpr (std::is_same::value) + if constexpr (std::is_same_v) { HostDataType test = cl_half_from_float(static_cast(i), gHalfRoundingMode); @@ -1204,17 +1204,79 @@ public: if constexpr ( std::is_same_v< HostDataType, - HOST_FLOAT> || std::is_same_v) + HOST_HALF> || std::is_same_v || std::is_same_v) { StartValue((HostDataType)0.0); CBasicTestMemOrderScope::OldValueCheck(false); } } + template float accum_halfs(Iterator begin, Iterator end) + { + cl_half sum = 0; + for (auto it = begin; it != end; ++it) + { + sum = cl_half_from_float(cl_half_to_float(sum) + + cl_half_to_float(*it), + gHalfRoundingMode); + } + return cl_half_to_float(sum); + } bool GenerateRefs(cl_uint threadCount, HostDataType *startRefValues, MTdata d) override { - if constexpr ( + if constexpr (std::is_same_v) + { + if (threadCount > ref_vals.size()) + { + ref_vals.resize(threadCount); + + for (cl_uint i = 0; i < threadCount; i++) + ref_vals[i] = cl_half_from_float( + get_random_float(min_range, max_range, d), + gHalfRoundingMode); + + memcpy(startRefValues, ref_vals.data(), + sizeof(HostDataType) * ref_vals.size()); + + // Estimate highest possible summation error for given set. + std::vector sums; + std::sort(ref_vals.begin(), ref_vals.end(), + [](cl_half a, cl_half b) { + return cl_half_to_float(a) < cl_half_to_float(b); + }); + + sums.push_back(accum_halfs(ref_vals.begin(), ref_vals.end())); + sums.push_back(accum_halfs(ref_vals.rbegin(), ref_vals.rend())); + + std::sort(ref_vals.begin(), ref_vals.end(), + [](cl_half a, cl_half b) { + return std::abs(cl_half_to_float(a)) + < std::abs(cl_half_to_float(b)); + }); + + float precise = 0.f; + for (auto elem : ref_vals) precise += cl_half_to_float(elem); + sums.push_back(precise); + + sums.push_back(accum_halfs(ref_vals.begin(), ref_vals.end())); + sums.push_back(accum_halfs(ref_vals.rbegin(), ref_vals.rend())); + + std::sort(sums.begin(), sums.end()); + max_error = std::abs(sums.front() - sums.back()); + + // restore unsorted order + memcpy(ref_vals.data(), startRefValues, + sizeof(HostDataType) * ref_vals.size()); + } + else + { + memcpy(startRefValues, ref_vals.data(), + sizeof(HostDataType) * threadCount); + } + return true; + } + else if constexpr ( std::is_same_v< HostDataType, HOST_FLOAT> || std::is_same_v) @@ -1286,7 +1348,7 @@ public: if constexpr ( std::is_same_v< HostDataType, - HOST_DOUBLE> || std::is_same_v) + HOST_HALF> || std::is_same_v || std::is_same_v) { return " atomic_fetch_add" + postfix + "(&destMemory[0], (" + DataType().AddSubOperandTypeName() + ")oldValues[tid]" @@ -1323,7 +1385,7 @@ public: if constexpr ( std::is_same_v< HostDataType, - HOST_DOUBLE> || std::is_same_v) + HOST_HALF> || std::is_same_v || std::is_same_v) { host_atomic_fetch_add(&destMemory[0], (HostDataType)oldValues[tid], MemoryOrder()); @@ -1349,7 +1411,20 @@ public: cl_uint whichDestValue) override { expected = StartValue(); - if constexpr ( + if constexpr (std::is_same_v) + { + if (whichDestValue == 0) + { + for (cl_uint i = 0; i < threadCount; i++) + { + expected = cl_half_from_float( + cl_half_to_float(expected) + + cl_half_to_float(startRefValues[i]), + gHalfRoundingMode); + } + } + } + else if constexpr ( std::is_same_v< HostDataType, HOST_DOUBLE> || std::is_same_v) @@ -1371,10 +1446,17 @@ public: const std::vector &testValues, cl_uint whichDestValue) override { - if constexpr ( + if constexpr (std::is_same_v) + { + if (whichDestValue == 0) + return std::abs(cl_half_to_float(expected) + - cl_half_to_float(testValues[whichDestValue])) + > max_error; + } + else if constexpr ( std::is_same_v< HostDataType, - HOST_DOUBLE> || std::is_same::value) + HOST_DOUBLE> || std::is_same_v) { if (whichDestValue == 0) return std::abs((HostDataType)expected @@ -1389,8 +1471,10 @@ public: bool VerifyRefs(bool &correct, cl_uint threadCount, HostDataType *refValues, HostAtomicType *finalValues) override { - if (std::is_same::value - || std::is_same::value) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v || std::is_same_v) { correct = true; for (cl_uint i = 1; i < threadCount; i++) @@ -1413,7 +1497,17 @@ public: int ExecuteSingleTest(cl_device_id deviceID, cl_context context, cl_command_queue queue) override { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (LocalMemory() + && (gHalfAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_ADD_EXT) == 0) + return 0; // skip test - not applicable + + if (!LocalMemory() + && (gHalfAtomicCaps & CL_DEVICE_GLOBAL_FP_ATOMIC_ADD_EXT) == 0) + return 0; + } + else if constexpr (std::is_same_v) { if (LocalMemory() && (gDoubleAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_ADD_EXT) == 0) @@ -1443,7 +1537,7 @@ public: if constexpr ( std::is_same_v< HostDataType, - HOST_DOUBLE> || std::is_same_v) + HOST_HALF> || std::is_same_v || std::is_same_v) { return threadCount; } @@ -1478,6 +1572,11 @@ static int test_atomic_fetch_add_generic(cl_device_id deviceID, if (gFloatAtomicsSupported) { + CBasicTestFetchAdd test_half( + TYPE_ATOMIC_HALF, useSVM); + EXECUTE_TEST(error, + test_half.Execute(deviceID, context, queue, num_elements)); + CBasicTestFetchAdd test_double( TYPE_ATOMIC_DOUBLE, useSVM); EXECUTE_TEST( @@ -1737,7 +1836,7 @@ public: bool VerifyRefs(bool &correct, cl_uint threadCount, HostDataType *refValues, HostAtomicType *finalValues) override { - if (std::is_same::value) + if (std::is_same_v) { correct = true; for (cl_uint i = 1; i < threadCount; i++) From 52ba127f79b2ef961f66ffe91955c8bb0fd457a2 Mon Sep 17 00:00:00 2001 From: Marcin Hajder Date: Tue, 28 Oct 2025 16:40:13 +0100 Subject: [PATCH 21/33] Added support for cl_ext_float_atomics in CBasicTestFetchMin/Max with atomic_double (#2361) Related to #2142, according to the work plan, extending CBasicTestFetchMin/CBasicTestFetchMax with support for atomic_double. --- test_conformance/c11_atomics/common.cpp | 21 +++++-- test_conformance/c11_atomics/host_atomics.h | 7 ++- test_conformance/c11_atomics/test_atomics.cpp | 62 +++++++++++++++---- 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/test_conformance/c11_atomics/common.cpp b/test_conformance/c11_atomics/common.cpp index 4838c347..3be3fbc1 100644 --- a/test_conformance/c11_atomics/common.cpp +++ b/test_conformance/c11_atomics/common.cpp @@ -193,19 +193,28 @@ int AtomicTypeInfo::IsSupported(cl_device_id device) template<> cl_int AtomicTypeExtendedInfo::MinValue() {return CL_INT_MIN;} template<> cl_uint AtomicTypeExtendedInfo::MinValue() {return 0;} template<> cl_long AtomicTypeExtendedInfo::MinValue() {return CL_LONG_MIN;} -template<> cl_ulong AtomicTypeExtendedInfo::MinValue() {return 0;} +template <> cl_ulong AtomicTypeExtendedInfo::MinValue() { return 0; } template <> cl_half AtomicTypeExtendedInfo::MinValue() { - return cl_half_from_float(CL_HALF_MIN, gHalfRoundingMode); + return cl_half_from_float(-CL_HALF_MAX, gHalfRoundingMode); } template <> cl_float AtomicTypeExtendedInfo::MinValue() { - return CL_FLT_MIN; + return -CL_FLT_MAX; +} +template <> cl_double AtomicTypeExtendedInfo::MinValue() +{ + return -CL_DBL_MAX; } -template<> cl_double AtomicTypeExtendedInfo::MinValue() {return CL_DBL_MIN;} -template<> cl_int AtomicTypeExtendedInfo::MaxValue() {return CL_INT_MAX;} -template<> cl_uint AtomicTypeExtendedInfo::MaxValue() {return CL_UINT_MAX;} +template <> cl_int AtomicTypeExtendedInfo::MaxValue() +{ + return CL_INT_MAX; +} +template <> cl_uint AtomicTypeExtendedInfo::MaxValue() +{ + return CL_UINT_MAX; +} template<> cl_long AtomicTypeExtendedInfo::MaxValue() {return CL_LONG_MAX;} template<> cl_ulong AtomicTypeExtendedInfo::MaxValue() {return CL_ULONG_MAX;} template <> cl_half AtomicTypeExtendedInfo::MaxValue() diff --git a/test_conformance/c11_atomics/host_atomics.h b/test_conformance/c11_atomics/host_atomics.h index 27560822..a0588ef4 100644 --- a/test_conformance/c11_atomics/host_atomics.h +++ b/test_conformance/c11_atomics/host_atomics.h @@ -198,14 +198,17 @@ bool host_atomic_compare_exchange(volatile AtomicType *a, CorrespondingType *exp } *expected = tmp; } - else if constexpr (std::is_same_v) + else if constexpr ( + std::is_same_v< + AtomicType, + HOST_ATOMIC_DOUBLE> || std::is_same_v) { static std::mutex mtx; std::lock_guard lock(mtx); tmp = *reinterpret_cast(a); if (tmp == *expected) { - *reinterpret_cast(a) = desired; + *a = desired; return true; } *expected = tmp; diff --git a/test_conformance/c11_atomics/test_atomics.cpp b/test_conformance/c11_atomics/test_atomics.cpp index 0712f109..5a553a06 100644 --- a/test_conformance/c11_atomics/test_atomics.cpp +++ b/test_conformance/c11_atomics/test_atomics.cpp @@ -2802,7 +2802,7 @@ public: if constexpr ( std::is_same_v< HostDataType, - HOST_HALF> || std::is_same_v) + HOST_HALF> || std::is_same_v || std::is_same_v) { return " atomic_fetch_min" + postfix + "(&destMemory[0], oldValues[tid] " + memoryOrderScope + ");\n" @@ -2824,7 +2824,7 @@ public: if constexpr ( std::is_same_v< HostDataType, - HOST_HALF> || std::is_same_v) + HOST_HALF> || std::is_same_v || std::is_same_v) { host_atomic_fetch_min(&destMemory[0], oldValues[tid], MemoryOrder()); @@ -2849,7 +2849,10 @@ public: gHalfRoundingMode); } } - else if constexpr (std::is_same_v) + else if constexpr ( + std::is_same_v< + HostDataType, + HOST_FLOAT> || std::is_same_v) { for (cl_uint i = 0; i < threadCount; i++) { @@ -2910,7 +2913,7 @@ public: { if (std::is_same_v< HostDataType, - HOST_HALF> || std::is_same::value) + HOST_HALF> || std::is_same_v || std::is_same_v) { if (whichDestValue == 0) return CBasicTestMemOrderScope:: @@ -2962,6 +2965,18 @@ public: == 0) return 0; } + else if constexpr (std::is_same_v) + { + if (LocalMemory() + && (gDoubleAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_MIN_MAX_EXT) + == 0) + return 0; // skip test - not applicable + + if (!LocalMemory() + && (gDoubleAtomicCaps & CL_DEVICE_GLOBAL_FP_ATOMIC_MIN_MAX_EXT) + == 0) + return 0; + } else if constexpr (std::is_same_v) { if (LocalMemory() @@ -2983,7 +2998,7 @@ public: if constexpr ( std::is_same_v< HostDataType, - HOST_HALF> || std::is_same_v) + HOST_HALF> || std::is_same_v || std::is_same_v) { return threadCount; } @@ -3018,6 +3033,11 @@ static int test_atomic_fetch_min_generic(cl_device_id deviceID, if (gFloatAtomicsSupported) { + CBasicTestFetchMin test_double( + TYPE_ATOMIC_DOUBLE, useSVM); + EXECUTE_TEST( + error, test_double.Execute(deviceID, context, queue, num_elements)); + CBasicTestFetchMin test_half( TYPE_ATOMIC_HALF, useSVM); EXECUTE_TEST(error, @@ -3130,7 +3150,7 @@ public: if constexpr ( std::is_same_v< HostDataType, - HOST_HALF> || std::is_same_v) + HOST_HALF> || std::is_same_v || std::is_same_v) { return " atomic_fetch_max" + postfix + "(&destMemory[0], oldValues[tid] " + memoryOrderScope + ");\n" @@ -3152,7 +3172,7 @@ public: if constexpr ( std::is_same_v< HostDataType, - HOST_HALF> || std::is_same_v) + HOST_HALF> || std::is_same_v || std::is_same_v) { host_atomic_fetch_max(&destMemory[0], oldValues[tid], MemoryOrder()); @@ -3177,7 +3197,10 @@ public: gHalfRoundingMode); } } - else if constexpr (std::is_same_v) + else if constexpr ( + std::is_same_v< + HostDataType, + HOST_FLOAT> || std::is_same_v) { for (cl_uint i = 0; i < threadCount; i++) { @@ -3238,7 +3261,7 @@ public: { if (std::is_same_v< HostDataType, - HOST_HALF> || std::is_same::value) + HOST_HALF> || std::is_same_v || std::is_same_v) { if (whichDestValue == 0) return CBasicTestMemOrderScope:: @@ -3255,7 +3278,7 @@ public: { if (std::is_same_v< HostDataType, - HOST_HALF> || std::is_same::value) + HOST_HALF> || std::is_same_v || std::is_same_v) { correct = true; for (cl_uint i = 1; i < threadCount; i++) @@ -3290,6 +3313,18 @@ public: == 0) return 0; } + else if constexpr (std::is_same_v) + { + if (LocalMemory() + && (gDoubleAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_MIN_MAX_EXT) + == 0) + return 0; // skip test - not applicable + + if (!LocalMemory() + && (gDoubleAtomicCaps & CL_DEVICE_GLOBAL_FP_ATOMIC_MIN_MAX_EXT) + == 0) + return 0; + } else if constexpr (std::is_same_v) { if (LocalMemory() @@ -3311,7 +3346,7 @@ public: if constexpr ( std::is_same_v< HostDataType, - HOST_HALF> || std::is_same_v) + HOST_HALF> || std::is_same_v || std::is_same_v) { return threadCount; } @@ -3346,6 +3381,11 @@ static int test_atomic_fetch_max_generic(cl_device_id deviceID, if (gFloatAtomicsSupported) { + CBasicTestFetchMax test_double( + TYPE_ATOMIC_DOUBLE, useSVM); + EXECUTE_TEST( + error, test_double.Execute(deviceID, context, queue, num_elements)); + CBasicTestFetchMax test_half( TYPE_ATOMIC_HALF, useSVM); EXECUTE_TEST(error, From d0aa95be2fa63f88a08754a22254ef4e4d07d926 Mon Sep 17 00:00:00 2001 From: Marcin Hajder Date: Tue, 28 Oct 2025 16:41:24 +0100 Subject: [PATCH 22/33] Added support for cl_ext_float_atomics in CBasicTestFetchSub with atomic_float (#2367) Related to #2142, according to the work plan, extending CBasicTestFetchSub with support for atomic_float. --- test_conformance/c11_atomics/host_atomics.h | 11 +- test_conformance/c11_atomics/main.cpp | 1 + test_conformance/c11_atomics/test_atomics.cpp | 106 ++++++++++++++++-- 3 files changed, 106 insertions(+), 12 deletions(-) diff --git a/test_conformance/c11_atomics/host_atomics.h b/test_conformance/c11_atomics/host_atomics.h index a0588ef4..9a33f26c 100644 --- a/test_conformance/c11_atomics/host_atomics.h +++ b/test_conformance/c11_atomics/host_atomics.h @@ -18,7 +18,6 @@ #include "harness/testHarness.h" #include - #include "CL/cl_half.h" #ifdef WIN32 @@ -136,7 +135,15 @@ template CorrespondingType host_atomic_fetch_sub(volatile AtomicType *a, CorrespondingType c, TExplicitMemoryOrderType order) { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + static std::mutex mx; + std::lock_guard lock(mx); + CorrespondingType old_value = *a; + *a -= c; + return old_value; + } + else if constexpr (std::is_same_v) { static std::mutex mx; std::lock_guard lock(mx); diff --git a/test_conformance/c11_atomics/main.cpp b/test_conformance/c11_atomics/main.cpp index 78291f06..e2f1888f 100644 --- a/test_conformance/c11_atomics/main.cpp +++ b/test_conformance/c11_atomics/main.cpp @@ -134,6 +134,7 @@ test_status InitCL(cl_device_id device) { if (is_extension_available(device, "cl_ext_float_atomics")) { gFloatAtomicsSupported = true; + if (is_extension_available(device, "cl_khr_fp64")) { cl_int error = clGetDeviceInfo( diff --git a/test_conformance/c11_atomics/test_atomics.cpp b/test_conformance/c11_atomics/test_atomics.cpp index 5a553a06..f46520ca 100644 --- a/test_conformance/c11_atomics/test_atomics.cpp +++ b/test_conformance/c11_atomics/test_atomics.cpp @@ -1668,7 +1668,10 @@ public: useSVM), min_range(-999.0), max_range(999.0), max_error(0.0) { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_FLOAT> || std::is_same_v) { StartValue(0); CBasicTestMemOrderScope + HostDataType subtract(Iterator begin, Iterator end) + { + HostDataType res = 0; + for (auto it = begin; it != end; ++it) res = res - *it; + return res; + } + template float subtract_halfs(Iterator begin, Iterator end) { cl_half res = 0; @@ -1690,12 +1700,53 @@ public: bool GenerateRefs(cl_uint threadCount, HostDataType *startRefValues, MTdata d) override { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { if (threadCount > ref_vals.size()) { ref_vals.resize(threadCount); + for (cl_uint i = 0; i < threadCount; i++) + ref_vals[i] = get_random_float(min_range, max_range, d); + memcpy(startRefValues, ref_vals.data(), + sizeof(HostDataType) * ref_vals.size()); + + // Estimate highest possible subtraction error for given set. + std::vector sums; + std::sort(ref_vals.begin(), ref_vals.end()); + sums.push_back(subtract(ref_vals.begin(), ref_vals.end())); + sums.push_back(subtract(ref_vals.rbegin(), ref_vals.rend())); + + std::sort( + ref_vals.begin(), ref_vals.end(), + [](float a, float b) { return std::abs(a) < std::abs(b); }); + + double precise = 0.0; + for (auto elem : ref_vals) precise += double(elem); + sums.push_back(precise); + sums.push_back(subtract(ref_vals.begin(), ref_vals.end())); + sums.push_back(subtract(ref_vals.rbegin(), ref_vals.rend())); + + std::sort(sums.begin(), sums.end()); + max_error = + std::abs((HOST_ATOMIC_FLOAT)sums.front() - sums.back()); + + // restore unsorted order + memcpy(ref_vals.data(), startRefValues, + sizeof(HostDataType) * ref_vals.size()); + } + else + { + memcpy(startRefValues, ref_vals.data(), + sizeof(HostDataType) * threadCount); + } + return true; + } + if constexpr (std::is_same_v) + { + if (threadCount > ref_vals.size()) + { + ref_vals.resize(threadCount); for (cl_uint i = 0; i < threadCount; i++) ref_vals[i] = cl_half_from_float( get_random_float(min_range, max_range, d), @@ -1725,7 +1776,6 @@ public: float precise = 0.f; for (auto elem : ref_vals) precise -= cl_half_to_float(elem); sums.push_back(precise); - sums.push_back( subtract_halfs(ref_vals.begin(), ref_vals.end())); sums.push_back( @@ -1733,7 +1783,6 @@ public: std::sort(sums.begin(), sums.end()); max_error = std::abs(sums.front() - sums.back()); - // restore unsorted order memcpy(ref_vals.data(), startRefValues, sizeof(HostDataType) * ref_vals.size()); @@ -1752,7 +1801,10 @@ public: std::string memoryOrderScope = MemoryOrderScopeStr(); std::string postfix(memoryOrderScope.empty() ? "" : "_explicit"); - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { return " atomic_fetch_sub" + postfix + "(&destMemory[0], (" + DataType().AddSubOperandTypeName() + ")oldValues[tid]" @@ -1774,7 +1826,10 @@ public: volatile HostAtomicType *destMemory, HostDataType *oldValues) override { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { host_atomic_fetch_sub(&destMemory[0], (HostDataType)oldValues[tid], MemoryOrder()); @@ -1796,7 +1851,13 @@ public: cl_uint whichDestValue) override { expected = StartValue(); - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (whichDestValue == 0) + for (cl_uint i = 0; i < threadCount; i++) + expected -= startRefValues[i]; + } + else if constexpr (std::is_same_v) { if (whichDestValue == 0) { @@ -1821,7 +1882,14 @@ public: const std::vector &testValues, cl_uint whichDestValue) override { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (whichDestValue == 0) + return std::abs((HOST_ATOMIC_FLOAT)expected + - testValues[whichDestValue]) + > max_error; + } + else if constexpr (std::is_same_v) { if (whichDestValue == 0) return std::abs(cl_half_to_float(expected) @@ -1859,7 +1927,17 @@ public: int ExecuteSingleTest(cl_device_id deviceID, cl_context context, cl_command_queue queue) override { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (LocalMemory() + && (gFloatAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_ADD_EXT) == 0) + return 0; // skip test - not applicable + + if (!LocalMemory() + && (gFloatAtomicCaps & CL_DEVICE_GLOBAL_FP_ATOMIC_ADD_EXT) == 0) + return 0; + } + else if constexpr (std::is_same_v) { if (LocalMemory() && (gHalfAtomicCaps & CL_DEVICE_LOCAL_FP_ATOMIC_ADD_EXT) == 0) @@ -1875,7 +1953,10 @@ public: } cl_uint NumResults(cl_uint threadCount, cl_device_id deviceID) override { - if constexpr (std::is_same_v) + if constexpr ( + std::is_same_v< + HostDataType, + HOST_HALF> || std::is_same_v) { return threadCount; } @@ -1910,6 +1991,11 @@ static int test_atomic_fetch_sub_generic(cl_device_id deviceID, if (gFloatAtomicsSupported) { + CBasicTestFetchSub test_float( + TYPE_ATOMIC_FLOAT, useSVM); + EXECUTE_TEST( + error, test_float.Execute(deviceID, context, queue, num_elements)); + CBasicTestFetchSub test_half( TYPE_ATOMIC_HALF, useSVM); EXECUTE_TEST(error, From 74ebc6729408c46628a03f9cd6f72b6eb9ebb055 Mon Sep 17 00:00:00 2001 From: Sreelakshmi Haridas Maruthur Date: Tue, 28 Oct 2025 09:49:14 -0600 Subject: [PATCH 23/33] Fix channel count for RGBx (#2493) RGBx is defined as a 4 channel format where the 4th channel is ignored (5.3.1.1) --- test_common/harness/imageHelpers.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test_common/harness/imageHelpers.cpp b/test_common/harness/imageHelpers.cpp index 6a990104..0f8f1a58 100644 --- a/test_common/harness/imageHelpers.cpp +++ b/test_common/harness/imageHelpers.cpp @@ -137,10 +137,10 @@ uint32_t get_channel_order_channel_count(cl_channel_order order) case CL_RGx: return 2; case CL_RGB: - case CL_RGBx: - case CL_sRGB: - case CL_sRGBx: return 3; + case CL_sRGB: return 3; + case CL_RGBx: + case CL_sRGBx: case CL_RGBA: case CL_ARGB: case CL_BGRA: From 1a4862e84e4add3a88574d4461578da8fb333f32 Mon Sep 17 00:00:00 2001 From: Ahmed <36049290+AhmedAmraniAkdi@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:50:08 +0000 Subject: [PATCH 24/33] Use a more genuine kernel for the command buffer device enqueue negative test (#2532) The current one has no arguments and does not enqueue any work. --- .../negative_command_nd_range_kernel.cpp | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/test_conformance/extensions/cl_khr_command_buffer/negative_command_nd_range_kernel.cpp b/test_conformance/extensions/cl_khr_command_buffer/negative_command_nd_range_kernel.cpp index d3ba72b4..c6e2262e 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/negative_command_nd_range_kernel.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/negative_command_nd_range_kernel.cpp @@ -398,16 +398,17 @@ struct CommandNDRangeKernelWithKernelEnqueueCall : public BasicCommandBufferTest const char* kernel_str = R"( -__kernel void enqueue_call_func() { - } - -__kernel void enqueue_call_kernel() { -queue_t def_q = get_default_queue(); -ndrange_t ndrange = ndrange_1D(1); -enqueue_kernel(def_q, CLK_ENQUEUE_FLAGS_WAIT_KERNEL, ndrange, - ^{enqueue_call_func();}); - } -)"; + __kernel void enqueue_call_func(__global int* out_mem) { + out_mem[get_global_id(0)] = 0x1234; + } + + __kernel void enqueue_call_kernel(__global int* out_mem) { + queue_t def_q = get_default_queue(); + ndrange_t ndrange = ndrange_1D(1); + enqueue_kernel(def_q, CLK_ENQUEUE_FLAGS_WAIT_KERNEL, ndrange, + ^{enqueue_call_func(out_mem);}); + } + )"; std::string build_options = std::string(" ") + cl_std; error = create_single_kernel_helper(context, &program, &kernel, 1, @@ -443,7 +444,17 @@ enqueue_kernel(def_q, CLK_ENQUEUE_FLAGS_WAIT_KERNEL, ndrange, cl_int Run() override { - cl_int error = clCommandNDRangeKernelKHR( + cl_int error = CL_SUCCESS; + + clMemWrapper out_mem = + clCreateBuffer(context, CL_MEM_WRITE_ONLY, + num_elements * sizeof(cl_int), nullptr, &error); + test_error(error, "clCreateBuffer failed"); + + error = clSetKernelArg(kernel, 0, sizeof(cl_mem), &out_mem); + test_error(error, "clSetKernelArg failed"); + + error = clCommandNDRangeKernelKHR( command_buffer, nullptr, nullptr, kernel, 1, nullptr, &num_elements, nullptr, 0, nullptr, nullptr, nullptr); From 39f961b71f2284ea05827c174a02ebefb1ac4b94 Mon Sep 17 00:00:00 2001 From: Daniel Crawley Date: Tue, 28 Oct 2025 11:53:54 -0400 Subject: [PATCH 25/33] Match spv and api version for Vulkan test (#2536) - Vulkan api version is 1_1, so target env for shaders should be vulkan1.1 --- test_conformance/vulkan/shaders/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_conformance/vulkan/shaders/CMakeLists.txt b/test_conformance/vulkan/shaders/CMakeLists.txt index 3614e1e2..b5a6b46b 100644 --- a/test_conformance/vulkan/shaders/CMakeLists.txt +++ b/test_conformance/vulkan/shaders/CMakeLists.txt @@ -17,7 +17,7 @@ else() add_custom_command( OUTPUT ${VULKAN_TEST_RESOURCES}/buffer.spv COMMAND ${Vulkan_glslang_binary} - --target-env vulkan1.0 + --target-env vulkan1.1 -o ${VULKAN_TEST_RESOURCES}/buffer.spv ${CMAKE_CURRENT_SOURCE_DIR}/buffer.comp DEPENDS buffer.comp @@ -35,7 +35,7 @@ else() add_custom_command( OUTPUT ${VULKAN_TEST_RESOURCES}/image2D_${GLSL_FORMAT}.spv COMMAND ${Vulkan_glslang_binary} - --target-env vulkan1.0 + --target-env vulkan1.1 -o ${VULKAN_TEST_RESOURCES}/image2D_${GLSL_FORMAT}.spv ${CMAKE_CURRENT_BINARY_DIR}/image2D_${GLSL_FORMAT}.comp DEPENDS image2D_${GLSL_FORMAT}.comp From 16cd0afe4fed7d5b9f2877c92cde858459a701f4 Mon Sep 17 00:00:00 2001 From: Jose Lopez Date: Tue, 28 Oct 2025 15:54:41 +0000 Subject: [PATCH 26/33] Add case for clSetKernelExecInfo with empty set (#2551) Adds a special case to ensure that calling `clSetKernelExecInfo` with `CL_KERNEL_EXEC_INFO_SVM_PTRS` and an empty set of pointers is valid --- .../test_set_kernel_exec_info_svm_ptrs.cpp | 267 ++++++++++-------- 1 file changed, 146 insertions(+), 121 deletions(-) diff --git a/test_conformance/SVM/test_set_kernel_exec_info_svm_ptrs.cpp b/test_conformance/SVM/test_set_kernel_exec_info_svm_ptrs.cpp index 13e4b20f..b5fbc4fa 100644 --- a/test_conformance/SVM/test_set_kernel_exec_info_svm_ptrs.cpp +++ b/test_conformance/SVM/test_set_kernel_exec_info_svm_ptrs.cpp @@ -1,6 +1,6 @@ // // Copyright (c) 2017 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 @@ -15,142 +15,167 @@ // #include "common.h" -typedef struct { - cl_int *pA; - cl_int *pB; - cl_int *pC; +typedef struct +{ + cl_int* pA; + cl_int* pB; + cl_int* pC; } BufPtrs; -const char *set_kernel_exec_info_svm_ptrs_kernel[] = { - "struct BufPtrs;\n" - "\n" - "typedef struct {\n" - " __global int *pA;\n" - " __global int *pB;\n" - " __global int *pC;\n" - "} BufPtrs;\n" - "\n" - "__kernel void set_kernel_exec_info_test(__global BufPtrs* pBufs)\n" - "{\n" - " size_t i;\n" - " i = get_global_id(0);\n" - " pBufs->pA[i]++;\n" - " pBufs->pB[i]++;\n" - " pBufs->pC[i]++;\n" - "}\n" +const char* set_kernel_exec_info_svm_ptrs_kernel[] = { + "struct BufPtrs;\n" + "\n" + "typedef struct {\n" + " __global int *pA;\n" + " __global int *pB;\n" + " __global int *pC;\n" + "} BufPtrs;\n" + "\n" + "__kernel void set_kernel_exec_info_test(__global BufPtrs* pBufs)\n" + "{\n" + " size_t i;\n" + " i = get_global_id(0);\n" + " pBufs->pA[i]++;\n" + " pBufs->pB[i]++;\n" + " pBufs->pC[i]++;\n" + "}\n" }; -// Test that clSetKernelExecInfo works correctly with CL_KERNEL_EXEC_INFO_SVM_PTRS flag. +// Test that clSetKernelExecInfo works correctly with +// CL_KERNEL_EXEC_INFO_SVM_PTRS flag. // REGISTER_TEST(svm_set_kernel_exec_info_svm_ptrs) { - clContextWrapper c = NULL; - clProgramWrapper program = NULL; - cl_uint num_devices = 0; - cl_int error = CL_SUCCESS; - clCommandQueueWrapper queues[MAXQ]; + clContextWrapper c = NULL; + clProgramWrapper program = NULL; + cl_uint num_devices = 0; + cl_int error = CL_SUCCESS; + clCommandQueueWrapper queues[MAXQ]; - // error = create_cl_objects(device, &set_kernel_exec_info_svm_ptrs_kernel[0], - // &context, &program, &q, &num_devices, CL_DEVICE_SVM_FINE_GRAIN); - error = create_cl_objects(device, &set_kernel_exec_info_svm_ptrs_kernel[0], - &c, &program, &queues[0], &num_devices, - CL_DEVICE_SVM_COARSE_GRAIN_BUFFER); - if(error == 1) return 0; // no devices capable of requested SVM level, so don't execute but count test as passing. - if(error < 0) return -1; // fail test. + // error = create_cl_objects(device, + // &set_kernel_exec_info_svm_ptrs_kernel[0], &context, &program, &q, + // &num_devices, CL_DEVICE_SVM_FINE_GRAIN); + error = create_cl_objects(device, &set_kernel_exec_info_svm_ptrs_kernel[0], + &c, &program, &queues[0], &num_devices, + CL_DEVICE_SVM_COARSE_GRAIN_BUFFER); + if (error == 1) + return 0; // no devices capable of requested SVM level, so don't execute + // but count test as passing. + if (error < 0) return -1; // fail test. - clKernelWrapper k = clCreateKernel(program, "set_kernel_exec_info_test", &error); - test_error(error, "clCreateKernel failed"); + clKernelWrapper k = + clCreateKernel(program, "set_kernel_exec_info_test", &error); + test_error(error, "clCreateKernel failed"); - size_t size = num_elements*sizeof(int); - //int* pA = (int*) clSVMalloc(c, CL_MEM_READ_WRITE | CL_DEVICE_SVM_FINE_GRAIN_SYSTEM, sizeof(int)*num_elements, 0); - //int* pB = (int*) clSVMalloc(c, CL_MEM_READ_WRITE | CL_DEVICE_SVM_FINE_GRAIN_SYSTEM, sizeof(int)*num_elements, 0); - //int* pC = (int*) clSVMalloc(c, CL_MEM_READ_WRITE | CL_DEVICE_SVM_FINE_GRAIN_SYSTEM, sizeof(int)*num_elements, 0); - int* pA = (int*) clSVMAlloc(c, CL_MEM_READ_WRITE, size, 0); - int* pB = (int*) clSVMAlloc(c, CL_MEM_READ_WRITE, size, 0); - int* pC = (int*) clSVMAlloc(c, CL_MEM_READ_WRITE, size, 0); - BufPtrs* pBuf = (BufPtrs*) clSVMAlloc(c, CL_MEM_READ_WRITE, sizeof(BufPtrs), 0); + size_t size = num_elements * sizeof(int); + // int* pA = (int*) clSVMalloc(c, CL_MEM_READ_WRITE | + // CL_DEVICE_SVM_FINE_GRAIN_SYSTEM, sizeof(int)*num_elements, 0); int* pB = + // (int*) clSVMalloc(c, CL_MEM_READ_WRITE | CL_DEVICE_SVM_FINE_GRAIN_SYSTEM, + // sizeof(int)*num_elements, 0); int* pC = (int*) clSVMalloc(c, + // CL_MEM_READ_WRITE | CL_DEVICE_SVM_FINE_GRAIN_SYSTEM, + // sizeof(int)*num_elements, 0); + int* pA = (int*)clSVMAlloc(c, CL_MEM_READ_WRITE, size, 0); + int* pB = (int*)clSVMAlloc(c, CL_MEM_READ_WRITE, size, 0); + int* pC = (int*)clSVMAlloc(c, CL_MEM_READ_WRITE, size, 0); + BufPtrs* pBuf = + (BufPtrs*)clSVMAlloc(c, CL_MEM_READ_WRITE, sizeof(BufPtrs), 0); - bool failed = false; - { - clMemWrapper ba,bb,bc,bBuf; - ba = clCreateBuffer(c, CL_MEM_USE_HOST_PTR, size, pA, &error); - test_error(error, "clCreateBuffer failed"); - bb = clCreateBuffer(c, CL_MEM_USE_HOST_PTR, size, pB, &error); - test_error(error, "clCreateBuffer failed"); - bc = clCreateBuffer(c, CL_MEM_USE_HOST_PTR, size, pC, &error); - test_error(error, "clCreateBuffer failed"); - bBuf = clCreateBuffer(c, CL_MEM_USE_HOST_PTR, sizeof(BufPtrs), pBuf, &error); - test_error(error, "clCreateBuffer failed"); - - clEnqueueMapBuffer(queues[0], ba, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, size, 0, NULL, NULL, &error); - test_error(error, "clEnqueueMapBuffer failed"); - clEnqueueMapBuffer(queues[0], bb, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, size, 0, NULL, NULL, &error); - test_error(error, "clEnqueueMapBuffer failed"); - clEnqueueMapBuffer(queues[0], bc, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, size, 0, NULL, NULL, &error); - test_error(error, "clEnqueueMapBuffer failed"); - clEnqueueMapBuffer(queues[0], bBuf, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, sizeof(BufPtrs), 0, NULL, NULL, &error); - test_error(error, "clEnqueueMapBuffer failed"); - - for(int i = 0; i < num_elements; i++) pA[i] = pB[i] = pC[i] = 0; - - pBuf->pA = pA; - pBuf->pB = pB; - pBuf->pC = pC; - - error = clEnqueueUnmapMemObject(queues[0], ba, pA, 0, NULL, NULL); - test_error(error, " clEnqueueUnmapMemObject failed."); - error = clEnqueueUnmapMemObject(queues[0], bb, pB, 0, NULL, NULL); - test_error(error, " clEnqueueUnmapMemObject failed."); - error = clEnqueueUnmapMemObject(queues[0], bc, pC, 0, NULL, NULL); - test_error(error, " clEnqueueUnmapMemObject failed."); - error = clEnqueueUnmapMemObject(queues[0], bBuf, pBuf, 0, NULL, NULL); - test_error(error, " clEnqueueUnmapMemObject failed."); - - - error = clSetKernelArgSVMPointer(k, 0, pBuf); - test_error(error, "clSetKernelArg failed"); - - error = clSetKernelExecInfo(k, CL_KERNEL_EXEC_INFO_SVM_PTRS, sizeof(BufPtrs), pBuf); - test_error(error, "clSetKernelExecInfo failed"); - - size_t range = num_elements; - error = clEnqueueNDRangeKernel(queues[0], k, 1, NULL, &range, NULL, 0, NULL, NULL); - test_error(error,"clEnqueueNDRangeKernel failed"); - - error = clFinish(queues[0]); - test_error(error, "clFinish failed."); - - clEnqueueMapBuffer(queues[0], ba, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, size, 0, NULL, NULL, &error); - test_error(error, "clEnqueueMapBuffer failed"); - clEnqueueMapBuffer(queues[0], bb, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, size, 0, NULL, NULL, &error); - test_error(error, "clEnqueueMapBuffer failed"); - clEnqueueMapBuffer(queues[0], bc, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, size, 0, NULL, NULL, &error); - test_error(error, "clEnqueueMapBuffer failed"); - - for(int i = 0; i < num_elements; i++) + bool failed = false; { - if(pA[i] + pB[i] + pC[i] != 3) - failed = true; + clMemWrapper ba, bb, bc, bBuf; + ba = clCreateBuffer(c, CL_MEM_USE_HOST_PTR, size, pA, &error); + test_error(error, "clCreateBuffer failed"); + bb = clCreateBuffer(c, CL_MEM_USE_HOST_PTR, size, pB, &error); + test_error(error, "clCreateBuffer failed"); + bc = clCreateBuffer(c, CL_MEM_USE_HOST_PTR, size, pC, &error); + test_error(error, "clCreateBuffer failed"); + bBuf = clCreateBuffer(c, CL_MEM_USE_HOST_PTR, sizeof(BufPtrs), pBuf, + &error); + test_error(error, "clCreateBuffer failed"); + + clEnqueueMapBuffer(queues[0], ba, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, + 0, size, 0, NULL, NULL, &error); + test_error(error, "clEnqueueMapBuffer failed"); + clEnqueueMapBuffer(queues[0], bb, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, + 0, size, 0, NULL, NULL, &error); + test_error(error, "clEnqueueMapBuffer failed"); + clEnqueueMapBuffer(queues[0], bc, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, + 0, size, 0, NULL, NULL, &error); + test_error(error, "clEnqueueMapBuffer failed"); + clEnqueueMapBuffer(queues[0], bBuf, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, + 0, sizeof(BufPtrs), 0, NULL, NULL, &error); + test_error(error, "clEnqueueMapBuffer failed"); + + for (int i = 0; i < num_elements; i++) pA[i] = pB[i] = pC[i] = 0; + + pBuf->pA = pA; + pBuf->pB = pB; + pBuf->pC = pC; + + error = clEnqueueUnmapMemObject(queues[0], ba, pA, 0, NULL, NULL); + test_error(error, " clEnqueueUnmapMemObject failed."); + error = clEnqueueUnmapMemObject(queues[0], bb, pB, 0, NULL, NULL); + test_error(error, " clEnqueueUnmapMemObject failed."); + error = clEnqueueUnmapMemObject(queues[0], bc, pC, 0, NULL, NULL); + test_error(error, " clEnqueueUnmapMemObject failed."); + error = clEnqueueUnmapMemObject(queues[0], bBuf, pBuf, 0, NULL, NULL); + test_error(error, " clEnqueueUnmapMemObject failed."); + + + error = clSetKernelArgSVMPointer(k, 0, pBuf); + test_error(error, "clSetKernelArg failed"); + + error = clSetKernelExecInfo(k, CL_KERNEL_EXEC_INFO_SVM_PTRS, + sizeof(BufPtrs), pBuf); + test_error(error, "clSetKernelExecInfo failed"); + + size_t range = num_elements; + error = clEnqueueNDRangeKernel(queues[0], k, 1, NULL, &range, NULL, 0, + NULL, NULL); + test_error(error, "clEnqueueNDRangeKernel failed"); + + error = clFinish(queues[0]); + test_error(error, "clFinish failed."); + + // Special case testing of unsetting previously set SVM pointers + error = clSetKernelExecInfo(k, CL_KERNEL_EXEC_INFO_SVM_PTRS, 0, NULL); + test_error(error, + "Unsetting previously set SVM pointers using " + "clSetKernelExecInfo failed"); + + clEnqueueMapBuffer(queues[0], ba, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, + 0, size, 0, NULL, NULL, &error); + test_error(error, "clEnqueueMapBuffer failed"); + clEnqueueMapBuffer(queues[0], bb, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, + 0, size, 0, NULL, NULL, &error); + test_error(error, "clEnqueueMapBuffer failed"); + clEnqueueMapBuffer(queues[0], bc, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, + 0, size, 0, NULL, NULL, &error); + test_error(error, "clEnqueueMapBuffer failed"); + + for (int i = 0; i < num_elements; i++) + { + if (pA[i] + pB[i] + pC[i] != 3) failed = true; + } + + error = clEnqueueUnmapMemObject(queues[0], ba, pA, 0, NULL, NULL); + test_error(error, " clEnqueueUnmapMemObject failed."); + error = clEnqueueUnmapMemObject(queues[0], bb, pB, 0, NULL, NULL); + test_error(error, " clEnqueueUnmapMemObject failed."); + error = clEnqueueUnmapMemObject(queues[0], bc, pC, 0, NULL, NULL); + test_error(error, " clEnqueueUnmapMemObject failed."); } - error = clEnqueueUnmapMemObject(queues[0], ba, pA, 0, NULL, NULL); - test_error(error, " clEnqueueUnmapMemObject failed."); - error = clEnqueueUnmapMemObject(queues[0], bb, pB, 0, NULL, NULL); - test_error(error, " clEnqueueUnmapMemObject failed."); - error = clEnqueueUnmapMemObject(queues[0], bc, pC, 0, NULL, NULL); - test_error(error, " clEnqueueUnmapMemObject failed."); - } + error = clFinish(queues[0]); + test_error(error, " clFinish failed."); - error = clFinish(queues[0]); - test_error(error, " clFinish failed."); + clSVMFree(c, pA); + clSVMFree(c, pB); + clSVMFree(c, pC); + clSVMFree(c, pBuf); - clSVMFree(c, pA); - clSVMFree(c, pB); - clSVMFree(c, pC); - clSVMFree(c, pBuf); + if (failed) return -1; - if(failed) return -1; - - return 0; + return 0; } From 580fde7501cf79c0dc190e9c9de1b5abeefa52f8 Mon Sep 17 00:00:00 2001 From: Jose Lopez Date: Tue, 28 Oct 2025 15:55:41 +0000 Subject: [PATCH 27/33] Replace unique_ptr for vectors in atomics_indexed_cases (#2557) Use vectors instead of unique_ptr for buffers data --- .../atomics/test_indexed_cases.cpp | 53 ++++--------------- 1 file changed, 11 insertions(+), 42 deletions(-) diff --git a/test_conformance/atomics/test_indexed_cases.cpp b/test_conformance/atomics/test_indexed_cases.cpp index af2d4a9d..68de27ea 100644 --- a/test_conformance/atomics/test_indexed_cases.cpp +++ b/test_conformance/atomics/test_indexed_cases.cpp @@ -255,18 +255,12 @@ int add_index_bin_test(size_t *global_threads, cl_command_queue queue, } // Initialize our storage - std::unique_ptr l_bin_counts(new cl_int[number_of_bins]); - if (!l_bin_counts) - { - log_error("add_index_bin_test FAILED to allocate initial values for " - "bin_counters.\n"); - return -1; - } + std::vector l_bin_counts(number_of_bins); int i; for (i = 0; i < number_of_bins; i++) l_bin_counts[i] = 0; err = clEnqueueWriteBuffer(queue, bin_counters, true, 0, sizeof(cl_int) * number_of_bins, - l_bin_counts.get(), 0, NULL, NULL); + l_bin_counts.data(), 0, NULL, NULL); if (err) { log_error("add_index_bin_test FAILED to set initial values for " @@ -275,19 +269,12 @@ int add_index_bin_test(size_t *global_threads, cl_command_queue queue, return -1; } - std::unique_ptr values( - new cl_int[number_of_bins * max_counts_per_bin]); - if (!values) - { - log_error( - "add_index_bin_test FAILED to allocate initial values for bins.\n"); - return -1; - } + std::vector values(number_of_bins * max_counts_per_bin); for (i = 0; i < number_of_bins * max_counts_per_bin; i++) values[i] = -1; err = clEnqueueWriteBuffer(queue, bins, true, 0, sizeof(cl_int) * number_of_bins * max_counts_per_bin, - values.get(), 0, NULL, NULL); + values.data(), 0, NULL, NULL); if (err) { log_error( @@ -296,13 +283,7 @@ int add_index_bin_test(size_t *global_threads, cl_command_queue queue, return -1; } - std::unique_ptr l_bin_assignments(new cl_int[number_of_items]); - if (!l_bin_assignments) - { - log_error("add_index_bin_test FAILED to allocate initial values for " - "l_bin_assignments.\n"); - return -1; - } + std::vector l_bin_assignments(number_of_items); for (i = 0; i < number_of_items; i++) { int bin = random_in_range(0, number_of_bins - 1, d); @@ -326,7 +307,7 @@ int add_index_bin_test(size_t *global_threads, cl_command_queue queue, } err = clEnqueueWriteBuffer(queue, bin_assignments, true, 0, sizeof(cl_int) * number_of_items, - l_bin_assignments.get(), 0, NULL, NULL); + l_bin_assignments.data(), 0, NULL, NULL); if (err) { log_error("add_index_bin_test FAILED to set initial values for " @@ -355,34 +336,22 @@ int add_index_bin_test(size_t *global_threads, cl_command_queue queue, return -1; } - std::unique_ptr final_bin_assignments( - new cl_int[number_of_bins * max_counts_per_bin]); - if (!final_bin_assignments) - { - log_error("add_index_bin_test FAILED to allocate initial values for " - "final_bin_assignments.\n"); - return -1; - } + std::vector final_bin_assignments(number_of_bins + * max_counts_per_bin); err = clEnqueueReadBuffer(queue, bins, true, 0, sizeof(cl_int) * number_of_bins * max_counts_per_bin, - final_bin_assignments.get(), 0, NULL, NULL); + final_bin_assignments.data(), 0, NULL, NULL); if (err) { log_error("add_index_bin_test FAILED to read back bins: %d\n", err); return -1; } - std::unique_ptr final_bin_counts(new cl_int[number_of_bins]); - if (!final_bin_counts) - { - log_error("add_index_bin_test FAILED to allocate initial values for " - "final_bin_counts.\n"); - return -1; - } + std::vector final_bin_counts(number_of_bins); err = clEnqueueReadBuffer(queue, bin_counters, true, 0, sizeof(cl_int) * number_of_bins, - final_bin_counts.get(), 0, NULL, NULL); + final_bin_counts.data(), 0, NULL, NULL); if (err) { log_error("add_index_bin_test FAILED to read back bin_counters: %d\n", From d63cc8ce5d76cb6c09e69b774f8b96e5e1c9275f Mon Sep 17 00:00:00 2001 From: Sergiu Bogdan Popescu <37843379+sergiupopescu22@users.noreply.github.com> Date: Tue, 28 Oct 2025 17:01:42 +0100 Subject: [PATCH 28/33] Modified kernel code to correspond to the Image declared format (CL_UNSIGNED_INT8) (#2512) **For mutable_dispatch_image_1d_arguments & mutable_dispatch_image_2d_arguments:** As the images are created using CL_UNSIGNED_INT8, the kernel does not use correct instructions, as they are designed for signed variable. This fix consists of modifying the kernel code to use unsigned instructions and auxiliary variables . --- .../mutable_command_image_arguments.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_image_arguments.cpp b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_image_arguments.cpp index 6fe31948..e287375b 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_image_arguments.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/cl_khr_command_buffer_mutable_dispatch/mutable_command_image_arguments.cpp @@ -70,9 +70,9 @@ struct MutableDispatchImage1DArguments : public BasicMutableCommandBufferTest { int offset = get_global_id(0); - int4 color = read_imagei( source, sampler, offset ); + uint4 color = read_imageui( source, sampler, offset ); - write_imagei( dest, offset, color ); + write_imageui( dest, offset, color ); })"; cl_int error; @@ -260,9 +260,9 @@ struct MutableDispatchImage2DArguments : public BasicMutableCommandBufferTest int x = get_global_id(0); int y = get_global_id(1); - int4 color = read_imagei( source, sampler, (int2) (x, y) ); + uint4 color = read_imageui( source, sampler, (int2) (x, y) ); - write_imagei( dest, (int2) (x, y), color ); + write_imageui( dest, (int2) (x, y), color ); })"; cl_int error; From c6e0f416e7aafeef473839e4a24517fe0e5909dc Mon Sep 17 00:00:00 2001 From: Ahmed <36049290+AhmedAmraniAkdi@users.noreply.github.com> Date: Tue, 28 Oct 2025 16:04:51 +0000 Subject: [PATCH 29/33] Specify memory scope and memory order for the atomic operations in generic_address_space generic_atomics_variant generic_atomics_invariant (#2550) Use the explicit version of the atomic_load/store and atomic_fetch_add with memory order relaxed and memory scope workgroup to allow devices that only support the minimum CL_DEVICE_ATOMIC_MEMORY_CAPABILITIES which are (CL_DEVICE_ATOMIC_ORDER_RELAXED | CL_DEVICE_ATOMIC_SCOPE_WORK_GROUP) to run the tests. The test should only require the relaxed ordering and memory scope workgroup anyway. From the specificiation: "The non-explicit atomic_store function requires support for OpenCL C 2.0, or OpenCL C 3.0 or newer and both the __opencl_c_atomic_order_seq_cst and __opencl_c_atomic_scope_device features" "The non-explicit atomic_load function requires support for OpenCL C 2.0 or OpenCL C 3.0 or newer and both the __opencl_c_atomic_order_seq_cst and __opencl_c_atomic_scope_device features." "The non-explicit atomic_fetch_key functions require support for OpenCL C 2.0, or OpenCL C 3.0 or newer and both the __opencl_c_atomic_order_seq_cst and __opencl_c_atomic_scope_device features." --- .../generic_address_space/atomic_tests.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test_conformance/generic_address_space/atomic_tests.cpp b/test_conformance/generic_address_space/atomic_tests.cpp index 8a568f0a..7ab65d36 100644 --- a/test_conformance/generic_address_space/atomic_tests.cpp +++ b/test_conformance/generic_address_space/atomic_tests.cpp @@ -35,7 +35,7 @@ kernel void testKernel(global atomic_int* globalPtr, local atomic_int* localPtr) int wgid = get_group_id(0); int wgsize = get_local_size(0); - if (tid == 0) atomic_store(localPtr, 0); + if (tid == 0) atomic_store_explicit(localPtr, 0, memory_order_relaxed, memory_scope_work_group); barrier(CLK_LOCAL_MEM_FENCE); @@ -47,12 +47,12 @@ kernel void testKernel(global atomic_int* globalPtr, local atomic_int* localPtr) if ((wgid % 2) == 0) ptr = localPtr; - int inc = atomic_fetch_add(ptr, 1); + int inc = atomic_fetch_add_explicit(ptr, 1, memory_order_relaxed, memory_scope_work_group); // In the cases where the local memory ptr was used, // save off the final value. if ((wgid % 2) == 0 && inc == (wgsize-1)) - atomic_store(&globalPtr[wgid], inc); + atomic_store_explicit(&globalPtr[wgid], inc, memory_order_relaxed, memory_scope_work_group); } )OpenCLC"; @@ -67,7 +67,7 @@ kernel void testKernel(global atomic_int* globalPtr, local atomic_int* localPtr) int wgid = get_group_id(0); int wgsize = get_local_size(0); - if (tid == 0) atomic_store(localPtr, 0); + if (tid == 0) atomic_store_explicit(localPtr, 0, memory_order_relaxed, memory_scope_work_group); barrier(CLK_LOCAL_MEM_FENCE); @@ -79,14 +79,17 @@ kernel void testKernel(global atomic_int* globalPtr, local atomic_int* localPtr) if ((tid % 2) == 0) ptr = localPtr; - atomic_fetch_add(ptr, 1); + atomic_fetch_add_explicit(ptr, 1, memory_order_relaxed, memory_scope_work_group); barrier(CLK_LOCAL_MEM_FENCE); // In the cases where the local memory ptr was used, // save off the final value. if (tid == 0) - atomic_store(&globalPtr[(wgid * 2) + 1], atomic_load(localPtr)); + atomic_store_explicit(&globalPtr[(wgid * 2) + 1], + atomic_load_explicit(localPtr, memory_order_relaxed, memory_scope_work_group), + memory_order_relaxed, + memory_scope_work_group); } )OpenCLC"; } From ba991c01523ea11a0ab7d27e43736bbe7e256f0d Mon Sep 17 00:00:00 2001 From: Ahmed Hesham <117350656+ahesham-arm@users.noreply.github.com> Date: Tue, 28 Oct 2025 17:30:30 +0000 Subject: [PATCH 30/33] Add negative test for AHB (#2539) Add a wrapper around AHB for proper resource deallocation and refactor existing tests to use the wrapper. Add a negative test for AHB to test for error codes when calling clCreateImageWithProperties and clCreateBufferWithProperties. --------- Signed-off-by: Alex Davicenko Signed-off-by: Ahmed Hesham Co-authored-by: Alex Davicenko --- .../cl_khr_external_memory_ahb/debug_ahb.cpp | 25 + .../cl_khr_external_memory_ahb/debug_ahb.h | 90 ++- .../cl_khr_external_memory_ahb/test_ahb.cpp | 212 +++--- .../test_ahb_negative.cpp | 644 +++++++++++------- 4 files changed, 592 insertions(+), 379 deletions(-) diff --git a/test_conformance/extensions/cl_khr_external_memory_ahb/debug_ahb.cpp b/test_conformance/extensions/cl_khr_external_memory_ahb/debug_ahb.cpp index e0ca6615..d92ffd09 100644 --- a/test_conformance/extensions/cl_khr_external_memory_ahb/debug_ahb.cpp +++ b/test_conformance/extensions/cl_khr_external_memory_ahb/debug_ahb.cpp @@ -15,6 +15,7 @@ // #include "debug_ahb.h" +#include "harness/errorHelpers.h" constexpr AHardwareBuffer_UsageFlags flag_list[] = { AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, @@ -191,3 +192,27 @@ std::string ahardwareBufferFormatToString(AHardwareBuffer_Format format) } return result; } +AHardwareBuffer *create_AHB(AHardwareBuffer_Desc *desc) +{ + AHardwareBuffer *buffer_ptr = nullptr; + int err = AHardwareBuffer_allocate(desc, &buffer_ptr); + if (err != 0) + { + throw std::runtime_error("AHardwareBuffer_allocate failed with code: " + + std::to_string(err) + "\n"); + } + return buffer_ptr; +} + +void log_unsupported_ahb_format(AHardwareBuffer_Desc aHardwareBufferDesc) +{ + std::string usage_string = ahardwareBufferDecodeUsageFlagsToString( + static_cast(aHardwareBufferDesc.usage)); + log_info("Unsupported format %s:\n Usage flags %s\n Size (%u, %u, " + "layers = %u)\n", + ahardwareBufferFormatToString(static_cast( + aHardwareBufferDesc.format)) + .c_str(), + usage_string.c_str(), aHardwareBufferDesc.width, + aHardwareBufferDesc.height, aHardwareBufferDesc.layers); +} diff --git a/test_conformance/extensions/cl_khr_external_memory_ahb/debug_ahb.h b/test_conformance/extensions/cl_khr_external_memory_ahb/debug_ahb.h index 037bc6c2..2cf06fdc 100644 --- a/test_conformance/extensions/cl_khr_external_memory_ahb/debug_ahb.h +++ b/test_conformance/extensions/cl_khr_external_memory_ahb/debug_ahb.h @@ -19,6 +19,7 @@ #include #include #include +#include #define CHECK_AHARDWARE_BUFFER_SUPPORT(ahardwareBuffer_Desc, format) \ if (!AHardwareBuffer_isSupported(&ahardwareBuffer_Desc)) \ @@ -39,4 +40,91 @@ std::string ahardwareBufferFormatToString(AHardwareBuffer_Format format); std::string ahardwareBufferUsageFlagToString(AHardwareBuffer_UsageFlags flag); std::string -ahardwareBufferDecodeUsageFlagsToString(AHardwareBuffer_UsageFlags flags); \ No newline at end of file +ahardwareBufferDecodeUsageFlagsToString(AHardwareBuffer_UsageFlags flags); + +AHardwareBuffer* create_AHB(AHardwareBuffer_Desc* desc); +void log_unsupported_ahb_format(AHardwareBuffer_Desc desc); + +struct AHardwareBufferWrapper +{ + AHardwareBuffer* m_ahb; + + AHardwareBufferWrapper(): m_ahb(nullptr) {} + + AHardwareBufferWrapper(AHardwareBuffer* ahb) { m_ahb = ahb; } + + AHardwareBufferWrapper(AHardwareBuffer_Desc* desc) + { + m_ahb = create_AHB(desc); + } + + AHardwareBufferWrapper& operator=(AHardwareBuffer* rhs) + { + release(); + m_ahb = rhs; + + return *this; + } + + ~AHardwareBufferWrapper() { release(); } + + // Copy constructor + AHardwareBufferWrapper(AHardwareBufferWrapper const& ahbw) + : m_ahb(ahbw.m_ahb) + { + retain(); + } + + // Copy assignment operator + AHardwareBufferWrapper& operator=(AHardwareBufferWrapper const& rhs) + { + release(); + + m_ahb = rhs.m_ahb; + retain(); + + return *this; + } + + // Move constructor + AHardwareBufferWrapper(AHardwareBufferWrapper&& ahbw) + { + m_ahb = ahbw.m_ahb; + ahbw.m_ahb = nullptr; + } + + // Move assignment operator + AHardwareBufferWrapper& operator=(AHardwareBufferWrapper&& rhs) + { + if (this != &rhs) + { + release(); // Giving up current reference + m_ahb = rhs.m_ahb; + rhs.m_ahb = nullptr; + } + return *this; + } + + void retain() + { + if (nullptr != m_ahb) + { + AHardwareBuffer_acquire(m_ahb); + } + } + + void release() + { + if (nullptr != m_ahb) + { + AHardwareBuffer_release(m_ahb); + } + } + + // Usage operators + operator AHardwareBuffer*() { return m_ahb; } + cl_mem_properties get_props() + { + return reinterpret_cast(m_ahb); + } +}; diff --git a/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb.cpp b/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb.cpp index a73af256..5151a668 100644 --- a/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb.cpp +++ b/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb.cpp @@ -97,7 +97,7 @@ static const char *diff_images_kernel_source = { }; // Checks that the inferred image format is correct -REGISTER_TEST(test_images) +REGISTER_TEST(images) { cl_int err = CL_SUCCESS; @@ -134,19 +134,15 @@ REGISTER_TEST(test_images) CHECK_AHARDWARE_BUFFER_SUPPORT(aHardwareBufferDesc, format); - AHardwareBuffer *aHardwareBuffer = nullptr; - int ahb_result = AHardwareBuffer_allocate(&aHardwareBufferDesc, - &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", - ahb_result); - return TEST_FAIL; - } + AHardwareBufferWrapper aHardwareBuffer(&aHardwareBufferDesc); + log_info( + "Testing %s\n", + ahardwareBufferFormatToString(format.aHardwareBufferFormat) + .c_str()); - const cl_mem_properties props[] = { + cl_mem_properties props[] = { CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 + aHardwareBuffer.get_props(), 0 }; cl_mem image = clCreateImageWithProperties( @@ -181,8 +177,6 @@ REGISTER_TEST(test_images) test_error(clReleaseMemObject(image), "Failed to release image"); - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; } } } @@ -190,7 +184,7 @@ REGISTER_TEST(test_images) return TEST_PASS; } -REGISTER_TEST(test_images_read) +REGISTER_TEST(images_read) { cl_int err = CL_SUCCESS; RandomSeed seed(gRandomSeed); @@ -238,15 +232,11 @@ REGISTER_TEST(test_images_read) CHECK_AHARDWARE_BUFFER_SUPPORT(aHardwareBufferDesc, format); - AHardwareBuffer *aHardwareBuffer = nullptr; - int ahb_result = AHardwareBuffer_allocate(&aHardwareBufferDesc, - &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", - ahb_result); - return TEST_FAIL; - } + AHardwareBufferWrapper aHardwareBuffer(&aHardwareBufferDesc); + log_info( + "Testing %s\n", + ahardwareBufferFormatToString(format.aHardwareBufferFormat) + .c_str()); // Determine AHB memory layout AHardwareBuffer_Desc hardware_buffer_desc = {}; @@ -279,7 +269,7 @@ REGISTER_TEST(test_images_read) generate_random_image_data(&imageInfo, srcData, seed); void *hardware_buffer_data = nullptr; - ahb_result = AHardwareBuffer_lock( + int ahb_result = AHardwareBuffer_lock( aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr, &hardware_buffer_data); if (ahb_result != 0) @@ -301,7 +291,7 @@ REGISTER_TEST(test_images_read) cl_mem_properties props[] = { CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 + aHardwareBuffer.get_props(), 0 }; clMemWrapper imported_image = clCreateImageWithProperties( @@ -482,9 +472,6 @@ REGISTER_TEST(test_images_read) } } } - - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; } } } @@ -492,7 +479,7 @@ REGISTER_TEST(test_images_read) return TEST_PASS; } -REGISTER_TEST(test_enqueue_read_image) +REGISTER_TEST(enqueue_read_image) { cl_int err = CL_SUCCESS; RandomSeed seed(gRandomSeed); @@ -540,15 +527,12 @@ REGISTER_TEST(test_enqueue_read_image) CHECK_AHARDWARE_BUFFER_SUPPORT(aHardwareBufferDesc, format); - AHardwareBuffer *aHardwareBuffer = nullptr; - int ahb_result = AHardwareBuffer_allocate(&aHardwareBufferDesc, - &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", - ahb_result); - return TEST_FAIL; - } + AHardwareBufferWrapper aHardwareBuffer(&aHardwareBufferDesc); + + log_info( + "Testing %s\n", + ahardwareBufferFormatToString(format.aHardwareBufferFormat) + .c_str()); // Determine AHB memory layout AHardwareBuffer_Desc hardware_buffer_desc = {}; @@ -581,7 +565,7 @@ REGISTER_TEST(test_enqueue_read_image) generate_random_image_data(&imageInfo, srcData, seed); void *hardware_buffer_data = nullptr; - ahb_result = AHardwareBuffer_lock( + int ahb_result = AHardwareBuffer_lock( aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr, &hardware_buffer_data); if (ahb_result != 0) @@ -601,9 +585,9 @@ REGISTER_TEST(test_enqueue_read_image) return TEST_FAIL; } - const cl_mem_properties props[] = { + cl_mem_properties props[] = { CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 + aHardwareBuffer.get_props(), 0 }; clMemWrapper imported_image = clCreateImageWithProperties( @@ -662,9 +646,6 @@ REGISTER_TEST(test_enqueue_read_image) out_image_ptr += imageInfo.rowPitch; } - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; - if (total_matched == 0) { test_fail("Zero bytes matched"); @@ -676,7 +657,7 @@ REGISTER_TEST(test_enqueue_read_image) return TEST_PASS; } -REGISTER_TEST(test_enqueue_copy_image) +REGISTER_TEST(enqueue_copy_image) { cl_int err = CL_SUCCESS; RandomSeed seed(gRandomSeed); @@ -724,15 +705,12 @@ REGISTER_TEST(test_enqueue_copy_image) CHECK_AHARDWARE_BUFFER_SUPPORT(aHardwareBufferDesc, format); - AHardwareBuffer *aHardwareBuffer = nullptr; - int ahb_result = AHardwareBuffer_allocate(&aHardwareBufferDesc, - &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", - ahb_result); - return TEST_FAIL; - } + AHardwareBufferWrapper aHardwareBuffer(&aHardwareBufferDesc); + + log_info( + "Testing %s\n", + ahardwareBufferFormatToString(format.aHardwareBufferFormat) + .c_str()); // Determine AHB memory layout AHardwareBuffer_Desc hardware_buffer_desc = {}; @@ -765,7 +743,7 @@ REGISTER_TEST(test_enqueue_copy_image) generate_random_image_data(&imageInfo, srcData, seed); void *hardware_buffer_data = nullptr; - ahb_result = AHardwareBuffer_lock( + int ahb_result = AHardwareBuffer_lock( aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr, &hardware_buffer_data); if (ahb_result != 0) @@ -787,7 +765,7 @@ REGISTER_TEST(test_enqueue_copy_image) cl_mem_properties props[] = { CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 + aHardwareBuffer.get_props(), 0 }; clMemWrapper imported_image = clCreateImageWithProperties( @@ -975,9 +953,6 @@ REGISTER_TEST(test_enqueue_copy_image) } } } - - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; } } } @@ -985,7 +960,7 @@ REGISTER_TEST(test_enqueue_copy_image) return TEST_PASS; } -REGISTER_TEST(test_enqueue_copy_image_to_buffer) +REGISTER_TEST(enqueue_copy_image_to_buffer) { cl_int err = CL_SUCCESS; RandomSeed seed(gRandomSeed); @@ -1033,15 +1008,12 @@ REGISTER_TEST(test_enqueue_copy_image_to_buffer) CHECK_AHARDWARE_BUFFER_SUPPORT(aHardwareBufferDesc, format); - AHardwareBuffer *aHardwareBuffer = nullptr; - int ahb_result = AHardwareBuffer_allocate(&aHardwareBufferDesc, - &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", - ahb_result); - return TEST_FAIL; - } + AHardwareBufferWrapper aHardwareBuffer(&aHardwareBufferDesc); + + log_info( + "Testing %s\n", + ahardwareBufferFormatToString(format.aHardwareBufferFormat) + .c_str()); // Determine AHB memory layout AHardwareBuffer_Desc hardware_buffer_desc = {}; @@ -1074,7 +1046,7 @@ REGISTER_TEST(test_enqueue_copy_image_to_buffer) generate_random_image_data(&imageInfo, srcData, seed); void *hardware_buffer_data = nullptr; - ahb_result = AHardwareBuffer_lock( + int ahb_result = AHardwareBuffer_lock( aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr, &hardware_buffer_data); if (ahb_result != 0) @@ -1096,7 +1068,7 @@ REGISTER_TEST(test_enqueue_copy_image_to_buffer) cl_mem_properties props[] = { CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 + aHardwareBuffer.get_props(), 0 }; clMemWrapper imported_image = clCreateImageWithProperties( @@ -1165,9 +1137,6 @@ REGISTER_TEST(test_enqueue_copy_image_to_buffer) out_buffer_ptr += scanlineSize; } - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; - if (total_matched == 0) { test_fail("Zero bytes matched"); @@ -1179,7 +1148,7 @@ REGISTER_TEST(test_enqueue_copy_image_to_buffer) return TEST_PASS; } -REGISTER_TEST(test_enqueue_copy_buffer_to_image) +REGISTER_TEST(enqueue_copy_buffer_to_image) { cl_int err = CL_SUCCESS; RandomSeed seed(gRandomSeed); @@ -1227,15 +1196,12 @@ REGISTER_TEST(test_enqueue_copy_buffer_to_image) CHECK_AHARDWARE_BUFFER_SUPPORT(aHardwareBufferDesc, format); - AHardwareBuffer *aHardwareBuffer = nullptr; - int ahb_result = AHardwareBuffer_allocate(&aHardwareBufferDesc, - &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", - ahb_result); - return TEST_FAIL; - } + AHardwareBufferWrapper aHardwareBuffer(&aHardwareBufferDesc); + + log_info( + "Testing %s\n", + ahardwareBufferFormatToString(format.aHardwareBufferFormat) + .c_str()); // Determine AHB memory layout AHardwareBuffer_Desc hardware_buffer_desc = {}; @@ -1275,7 +1241,7 @@ REGISTER_TEST(test_enqueue_copy_buffer_to_image) cl_mem_properties props[] = { CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 + aHardwareBuffer.get_props(), 0 }; clMemWrapper imported_image = clCreateImageWithProperties( @@ -1307,7 +1273,7 @@ REGISTER_TEST(test_enqueue_copy_buffer_to_image) &hardware_buffer_desc); void *hardware_buffer_data = nullptr; - ahb_result = AHardwareBuffer_lock( + int ahb_result = AHardwareBuffer_lock( aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1, nullptr, &hardware_buffer_data); if (ahb_result != 0) @@ -1366,9 +1332,6 @@ REGISTER_TEST(test_enqueue_copy_buffer_to_image) return TEST_FAIL; } - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; - if (total_matched == 0) { test_fail("Zero bytes matched"); @@ -1380,7 +1343,7 @@ REGISTER_TEST(test_enqueue_copy_buffer_to_image) return TEST_PASS; } -REGISTER_TEST(test_enqueue_write_image) +REGISTER_TEST(enqueue_write_image) { cl_int err = CL_SUCCESS; RandomSeed seed(gRandomSeed); @@ -1428,15 +1391,12 @@ REGISTER_TEST(test_enqueue_write_image) CHECK_AHARDWARE_BUFFER_SUPPORT(aHardwareBufferDesc, format); - AHardwareBuffer *aHardwareBuffer = nullptr; - int ahb_result = AHardwareBuffer_allocate(&aHardwareBufferDesc, - &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", - ahb_result); - return TEST_FAIL; - } + AHardwareBufferWrapper aHardwareBuffer(&aHardwareBufferDesc); + + log_info( + "Testing %s\n", + ahardwareBufferFormatToString(format.aHardwareBufferFormat) + .c_str()); // Determine AHB memory layout AHardwareBuffer_Desc hardware_buffer_desc = {}; @@ -1453,7 +1413,7 @@ REGISTER_TEST(test_enqueue_write_image) cl_mem_properties props[] = { CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 + aHardwareBuffer.get_props(), 0 }; clMemWrapper imported_image = clCreateImageWithProperties( @@ -1503,7 +1463,7 @@ REGISTER_TEST(test_enqueue_write_image) &hardware_buffer_desc); void *hardware_buffer_data = nullptr; - ahb_result = AHardwareBuffer_lock( + int ahb_result = AHardwareBuffer_lock( aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1, nullptr, &hardware_buffer_data); if (ahb_result != 0) @@ -1564,9 +1524,6 @@ REGISTER_TEST(test_enqueue_write_image) return TEST_FAIL; } - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; - if (total_matched == 0) { test_fail("Zero bytes matched"); @@ -1578,7 +1535,7 @@ REGISTER_TEST(test_enqueue_write_image) return TEST_PASS; } -REGISTER_TEST(test_enqueue_fill_image) +REGISTER_TEST(enqueue_fill_image) { cl_int err = CL_SUCCESS; RandomSeed seed(gRandomSeed); @@ -1626,15 +1583,12 @@ REGISTER_TEST(test_enqueue_fill_image) CHECK_AHARDWARE_BUFFER_SUPPORT(aHardwareBufferDesc, format); - AHardwareBuffer *aHardwareBuffer = nullptr; - int ahb_result = AHardwareBuffer_allocate(&aHardwareBufferDesc, - &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", - ahb_result); - return TEST_FAIL; - } + AHardwareBufferWrapper aHardwareBuffer(&aHardwareBufferDesc); + + log_info( + "Testing %s\n", + ahardwareBufferFormatToString(format.aHardwareBufferFormat) + .c_str()); // Determine AHB memory layout AHardwareBuffer_Desc hardware_buffer_desc = {}; @@ -1650,7 +1604,7 @@ REGISTER_TEST(test_enqueue_fill_image) cl_mem_properties props[] = { CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 + aHardwareBuffer.get_props(), 0 }; clMemWrapper imported_image = clCreateImageWithProperties( @@ -1739,7 +1693,7 @@ REGISTER_TEST(test_enqueue_fill_image) &hardware_buffer_desc); void *hardware_buffer_data = nullptr; - ahb_result = AHardwareBuffer_lock( + int ahb_result = AHardwareBuffer_lock( aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1, nullptr, &hardware_buffer_data); if (ahb_result != 0) @@ -1819,8 +1773,6 @@ REGISTER_TEST(test_enqueue_fill_image) return TEST_FAIL; } - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; free(verificationLine); if (total_matched == 0) @@ -1834,7 +1786,7 @@ REGISTER_TEST(test_enqueue_fill_image) return TEST_PASS; } -REGISTER_TEST(test_blob) +REGISTER_TEST(blob) { cl_int err = CL_SUCCESS; @@ -1883,19 +1835,17 @@ REGISTER_TEST(test_blob) continue; } - AHardwareBuffer *aHardwareBuffer = nullptr; - int ahb_result = - AHardwareBuffer_allocate(&aHardwareBufferDesc, &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", - ahb_result); - return TEST_FAIL; - } + AHardwareBufferWrapper aHardwareBuffer(&aHardwareBufferDesc); + + log_info( + "Testing %s\n", + ahardwareBufferFormatToString( + static_cast(aHardwareBufferDesc.format)) + .c_str()); cl_mem_properties props[] = { CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 + aHardwareBuffer.get_props(), 0 }; cl_mem buffer = clCreateBufferWithProperties( @@ -1903,8 +1853,6 @@ REGISTER_TEST(test_blob) test_error(err, "Failed to create CL buffer from AHardwareBuffer"); test_error(clReleaseMemObject(buffer), "Failed to release buffer"); - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; } return TEST_PASS; diff --git a/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb_negative.cpp b/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb_negative.cpp index ca010b58..6cf5c6d1 100644 --- a/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb_negative.cpp +++ b/test_conformance/extensions/cl_khr_external_memory_ahb/test_ahb_negative.cpp @@ -1,246 +1,398 @@ -// -// 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/compat.h" -#include "harness/kernelHelpers.h" -#include "harness/imageHelpers.h" -#include "harness/errorHelpers.h" -#include -#include "debug_ahb.h" - -REGISTER_TEST(test_buffer_format_negative) -{ - cl_int err = CL_SUCCESS; - - if (!is_extension_available(device, "cl_khr_external_memory")) - { - log_info("cl_khr_external_memory is not supported on this platform. " - "Skipping test.\n"); - return TEST_SKIPPED_ITSELF; - } - if (!is_extension_available( - device, "cl_khr_external_memory_android_hardware_buffer")) - { - log_info("cl_khr_external_memory_android_hardware_buffer is not " - "supported on this platform. " - "Skipping test.\n"); - return TEST_SKIPPED_ITSELF; - } - - AHardwareBuffer_Desc aHardwareBufferDesc = { 0 }; - aHardwareBufferDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; - aHardwareBufferDesc.usage = AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER; - aHardwareBufferDesc.width = 64; - aHardwareBufferDesc.height = 1; - aHardwareBufferDesc.layers = 1; - aHardwareBufferDesc.usage = AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER; - - if (!AHardwareBuffer_isSupported(&aHardwareBufferDesc)) - { - const std::string usage_string = - ahardwareBufferDecodeUsageFlagsToString( - static_cast( - aHardwareBufferDesc.usage)); - log_info( - "Unsupported format %s, usage flags %s\n", - ahardwareBufferFormatToString( - static_cast(aHardwareBufferDesc.format)) - .c_str(), - usage_string.c_str()); - return TEST_SKIPPED_ITSELF; - } - - AHardwareBuffer *aHardwareBuffer = nullptr; - const int ahb_result = - AHardwareBuffer_allocate(&aHardwareBufferDesc, &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", ahb_result); - return TEST_FAIL; - } - log_info("Testing %s\n", - ahardwareBufferFormatToString(static_cast( - aHardwareBufferDesc.format)) - .c_str()); - - cl_mem_properties props[] = { - CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 - }; - - cl_mem buffer = clCreateBufferWithProperties( - context, props, CL_MEM_READ_WRITE, 0, nullptr, &err); - test_assert_error(err == CL_INVALID_OPERATION, - "To create a buffer the aHardwareFormat must be " - "AHARDWAREBUFFER_FORMAT_BLOB"); - - if (buffer != nullptr) - { - test_error(clReleaseMemObject(buffer), "Failed to release buffer"); - } - - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; - - return TEST_PASS; -} - -REGISTER_TEST(test_buffer_size_negative) -{ - cl_int err = CL_SUCCESS; - constexpr size_t buffer_size = 64; - - if (!is_extension_available(device, "cl_khr_external_memory")) - { - log_info("cl_khr_external_memory is not supported on this platform. " - "Skipping test.\n"); - return TEST_SKIPPED_ITSELF; - } - if (!is_extension_available( - device, "cl_khr_external_memory_android_hardware_buffer")) - { - log_info("cl_khr_external_memory_android_hardware_buffer is not " - "supported on this platform. " - "Skipping test.\n"); - return TEST_SKIPPED_ITSELF; - } - - AHardwareBuffer_Desc aHardwareBufferDesc = { 0 }; - aHardwareBufferDesc.format = AHARDWAREBUFFER_FORMAT_BLOB; - aHardwareBufferDesc.usage = AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER; - aHardwareBufferDesc.width = buffer_size; - aHardwareBufferDesc.height = 1; - aHardwareBufferDesc.layers = 1; - aHardwareBufferDesc.usage = AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER; - - if (!AHardwareBuffer_isSupported(&aHardwareBufferDesc)) - { - const std::string usage_string = - ahardwareBufferDecodeUsageFlagsToString( - static_cast( - aHardwareBufferDesc.usage)); - log_info( - "Unsupported format %s, usage flags %s\n", - ahardwareBufferFormatToString( - static_cast(aHardwareBufferDesc.format)) - .c_str(), - usage_string.c_str()); - return TEST_SKIPPED_ITSELF; - } - - AHardwareBuffer *aHardwareBuffer = nullptr; - const int ahb_result = - AHardwareBuffer_allocate(&aHardwareBufferDesc, &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", ahb_result); - return TEST_FAIL; - } - log_info("Testing %s\n", - ahardwareBufferFormatToString(static_cast( - aHardwareBufferDesc.format)) - .c_str()); - - cl_mem_properties props[] = { - CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 - }; - - cl_mem buffer = clCreateBufferWithProperties( - context, props, CL_MEM_READ_WRITE, buffer_size / 2, nullptr, &err); - test_assert_error(err == CL_INVALID_BUFFER_SIZE, - "Wrong error value returned"); - - if (buffer != nullptr) - { - test_error(clReleaseMemObject(buffer), "Failed to release buffer"); - } - - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; - - return TEST_PASS; -} - -REGISTER_TEST(test_images_negative) -{ - cl_int err = CL_SUCCESS; - - if (!is_extension_available(device, "cl_khr_external_memory")) - { - log_info("cl_khr_external_memory is not supported on this platform. " - "Skipping test.\n"); - return TEST_SKIPPED_ITSELF; - } - if (!is_extension_available( - device, "cl_khr_external_memory_android_hardware_buffer")) - { - log_info("cl_khr_external_memory_android_hardware_buffer is not " - "supported on this platform. " - "Skipping test.\n"); - return TEST_SKIPPED_ITSELF; - } - - AHardwareBuffer_Desc aHardwareBufferDesc = { 0 }; - aHardwareBufferDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; - aHardwareBufferDesc.usage = static_cast( - AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN - | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN - | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE - | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER); - aHardwareBufferDesc.width = 64; - aHardwareBufferDesc.height = 64; - aHardwareBufferDesc.layers = 1; - - AHardwareBuffer *aHardwareBuffer = nullptr; - int ahb_result = - AHardwareBuffer_allocate(&aHardwareBufferDesc, &aHardwareBuffer); - if (ahb_result != 0) - { - log_error("AHardwareBuffer_allocate failed with code %d\n", ahb_result); - return TEST_FAIL; - } - - const cl_mem_properties props[] = { - CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, - reinterpret_cast(aHardwareBuffer), 0 - }; - - constexpr cl_image_format image_format = { CL_RGBA, CL_UNORM_INT8 }; - cl_mem image = - clCreateImageWithProperties(context, props, CL_MEM_READ_WRITE, - &image_format, nullptr, nullptr, &err); - test_assert_error(err == CL_INVALID_IMAGE_FORMAT_DESCRIPTOR, - "Wrong error value returned"); - if (image != nullptr) - { - test_error(clReleaseMemObject(image), "Failed to release image"); - } - - constexpr cl_image_desc image_desc = { CL_MEM_OBJECT_IMAGE2D, 64, 64 }; - image = clCreateImageWithProperties(context, props, CL_MEM_READ_WRITE, - nullptr, &image_desc, nullptr, &err); - test_assert_error(err == CL_INVALID_IMAGE_DESCRIPTOR, - "Wrong error value returned"); - if (image != nullptr) - { - test_error(clReleaseMemObject(image), "Failed to release image"); - } - AHardwareBuffer_release(aHardwareBuffer); - aHardwareBuffer = nullptr; - - return TEST_PASS; -} +// +// 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/compat.h" +#include "harness/kernelHelpers.h" +#include "harness/imageHelpers.h" +#include "harness/errorHelpers.h" +#include +#include "debug_ahb.h" + +REGISTER_TEST(buffer_format_negative) +{ + cl_int err = CL_SUCCESS; + + if (!is_extension_available(device, "cl_khr_external_memory")) + { + log_info("cl_khr_external_memory is not supported on this platform. " + "Skipping test.\n"); + return TEST_SKIPPED_ITSELF; + } + if (!is_extension_available( + device, "cl_khr_external_memory_android_hardware_buffer")) + { + log_info("cl_khr_external_memory_android_hardware_buffer is not " + "supported on this platform. " + "Skipping test.\n"); + return TEST_SKIPPED_ITSELF; + } + + AHardwareBuffer_Desc aHardwareBufferDesc = { 0 }; + aHardwareBufferDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; + aHardwareBufferDesc.usage = AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER; + aHardwareBufferDesc.width = 64; + aHardwareBufferDesc.height = 1; + aHardwareBufferDesc.layers = 1; + aHardwareBufferDesc.usage = AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER; + + if (!AHardwareBuffer_isSupported(&aHardwareBufferDesc)) + { + const std::string usage_string = + ahardwareBufferDecodeUsageFlagsToString( + static_cast( + aHardwareBufferDesc.usage)); + log_info( + "Unsupported format %s, usage flags %s\n", + ahardwareBufferFormatToString( + static_cast(aHardwareBufferDesc.format)) + .c_str(), + usage_string.c_str()); + return TEST_SKIPPED_ITSELF; + } + + AHardwareBuffer *aHardwareBuffer = nullptr; + const int ahb_result = + AHardwareBuffer_allocate(&aHardwareBufferDesc, &aHardwareBuffer); + if (ahb_result != 0) + { + log_error("AHardwareBuffer_allocate failed with code %d\n", ahb_result); + return TEST_FAIL; + } + log_info("Testing %s\n", + ahardwareBufferFormatToString(static_cast( + aHardwareBufferDesc.format)) + .c_str()); + + cl_mem_properties props[] = { + CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, + reinterpret_cast(aHardwareBuffer), 0 + }; + + cl_mem buffer = clCreateBufferWithProperties( + context, props, CL_MEM_READ_WRITE, 0, nullptr, &err); + test_assert_error(err == CL_INVALID_OPERATION, + "To create a buffer the aHardwareFormat must be " + "AHARDWAREBUFFER_FORMAT_BLOB"); + + if (buffer != nullptr) + { + test_error(clReleaseMemObject(buffer), "Failed to release buffer"); + } + + AHardwareBuffer_release(aHardwareBuffer); + aHardwareBuffer = nullptr; + + return TEST_PASS; +} + +REGISTER_TEST(buffer_size_negative) +{ + cl_int err = CL_SUCCESS; + constexpr size_t buffer_size = 64; + + if (!is_extension_available(device, "cl_khr_external_memory")) + { + log_info("cl_khr_external_memory is not supported on this platform. " + "Skipping test.\n"); + return TEST_SKIPPED_ITSELF; + } + if (!is_extension_available( + device, "cl_khr_external_memory_android_hardware_buffer")) + { + log_info("cl_khr_external_memory_android_hardware_buffer is not " + "supported on this platform. " + "Skipping test.\n"); + return TEST_SKIPPED_ITSELF; + } + + AHardwareBuffer_Desc aHardwareBufferDesc = { 0 }; + aHardwareBufferDesc.format = AHARDWAREBUFFER_FORMAT_BLOB; + aHardwareBufferDesc.usage = AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER; + aHardwareBufferDesc.width = buffer_size; + aHardwareBufferDesc.height = 1; + aHardwareBufferDesc.layers = 1; + aHardwareBufferDesc.usage = AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER; + + if (!AHardwareBuffer_isSupported(&aHardwareBufferDesc)) + { + const std::string usage_string = + ahardwareBufferDecodeUsageFlagsToString( + static_cast( + aHardwareBufferDesc.usage)); + log_info( + "Unsupported format %s, usage flags %s\n", + ahardwareBufferFormatToString( + static_cast(aHardwareBufferDesc.format)) + .c_str(), + usage_string.c_str()); + return TEST_SKIPPED_ITSELF; + } + + AHardwareBuffer *aHardwareBuffer = nullptr; + const int ahb_result = + AHardwareBuffer_allocate(&aHardwareBufferDesc, &aHardwareBuffer); + if (ahb_result != 0) + { + log_error("AHardwareBuffer_allocate failed with code %d\n", ahb_result); + return TEST_FAIL; + } + log_info("Testing %s\n", + ahardwareBufferFormatToString(static_cast( + aHardwareBufferDesc.format)) + .c_str()); + + cl_mem_properties props[] = { + CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, + reinterpret_cast(aHardwareBuffer), 0 + }; + + cl_mem buffer = clCreateBufferWithProperties( + context, props, CL_MEM_READ_WRITE, buffer_size / 2, nullptr, &err); + test_assert_error(err == CL_INVALID_BUFFER_SIZE, + "Wrong error value returned"); + + if (buffer != nullptr) + { + test_error(clReleaseMemObject(buffer), "Failed to release buffer"); + } + + AHardwareBuffer_release(aHardwareBuffer); + aHardwareBuffer = nullptr; + + return TEST_PASS; +} + +REGISTER_TEST(images_negative) +{ + cl_int err = CL_SUCCESS; + + if (!is_extension_available(device, "cl_khr_external_memory")) + { + log_info("cl_khr_external_memory is not supported on this platform. " + "Skipping test.\n"); + return TEST_SKIPPED_ITSELF; + } + if (!is_extension_available( + device, "cl_khr_external_memory_android_hardware_buffer")) + { + log_info("cl_khr_external_memory_android_hardware_buffer is not " + "supported on this platform. " + "Skipping test.\n"); + return TEST_SKIPPED_ITSELF; + } + + AHardwareBuffer_Desc aHardwareBufferDesc = { 0 }; + aHardwareBufferDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; + aHardwareBufferDesc.usage = static_cast( + AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN + | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN + | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE + | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER); + aHardwareBufferDesc.width = 64; + aHardwareBufferDesc.height = 64; + aHardwareBufferDesc.layers = 1; + + AHardwareBuffer *aHardwareBuffer = nullptr; + int ahb_result = + AHardwareBuffer_allocate(&aHardwareBufferDesc, &aHardwareBuffer); + if (ahb_result != 0) + { + log_error("AHardwareBuffer_allocate failed with code %d\n", ahb_result); + return TEST_FAIL; + } + + const cl_mem_properties props[] = { + CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, + reinterpret_cast(aHardwareBuffer), 0 + }; + + constexpr cl_image_format image_format = { CL_RGBA, CL_UNORM_INT8 }; + cl_mem image = + clCreateImageWithProperties(context, props, CL_MEM_READ_WRITE, + &image_format, nullptr, nullptr, &err); + test_assert_error(err == CL_INVALID_IMAGE_FORMAT_DESCRIPTOR, + "Wrong error value returned"); + if (image != nullptr) + { + test_error(clReleaseMemObject(image), "Failed to release image"); + } + + constexpr cl_image_desc image_desc = { CL_MEM_OBJECT_IMAGE2D, 64, 64 }; + image = clCreateImageWithProperties(context, props, CL_MEM_READ_WRITE, + nullptr, &image_desc, nullptr, &err); + test_assert_error(err == CL_INVALID_IMAGE_DESCRIPTOR, + "Wrong error value returned"); + if (image != nullptr) + { + test_error(clReleaseMemObject(image), "Failed to release image"); + } + AHardwareBuffer_release(aHardwareBuffer); + aHardwareBuffer = nullptr; + + return TEST_PASS; +} + +REGISTER_TEST(invalid_arguments) +{ + cl_int err; + constexpr cl_uint buffer_size = 4096; + + if (!is_extension_available( + device, "cl_khr_external_memory_android_hardware_buffer")) + { + log_info("cl_khr_external_memory_android_hardware_buffer is not " + "supported on this platform. Skipping test.\n"); + return TEST_SKIPPED_ITSELF; + } + + AHardwareBuffer_Desc aHardwareBufferDesc = { 0 }; + aHardwareBufferDesc.width = buffer_size; + aHardwareBufferDesc.height = 1; + aHardwareBufferDesc.layers = 1; + aHardwareBufferDesc.format = AHARDWAREBUFFER_FORMAT_BLOB; + aHardwareBufferDesc.usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN + | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN; + + if (!AHardwareBuffer_isSupported(&aHardwareBufferDesc)) + { + log_unsupported_ahb_format(aHardwareBufferDesc); + return TEST_SKIPPED_ITSELF; + } + + AHardwareBufferWrapper ahb_buffer(&aHardwareBufferDesc); + + aHardwareBufferDesc.width = 64; + aHardwareBufferDesc.height = 64; + aHardwareBufferDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; + + if (!AHardwareBuffer_isSupported(&aHardwareBufferDesc)) + { + log_unsupported_ahb_format(aHardwareBufferDesc); + return TEST_SKIPPED_ITSELF; + } + + AHardwareBufferWrapper ahb_image(&aHardwareBufferDesc); + + const cl_mem_properties props_buffer[] = { + CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, + ahb_buffer.get_props(), + 0, + }; + const cl_mem_properties props_image[] = { + CL_EXTERNAL_MEMORY_HANDLE_ANDROID_HARDWARE_BUFFER_KHR, + ahb_image.get_props(), + 0, + }; + + // stub values + cl_image_format image_format = { 0 }; + cl_image_desc image_desc = { 0 }; + int host_data = 0; + void *host_ptr = reinterpret_cast(&host_data); + + log_info("Testing buffer error conditions\n"); + + // Buffer error conditions + clMemWrapper mem = + clCreateBufferWithProperties(context, props_buffer, CL_MEM_READ_WRITE, + buffer_size + 1, nullptr, &err); + if (CL_INVALID_BUFFER_SIZE != err) + { + log_error( + "clCreateBufferWithProperties should return CL_INVALID_BUFFER_SIZE " + "but returned %s: CL_INVALID_BUFFER_SIZE if size is non-zero and " + "greater than the AHardwareBuffer when importing external memory " + "using cl_khr_external_memory_android_hardware_buffer\n", + IGetErrorString(err)); + return TEST_FAIL; + } + + mem = clCreateBufferWithProperties(context, props_image, CL_MEM_READ_WRITE, + 0, nullptr, &err); + if (CL_INVALID_OPERATION != err) + { + log_error( + "clCreateBufferWithProperties should return CL_INVALID_OPERATION " + "but returned %s: CL_INVALID_OPERATION if the AHardwareBuffer " + "format is not AHARDWAREBUFFER_FORMAT_BLOB\n", + IGetErrorString(err)); + return TEST_FAIL; + } + + mem = clCreateBufferWithProperties(context, props_buffer, CL_MEM_READ_WRITE, + 0, host_ptr, &err); + if (CL_INVALID_HOST_PTR != err) + { + log_error( + "clCreateBufferWithProperties should return CL_INVALID_HOST_PTR " + "but returned %s: CL_INVALID_HOST_PTR if host_ptr is not NULL\n", + IGetErrorString(err)); + return TEST_FAIL; + } + + log_info("Testing image error conditions\n"); + + // Image error conditions + mem = clCreateImageWithProperties(context, props_image, CL_MEM_READ_WRITE, + &image_format, nullptr, nullptr, &err); + if (CL_INVALID_IMAGE_FORMAT_DESCRIPTOR != err) + { + log_error( + "clCreateBufferWithProperties should return " + "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR but returned %s: " + "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR if image_format is not NULL " + "when using cl_khr_external_memory_android_hardware_buffer.\n", + IGetErrorString(err)); + return TEST_FAIL; + } + + mem = clCreateImageWithProperties(context, props_image, CL_MEM_READ_WRITE, + nullptr, &image_desc, nullptr, &err); + if (CL_INVALID_IMAGE_DESCRIPTOR != err) + { + log_error("clCreateBufferWithProperties should return " + "CL_INVALID_IMAGE_DESCRIPTOR but returned %s: " + "CL_INVALID_IMAGE_DESCRIPTOR if image_desc is not NULL when " + "using cl_khr_external_memory_android_hardware_buffer.\n", + IGetErrorString(err)); + return TEST_FAIL; + } + + mem = clCreateImageWithProperties(context, props_buffer, CL_MEM_READ_WRITE, + nullptr, nullptr, nullptr, &err); + if (CL_IMAGE_FORMAT_NOT_SUPPORTED != err) + { + log_error("clCreateBufferWithProperties should return " + "CL_IMAGE_FORMAT_NOT_SUPPORTED but returned %s: " + "CL_IMAGE_FORMAT_NOT_SUPPORTED if AHardwareBuffer's format " + "is not supported\n", + IGetErrorString(err)); + return TEST_FAIL; + } + + mem = clCreateImageWithProperties(context, props_image, CL_MEM_READ_WRITE, + nullptr, nullptr, host_ptr, &err); + if (CL_INVALID_HOST_PTR != err) + { + log_error( + "clCreateBufferWithProperties should return CL_INVALID_HOST_PTR " + "but returned %s: CL_INVALID_HOST_PTR if host_ptr is not NULL\n", + IGetErrorString(err)); + return TEST_FAIL; + } + + return TEST_PASS; +} From 941f7edb95fd1f33f5ef3a64f2e4c297069d24fd Mon Sep 17 00:00:00 2001 From: Marcin Hajder Date: Tue, 4 Nov 2025 17:41:26 +0100 Subject: [PATCH 31/33] Added coverage of using multiple kernels created from the same source program with different builds (#2537) Fixes #2164 according to issue description --- test_conformance/compiler/test_compile.cpp | 106 +++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/test_conformance/compiler/test_compile.cpp b/test_conformance/compiler/test_compile.cpp index 6e6e5f53..b5a134d1 100644 --- a/test_conformance/compiler/test_compile.cpp +++ b/test_conformance/compiler/test_compile.cpp @@ -25,6 +25,7 @@ #endif #include "harness/conversions.h" #include "harness/stringHelpers.h" +#include "harness/parseParameters.h" #define MAX_LINE_SIZE_IN_PROGRAM 1024 #define MAX_LOG_SIZE_IN_PROGRAM 2048 @@ -159,6 +160,14 @@ const char *link_static_function_access = // use with compile_static_function "extern int foo(int, int);\n" "int access_foo() { int blah = foo(3, 4); return blah + 5; }\n"; +const char *multi_build_test_kernel = R"( +__kernel void test_kernel(__global int *dst) +{ + int tid = get_global_id(0); + dst[tid] = BUILD_OPT_VAL; +} +)"; + static int test_large_single_compile(cl_context context, cl_device_id deviceID, unsigned int numLines) { @@ -3933,3 +3942,100 @@ REGISTER_TEST(compile_and_link_status_options_log) return 0; } + +REGISTER_TEST(multiple_build_program) +{ + if (gCompilationMode != kOnline) + { + log_info( + "Skipping multiple_build_program, compilation mode not online\n"); + return TEST_SKIPPED_ITSELF; + } + + cl_int error = CL_SUCCESS; + const size_t num_threads = num_elements; + + clProgramWrapper program = clCreateProgramWithSource( + context, 1, &multi_build_test_kernel, nullptr, &error); + test_error(error, "clCreateProgramWithSource failed"); + + clMemWrapper out_stream_0 = clCreateBuffer( + context, CL_MEM_READ_WRITE, sizeof(cl_int) * num_threads, NULL, &error); + test_error(error, "clCreateBuffer failed"); + + clMemWrapper out_stream_1 = clCreateBuffer( + context, CL_MEM_READ_WRITE, sizeof(cl_int) * num_threads, NULL, &error); + test_error(error, "clCreateBuffer failed"); + + { + /* Build with the macro defined */ + error = clBuildProgram(program, 1, &device, "-DBUILD_OPT_VAL=1 ", NULL, + NULL); + test_error(error, "clBuildProgram failed"); + + clKernelWrapper kernel0 = + clCreateKernel(program, "test_kernel", &error); + test_error(error, "clCreateKernel failed"); + + error = clSetKernelArg(kernel0, 0, sizeof(out_stream_0), &out_stream_0); + test_error(error, "clSetKernelArg failed"); + + error = clEnqueueNDRangeKernel(queue, kernel0, 1, NULL, &num_threads, + NULL, 0, NULL, NULL); + test_error(error, "clEnqueueNDRangeKernel failed"); + } + + { + /* Rebuild with the macro redefined */ + error = clBuildProgram(program, 1, &device, "-DBUILD_OPT_VAL=2 ", NULL, + NULL); + test_error(error, "clBuildProgram failed"); + + clKernelWrapper kernel1 = + clCreateKernel(program, "test_kernel", &error); + test_error(error, "clCreateKernel failed"); + + error = clSetKernelArg(kernel1, 0, sizeof(out_stream_1), &out_stream_1); + test_error(error, "clSetKernelArg failed"); + + error = clEnqueueNDRangeKernel(queue, kernel1, 1, NULL, &num_threads, + NULL, 0, NULL, NULL); + test_error(error, "clEnqueueNDRangeKernel failed"); + } + + error = clFinish(queue); + test_error(error, "clFinish failed"); + + std::vector test_values(num_threads, 0); + error = clEnqueueReadBuffer(queue, out_stream_0, true, 0, + sizeof(cl_int) * num_threads, + test_values.data(), 0, NULL, NULL); + test_error(error, "clEnqueueReadBuffer failed"); + + for (size_t i = 0; i < test_values.size(); i++) + { + if (test_values[i] != 1) + { + log_error("Unexpected test value %d for kernel0 at pos %zu.\n", + test_values[i], i); + return TEST_FAIL; + } + } + + error = clEnqueueReadBuffer(queue, out_stream_1, true, 0, + sizeof(cl_int) * num_threads, + test_values.data(), 0, NULL, NULL); + test_error(error, "clEnqueueReadBuffer failed"); + + for (size_t i = 0; i < test_values.size(); i++) + { + if (test_values[i] != 2) + { + log_error("Unexpected test value %d for kernel1 at pos %zu.\n", + test_values[i], i); + return TEST_FAIL; + } + } + + return TEST_PASS; +} From 9abcd0054c1cac66d3ea2c496a52fc184ff367a1 Mon Sep 17 00:00:00 2001 From: gorazd-sumkovski-arm <161028652+gorazd-sumkovski-arm@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:44:31 +0000 Subject: [PATCH 32/33] Add ThreadSanitizer option (#2560) Add the `SANITIZER_THREAD` CMake option which will add the `-fsanitize=thread` flag to the compiler and linker when enabled. --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c0481ee..30a64447 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,12 @@ if(USE_CL_EXPERIMENTAL) add_definitions(-DCL_EXPERIMENTAL) endif(USE_CL_EXPERIMENTAL) +option(SANITIZER_THREAD "Build with the thread sanitiser" OFF) +if (SANITIZER_THREAD) + add_compile_options(-fsanitize=thread) + add_link_options(-fsanitize=thread) +endif (SANITIZER_THREAD) + #----------------------------------------------------------- # Default Configurable Test Set #----------------------------------------------------------- From e641de99a5c6f671c63e490acd5da0e492a4438f Mon Sep 17 00:00:00 2001 From: Ben Ashbaugh Date: Tue, 4 Nov 2025 08:50:30 -0800 Subject: [PATCH 33/33] add test for a NULL local work size and a required work-group size (#2514) see #2501 This tests the following scenarios: 1. Execute a kernel with a required work-group size, passing `NULL` as the local work size. 2. Query the suggested work-group size for a kernel with a required work-group size. --- .../api/test_kernel_attributes.cpp | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/test_conformance/api/test_kernel_attributes.cpp b/test_conformance/api/test_kernel_attributes.cpp index f8c9ec06..86b3595c 100644 --- a/test_conformance/api/test_kernel_attributes.cpp +++ b/test_conformance/api/test_kernel_attributes.cpp @@ -336,3 +336,161 @@ REGISTER_TEST(kernel_attributes) } return success ? TEST_PASS : TEST_FAIL; } + +REGISTER_TEST(null_required_work_group_size) +{ + cl_int error = CL_SUCCESS; + + clGetKernelSuggestedLocalWorkSizeKHR_fn + clGetKernelSuggestedLocalWorkSizeKHR = nullptr; + if (is_extension_available(device, "cl_khr_suggested_local_work_size")) + { + cl_platform_id platform = nullptr; + error = clGetDeviceInfo(device, CL_DEVICE_PLATFORM, sizeof(platform), + &platform, NULL); + test_error(error, "clGetDeviceInfo for platform failed"); + + clGetKernelSuggestedLocalWorkSizeKHR = + (clGetKernelSuggestedLocalWorkSizeKHR_fn) + clGetExtensionFunctionAddressForPlatform( + platform, "clGetKernelSuggestedLocalWorkSizeKHR"); + test_assert_error(clGetKernelSuggestedLocalWorkSizeKHR != nullptr, + "Couldn't get function pointer for " + "clGetKernelSuggestedLocalWorkSizeKHR"); + } + + cl_uint device_max_dim = 0; + error = clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, + sizeof(device_max_dim), &device_max_dim, nullptr); + test_error(error, + "clGetDeviceInfo for CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS failed"); + test_assert_error(device_max_dim >= 3, + "CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS must be at least 3!"); + + std::vector device_max_work_item_sizes(device_max_dim); + error = clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_ITEM_SIZES, + sizeof(size_t) * device_max_dim, + device_max_work_item_sizes.data(), nullptr); + + size_t device_max_work_group_size = 0; + error = clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, + sizeof(device_max_work_group_size), + &device_max_work_group_size, nullptr); + test_error(error, + "clGetDeviceInfo for CL_DEVICE_MAX_WORK_GROUP_SIZE failed"); + + clMemWrapper dst; + dst = clCreateBuffer(context, CL_MEM_READ_WRITE, 3 * sizeof(cl_int), + nullptr, &error); + + struct KernelAttribInfo + { + std::string str; + cl_uint max_dim; + }; + + std::vector attribs; + attribs.push_back({ "__attribute__((reqd_work_group_size(2,1,1)))", 1 }); + attribs.push_back({ "__attribute__((reqd_work_group_size(2,3,1)))", 2 }); + attribs.push_back({ "__attribute__((reqd_work_group_size(2,3,4)))", 3 }); + + const std::string body_str = R"( + __kernel void wg_size(__global int* dst) + { + if (get_global_id(0) == 0 && + get_global_id(1) == 0 && + get_global_id(2) == 0) { + dst[0] = get_local_size(0); + dst[1] = get_local_size(1); + dst[2] = get_local_size(2); + } + } + )"; + + for (auto& attrib : attribs) + { + const std::string source_str = attrib.str + body_str; + const char* source = source_str.c_str(); + + clProgramWrapper program; + clKernelWrapper kernel; + error = create_single_kernel_helper(context, &program, &kernel, 1, + &source, "wg_size"); + test_error(error, "Unable to create test kernel"); + + error = clSetKernelArg(kernel, 0, sizeof(cl_mem), &dst); + test_error(error, "clSetKernelArg failed"); + + for (cl_uint work_dim = 1; work_dim <= attrib.max_dim; work_dim++) + { + const cl_int expected[3] = { 2, work_dim >= 2 ? 3 : 1, + work_dim >= 3 ? 4 : 1 }; + const size_t test_work_group_size = + expected[0] * expected[1] * expected[2]; + if ((size_t)expected[0] > device_max_work_item_sizes[0] + || (size_t)expected[1] > device_max_work_item_sizes[1] + || (size_t)expected[2] > device_max_work_item_sizes[2] + || test_work_group_size > device_max_work_group_size) + { + log_info("Skipping test for work_dim = %u: required work group " + "size (%i, %i, %i) (total %zu) exceeds device max " + "work group size (%zu, %zu, %zu) (total %zu)\n", + work_dim, expected[0], expected[1], expected[2], + test_work_group_size, device_max_work_item_sizes[0], + device_max_work_item_sizes[1], + device_max_work_item_sizes[2], + device_max_work_group_size); + continue; + } + + const cl_int zero = 0; + error = clEnqueueFillBuffer(queue, dst, &zero, sizeof(zero), 0, + sizeof(expected), 0, nullptr, nullptr); + + const size_t global_work_size[3] = { 2 * 32, 3 * 32, 4 * 32 }; + error = clEnqueueNDRangeKernel(queue, kernel, work_dim, nullptr, + global_work_size, nullptr, 0, + nullptr, nullptr); + test_error(error, "clEnqueueNDRangeKernel failed"); + + cl_int results[3] = { -1, -1, -1 }; + error = clEnqueueReadBuffer(queue, dst, CL_TRUE, 0, sizeof(results), + results, 0, nullptr, nullptr); + test_error(error, "clEnqueueReadBuffer failed"); + + if (results[0] != expected[0] || results[1] != expected[1] + || results[2] != expected[2]) + { + log_error("Executed local size mismatch with work_dim = %u: " + "Expected (%d,%d,%d) got (%d,%d,%d)\n", + work_dim, expected[0], expected[1], expected[2], + results[0], results[1], results[2]); + return TEST_FAIL; + } + + if (clGetKernelSuggestedLocalWorkSizeKHR != nullptr) + { + size_t suggested[3] = { 1, 1, 1 }; + error = clGetKernelSuggestedLocalWorkSizeKHR( + queue, kernel, work_dim, nullptr, global_work_size, + suggested); + test_error(error, + "clGetKernelSuggestedLocalWorkSizeKHR failed"); + + if ((cl_int)suggested[0] != expected[0] + || (cl_int)suggested[1] != expected[1] + || (cl_int)suggested[2] != expected[2]) + { + log_error("Suggested local size mismatch with work_dim = " + "%u: Expected (%d,%d,%d) got (%d,%d,%d)\n", + work_dim, expected[0], expected[1], expected[2], + (cl_int)suggested[0], (cl_int)suggested[1], + (cl_int)suggested[2]); + return TEST_FAIL; + } + } + } + } + + return TEST_PASS; +}