From 5997a00b2f048670c52b07830c72bad6be546cab Mon Sep 17 00:00:00 2001 From: Ewan Crawford Date: Tue, 8 Jul 2025 00:35:20 +0100 Subject: [PATCH] Test releasing a command-buffer after submission but before execution has finished (#2414) Add cl_khr_command_buffer test that is it valid to release a command-buffer after it has been enqueued but before execution is finished. This stresses the semantics from [clReleaseCommandBufferKHR](https://registry.khronos.org/OpenCL/sdk/3.0/docs/man/html/clReleaseCommandBufferKHR.html#_description) that: "After the command_buffer reference count becomes zero **and has finished execution**, the command-buffer is deleted" --- .../basic_command_buffer.cpp | 37 +++++++++++++++++++ .../basic_command_buffer.h | 9 +++++ .../basic_command_buffer_tests.cpp | 6 +++ 3 files changed, 52 insertions(+) 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 43926b84..9c3a402b 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 @@ -435,3 +435,40 @@ bool InterleavedEnqueueTest::Skip() { return BasicCommandBufferTest::Skip() || !simultaneous_use_support; } + +cl_int EnqueueAndReleaseTest::Run() +{ + 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"); + + cl_int pattern = 42; + error = clEnqueueFillBuffer(queue, in_mem, &pattern, sizeof(cl_int), 0, + data_size(), 0, nullptr, nullptr); + test_error(error, "clEnqueueFillBuffer failed"); + + error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0, nullptr, + nullptr); + test_error(error, "clEnqueueCommandBufferKHR failed"); + + // Calls release on cl_command_buffer_khr handle inside wrapper class, and + // sets the handle to nullptr, so that release doesn't get called again at + // end of test when wrapper object is destroyed. + command_buffer.reset(); + + 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"); + + for (size_t i = 0; i < num_elements; i++) + { + CHECK_VERIFICATION_ERROR(pattern, output_data[i], i); + } + + return CL_SUCCESS; +} 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 7ad7d28d..241a08c5 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 @@ -128,6 +128,15 @@ struct InterleavedEnqueueTest : public BasicCommandBufferTest bool Skip() override; }; +// Test releasing a command-buffer after it has been submitted for execution, +// but before the user has waited on completion of the enqueue. +struct EnqueueAndReleaseTest : public BasicCommandBufferTest +{ + using BasicCommandBufferTest::BasicCommandBufferTest; + + cl_int Run() override; +}; + template int MakeAndRunTest(cl_device_id device, cl_context context, cl_command_queue queue, int num_elements) diff --git a/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer_tests.cpp b/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer_tests.cpp index 0f95372a..69d554c4 100644 --- a/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer_tests.cpp +++ b/test_conformance/extensions/cl_khr_command_buffer/basic_command_buffer_tests.cpp @@ -44,3 +44,9 @@ REGISTER_TEST(explicit_flush) return MakeAndRunTest(device, context, queue, num_elements); } + +REGISTER_TEST(enqueue_and_release) +{ + return MakeAndRunTest(device, context, queue, + num_elements); +}