mirror of
https://github.com/KhronosGroup/OpenCL-CTS.git
synced 2026-03-19 06:09:01 +00:00
Initial command-buffer extension tests (#1368)
* Initial command-buffer tests Introduce some basic testing of the [cl_khr_command_buffer](https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_Ext.html#cl_khr_command_buffer) extension. This is intended as a starting point from which we can iteratively build up tests for the extension collaboratively. * Move tests into derived classes * Move tests from methods into derived classes implementing a `Run()` interface. * Fix memory leak when command_buffer isn't freed when a test is skipped. * Print correct error code for `CL_DEVICE_COMMAND_BUFFER_CAPABILITIES_KHR` * Pass `nullptr` for queue parameter to command recording entry-points * Define command-buffer type wrapper Other OpenCL object have a wrapper to reference count their use and free the wrapped object. The command-buffer object can't use the generic type wrappers which are templated on the appropriate release/retain function, as the release/retain functions are queried at runtime. Instead, define our own command-buffer wrapper class where a base object is passed on construction which contains function pointers to the release/retain functions that can be used in the wrapper. * Use create_single_kernel_helper_create_program Use `create_single_kernel_helper_create_program` rather than hardcoding `clCreateProgramWithSource` to allow for other types of program input. Also fix bug using wrong enum for passing properties on command-buffer creation, should be `CL_COMMAND_BUFFER_FLAGS_KHR` * Add out-of-order command-buffer test Introduce a basic test for checking sync-point use with out-of-order command-buffers. This also includes better checking of required queue properties.
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
add_subdirectory( cl_ext_cxx_for_opencl )
|
||||
add_subdirectory( cl_khr_command_buffer )
|
||||
add_subdirectory( cl_khr_dx9_media_sharing )
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
set(MODULE_NAME CL_KHR_COMMAND_BUFFER)
|
||||
|
||||
set(${MODULE_NAME}_SOURCES
|
||||
main.cpp
|
||||
basic_command_buffer.cpp
|
||||
)
|
||||
|
||||
include(../../CMakeCommon.txt)
|
||||
@@ -0,0 +1,588 @@
|
||||
//
|
||||
// Copyright (c) 2022 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 "command_buffer_test_base.h"
|
||||
#include "procs.h"
|
||||
#include "harness/typeWrappers.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#define CHECK_VERIFICATION_ERROR(reference, result, index) \
|
||||
{ \
|
||||
if (reference != result) \
|
||||
{ \
|
||||
log_error("Expected %d was %d at index %u\n", reference, result, \
|
||||
index); \
|
||||
return TEST_FAIL; \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper test fixture for constructing OpenCL objects used in testing
|
||||
// a variety of simple command-buffer enqueue scenarios.
|
||||
struct BasicCommandBufferTest : CommandBufferTestBase
|
||||
{
|
||||
|
||||
BasicCommandBufferTest(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue)
|
||||
: CommandBufferTestBase(device), context(context), queue(queue),
|
||||
command_buffer(this), simultaneous_use(false),
|
||||
out_of_order_support(false), num_elements(0)
|
||||
{}
|
||||
|
||||
virtual bool Skip()
|
||||
{
|
||||
cl_command_queue_properties required_properties;
|
||||
cl_int error = clGetDeviceInfo(
|
||||
device, CL_DEVICE_COMMAND_BUFFER_REQUIRED_QUEUE_PROPERTIES_KHR,
|
||||
sizeof(required_properties), &required_properties, NULL);
|
||||
test_error(error,
|
||||
"Unable to query "
|
||||
"CL_DEVICE_COMMAND_BUFFER_REQUIRED_QUEUE_PROPERTIES_KHR");
|
||||
|
||||
cl_command_queue_properties queue_properties;
|
||||
|
||||
error = clGetCommandQueueInfo(queue, CL_QUEUE_PROPERTIES,
|
||||
sizeof(queue_properties),
|
||||
&queue_properties, NULL);
|
||||
test_error(error, "Unable to query CL_QUEUE_PROPERTIES");
|
||||
|
||||
// Skip if queue properties don't contain those required
|
||||
return required_properties != (required_properties & queue_properties);
|
||||
}
|
||||
|
||||
virtual cl_int SetUp(int elements)
|
||||
{
|
||||
cl_int error = init_extension_functions();
|
||||
if (error != CL_SUCCESS)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
// Query if device supports simultaneous use
|
||||
cl_device_command_buffer_capabilities_khr capabilities;
|
||||
error =
|
||||
clGetDeviceInfo(device, CL_DEVICE_COMMAND_BUFFER_CAPABILITIES_KHR,
|
||||
sizeof(capabilities), &capabilities, NULL);
|
||||
test_error(error,
|
||||
"Unable to query CL_DEVICE_COMMAND_BUFFER_CAPABILITIES_KHR");
|
||||
simultaneous_use =
|
||||
capabilities & CL_COMMAND_BUFFER_CAPABILITY_SIMULTANEOUS_USE_KHR;
|
||||
out_of_order_support =
|
||||
capabilities & CL_COMMAND_BUFFER_CAPABILITY_OUT_OF_ORDER_KHR;
|
||||
|
||||
if (elements <= 0)
|
||||
{
|
||||
return CL_INVALID_VALUE;
|
||||
}
|
||||
num_elements = static_cast<size_t>(elements);
|
||||
|
||||
// Kernel performs a parallel copy from an input buffer to output buffer
|
||||
// is created.
|
||||
const char *kernel_str =
|
||||
R"(
|
||||
__kernel void copy(__global int* in, __global int* out) {
|
||||
size_t id = get_global_id(0);
|
||||
out[id] = in[id];
|
||||
})";
|
||||
|
||||
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);
|
||||
test_error(error, "Failed to build program");
|
||||
|
||||
in_mem = clCreateBuffer(context, CL_MEM_READ_ONLY,
|
||||
sizeof(cl_int) * num_elements, nullptr, &error);
|
||||
test_error(error, "clCreateBuffer failed");
|
||||
|
||||
out_mem =
|
||||
clCreateBuffer(context, CL_MEM_WRITE_ONLY,
|
||||
sizeof(cl_int) * num_elements, nullptr, &error);
|
||||
test_error(error, "clCreateBuffer failed");
|
||||
|
||||
kernel = clCreateKernel(program, "copy", &error);
|
||||
test_error(error, "Failed to create copy kernel");
|
||||
|
||||
error = clSetKernelArg(kernel, 0, sizeof(in_mem), &in_mem);
|
||||
test_error(error, "clSetKernelArg failed");
|
||||
|
||||
error = clSetKernelArg(kernel, 1, sizeof(out_mem), &out_mem);
|
||||
test_error(error, "clSetKernelArg failed");
|
||||
|
||||
if (simultaneous_use)
|
||||
{
|
||||
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);
|
||||
}
|
||||
test_error(error, "clCreateCommandBufferKHR failed");
|
||||
|
||||
return CL_SUCCESS;
|
||||
}
|
||||
|
||||
// Test body returning an OpenCL error code
|
||||
virtual cl_int Run() = 0;
|
||||
|
||||
|
||||
protected:
|
||||
size_t data_size() const { return num_elements * sizeof(cl_int); }
|
||||
|
||||
cl_context context;
|
||||
cl_command_queue queue;
|
||||
clCommandBufferWrapper command_buffer;
|
||||
clProgramWrapper program;
|
||||
clKernelWrapper kernel;
|
||||
clMemWrapper in_mem, out_mem;
|
||||
size_t num_elements;
|
||||
|
||||
// Device support query results
|
||||
bool simultaneous_use;
|
||||
bool out_of_order_support;
|
||||
};
|
||||
|
||||
// Test enqueuing a command-buffer containing a single NDRange command once
|
||||
struct BasicEnqueueTest : public BasicCommandBufferTest
|
||||
{
|
||||
using BasicCommandBufferTest::BasicCommandBufferTest;
|
||||
|
||||
cl_int Run() override
|
||||
{
|
||||
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");
|
||||
|
||||
const 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");
|
||||
|
||||
std::vector<cl_int> 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;
|
||||
}
|
||||
};
|
||||
|
||||
// Test enqueuing a command-buffer containing multiple command, including
|
||||
// operations other than NDRange kernel execution.
|
||||
struct MixedCommandsTest : public BasicCommandBufferTest
|
||||
{
|
||||
using BasicCommandBufferTest::BasicCommandBufferTest;
|
||||
|
||||
cl_int Run() override
|
||||
{
|
||||
cl_int error;
|
||||
const size_t iterations = 4;
|
||||
clMemWrapper result_mem =
|
||||
clCreateBuffer(context, CL_MEM_READ_WRITE,
|
||||
sizeof(cl_int) * iterations, nullptr, &error);
|
||||
test_error(error, "clCreateBuffer failed");
|
||||
|
||||
const cl_int pattern_base = 42;
|
||||
for (size_t i = 0; i < iterations; i++)
|
||||
{
|
||||
const cl_int pattern = pattern_base + i;
|
||||
cl_int error = clCommandFillBufferKHR(
|
||||
command_buffer, nullptr, in_mem, &pattern, sizeof(cl_int), 0,
|
||||
data_size(), 0, nullptr, nullptr, nullptr);
|
||||
test_error(error, "clCommandFillBufferKHR failed");
|
||||
|
||||
error = clCommandNDRangeKernelKHR(
|
||||
command_buffer, nullptr, nullptr, kernel, 1, nullptr,
|
||||
&num_elements, nullptr, 0, nullptr, nullptr, nullptr);
|
||||
test_error(error, "clCommandNDRangeKernelKHR failed");
|
||||
|
||||
const size_t result_offset = i * sizeof(cl_int);
|
||||
error = clCommandCopyBufferKHR(
|
||||
command_buffer, nullptr, out_mem, result_mem, 0, result_offset,
|
||||
sizeof(cl_int), 0, nullptr, nullptr, nullptr);
|
||||
test_error(error, "clCommandCopyBufferKHR failed");
|
||||
}
|
||||
|
||||
error = clFinalizeCommandBufferKHR(command_buffer);
|
||||
test_error(error, "clFinalizeCommandBufferKHR failed");
|
||||
|
||||
error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0,
|
||||
nullptr, nullptr);
|
||||
test_error(error, "clEnqueueCommandBufferKHR failed");
|
||||
|
||||
std::vector<cl_int> result_data(num_elements);
|
||||
error = clEnqueueReadBuffer(queue, result_mem, CL_TRUE, 0,
|
||||
iterations * sizeof(cl_int),
|
||||
result_data.data(), 0, nullptr, nullptr);
|
||||
test_error(error, "clEnqueueReadBuffer failed");
|
||||
|
||||
for (size_t i = 0; i < iterations; i++)
|
||||
{
|
||||
const cl_int ref = pattern_base + i;
|
||||
CHECK_VERIFICATION_ERROR(ref, result_data[i], i);
|
||||
}
|
||||
|
||||
return CL_SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
// Test enqueueing a command-buffer blocked on a user-event
|
||||
struct UserEventTest : public BasicCommandBufferTest
|
||||
{
|
||||
using BasicCommandBufferTest::BasicCommandBufferTest;
|
||||
|
||||
cl_int Run() override
|
||||
{
|
||||
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");
|
||||
|
||||
clEventWrapper user_event = clCreateUserEvent(context, &error);
|
||||
test_error(error, "clCreateUserEvent failed");
|
||||
|
||||
const 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, 1,
|
||||
&user_event, nullptr);
|
||||
test_error(error, "clEnqueueCommandBufferKHR failed");
|
||||
|
||||
std::vector<cl_int> output_data(num_elements);
|
||||
error = clEnqueueReadBuffer(queue, out_mem, CL_FALSE, 0, data_size(),
|
||||
output_data.data(), 0, nullptr, nullptr);
|
||||
test_error(error, "clEnqueueReadBuffer failed");
|
||||
|
||||
error = clSetUserEventStatus(user_event, CL_COMPLETE);
|
||||
test_error(error, "clSetUserEventStatus failed");
|
||||
|
||||
error = clFinish(queue);
|
||||
test_error(error, "clFinish failed");
|
||||
|
||||
for (size_t i = 0; i < num_elements; i++)
|
||||
{
|
||||
CHECK_VERIFICATION_ERROR(pattern, output_data[i], i);
|
||||
}
|
||||
|
||||
return CL_SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
// Test flushing the command-queue between command-buffer enqueues
|
||||
struct ExplicitFlushTest : public BasicCommandBufferTest
|
||||
{
|
||||
using BasicCommandBufferTest::BasicCommandBufferTest;
|
||||
|
||||
cl_int Run() override
|
||||
{
|
||||
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");
|
||||
|
||||
const cl_int pattern_A = 42;
|
||||
error = clEnqueueFillBuffer(queue, in_mem, &pattern_A, sizeof(cl_int),
|
||||
0, data_size(), 0, nullptr, nullptr);
|
||||
test_error(error, "clEnqueueFillBuffer failed");
|
||||
|
||||
error = clFlush(queue);
|
||||
test_error(error, "clFlush failed");
|
||||
|
||||
error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0,
|
||||
nullptr, nullptr);
|
||||
test_error(error, "clEnqueueCommandBufferKHR failed");
|
||||
|
||||
std::vector<cl_int> output_data_A(num_elements);
|
||||
error = clEnqueueReadBuffer(queue, out_mem, CL_FALSE, 0, data_size(),
|
||||
output_data_A.data(), 0, nullptr, nullptr);
|
||||
test_error(error, "clEnqueueReadBuffer failed");
|
||||
|
||||
const cl_int pattern_B = 0xA;
|
||||
error = clEnqueueFillBuffer(queue, in_mem, &pattern_B, sizeof(cl_int),
|
||||
0, data_size(), 0, nullptr, nullptr);
|
||||
test_error(error, "clEnqueueFillBuffer failed");
|
||||
|
||||
error = clFlush(queue);
|
||||
test_error(error, "clFlush failed");
|
||||
|
||||
error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0,
|
||||
nullptr, nullptr);
|
||||
test_error(error, "clEnqueueCommandBufferKHR failed");
|
||||
|
||||
error = clFlush(queue);
|
||||
test_error(error, "clFlush failed");
|
||||
|
||||
std::vector<cl_int> output_data_B(num_elements);
|
||||
error = clEnqueueReadBuffer(queue, out_mem, CL_FALSE, 0, data_size(),
|
||||
output_data_B.data(), 0, nullptr, nullptr);
|
||||
test_error(error, "clEnqueueReadBuffer failed");
|
||||
|
||||
error = clFinish(queue);
|
||||
test_error(error, "clFinish failed");
|
||||
|
||||
for (size_t i = 0; i < num_elements; i++)
|
||||
{
|
||||
CHECK_VERIFICATION_ERROR(pattern_A, output_data_A[i], i);
|
||||
|
||||
CHECK_VERIFICATION_ERROR(pattern_B, output_data_B[i], i);
|
||||
}
|
||||
return CL_SUCCESS;
|
||||
}
|
||||
|
||||
bool Skip() override
|
||||
{
|
||||
return !simultaneous_use || BasicCommandBufferTest::Skip();
|
||||
}
|
||||
};
|
||||
|
||||
// Test enqueueing a command-buffer twice separated by another enqueue operation
|
||||
struct InterleavedEnqueueTest : public BasicCommandBufferTest
|
||||
{
|
||||
using BasicCommandBufferTest::BasicCommandBufferTest;
|
||||
|
||||
cl_int Run() override
|
||||
{
|
||||
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");
|
||||
|
||||
pattern = 0xABCD;
|
||||
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");
|
||||
|
||||
error = clEnqueueCopyBuffer(queue, in_mem, out_mem, 0, 0, data_size(),
|
||||
0, nullptr, nullptr);
|
||||
test_error(error, "clEnqueueCopyBuffer failed");
|
||||
|
||||
std::vector<cl_int> 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;
|
||||
}
|
||||
|
||||
bool Skip() override
|
||||
{
|
||||
return !simultaneous_use || BasicCommandBufferTest::Skip();
|
||||
}
|
||||
};
|
||||
|
||||
// Test sync-points with an out-of-order command-buffer
|
||||
struct OutOfOrderTest : public BasicCommandBufferTest
|
||||
{
|
||||
using BasicCommandBufferTest::BasicCommandBufferTest;
|
||||
OutOfOrderTest(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue)
|
||||
: BasicCommandBufferTest(device, context, queue),
|
||||
out_of_order_command_buffer(this), out_of_order_queue(nullptr),
|
||||
event(nullptr)
|
||||
{}
|
||||
|
||||
cl_int Run() override
|
||||
{
|
||||
cl_sync_point_khr sync_points[2];
|
||||
|
||||
const cl_int pattern = 42;
|
||||
cl_int error =
|
||||
clCommandFillBufferKHR(out_of_order_command_buffer, nullptr, in_mem,
|
||||
&pattern, sizeof(cl_int), 0, data_size(), 0,
|
||||
nullptr, &sync_points[0], nullptr);
|
||||
test_error(error, "clCommandFillBufferKHR failed");
|
||||
|
||||
const cl_int overwritten_pattern = 0xACDC;
|
||||
error = clCommandFillBufferKHR(out_of_order_command_buffer, 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");
|
||||
|
||||
error = clEnqueueCommandBufferKHR(
|
||||
0, nullptr, out_of_order_command_buffer, 0, nullptr, &event);
|
||||
test_error(error, "clEnqueueCommandBufferKHR failed");
|
||||
|
||||
std::vector<cl_int> output_data(num_elements);
|
||||
error = clEnqueueReadBuffer(out_of_order_queue, out_mem, CL_TRUE, 0,
|
||||
data_size(), output_data.data(), 1, &event,
|
||||
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;
|
||||
}
|
||||
|
||||
cl_int SetUp(int elements) override
|
||||
{
|
||||
cl_int error = BasicCommandBufferTest::SetUp(elements);
|
||||
test_error(error, "BasicCommandBufferTest::SetUp failed");
|
||||
|
||||
if (!out_of_order_support)
|
||||
{
|
||||
// Test will skip as device doesn't support out-of-order
|
||||
// command-buffers
|
||||
return CL_SUCCESS;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool Skip() override
|
||||
{
|
||||
return !out_of_order_support || BasicCommandBufferTest::Skip();
|
||||
}
|
||||
|
||||
clCommandQueueWrapper out_of_order_queue;
|
||||
clCommandBufferWrapper out_of_order_command_buffer;
|
||||
clEventWrapper event;
|
||||
};
|
||||
|
||||
#undef CHECK_VERIFICATION_ERROR
|
||||
|
||||
template <class T>
|
||||
int MakeAndRunTest(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements)
|
||||
{
|
||||
CHECK_COMMAND_BUFFER_EXTENSION_AVAILABLE(device);
|
||||
|
||||
auto test_fixture = T(device, context, queue);
|
||||
cl_int error = test_fixture.SetUp(num_elements);
|
||||
test_error_ret(error, "Error in test initialization", TEST_FAIL);
|
||||
|
||||
if (test_fixture.Skip())
|
||||
{
|
||||
return TEST_SKIPPED_ITSELF;
|
||||
}
|
||||
|
||||
error = test_fixture.Run();
|
||||
test_error_ret(error, "Test Failed", TEST_FAIL);
|
||||
|
||||
return TEST_PASS;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
int test_single_ndrange(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements)
|
||||
{
|
||||
return MakeAndRunTest<BasicEnqueueTest>(device, context, queue,
|
||||
num_elements);
|
||||
}
|
||||
|
||||
int test_interleaved_enqueue(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements)
|
||||
{
|
||||
return MakeAndRunTest<InterleavedEnqueueTest>(device, context, queue,
|
||||
num_elements);
|
||||
}
|
||||
|
||||
int test_mixed_commands(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements)
|
||||
{
|
||||
return MakeAndRunTest<MixedCommandsTest>(device, context, queue,
|
||||
num_elements);
|
||||
}
|
||||
|
||||
int test_explicit_flush(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements)
|
||||
{
|
||||
return MakeAndRunTest<ExplicitFlushTest>(device, context, queue,
|
||||
num_elements);
|
||||
}
|
||||
|
||||
int test_user_events(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements)
|
||||
{
|
||||
return MakeAndRunTest<UserEventTest>(device, context, queue, num_elements);
|
||||
}
|
||||
|
||||
int test_out_of_order(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements)
|
||||
{
|
||||
return MakeAndRunTest<OutOfOrderTest>(device, context, queue, num_elements);
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// Copyright (c) 2022 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.
|
||||
|
||||
#ifndef _CL_KHR_COMMAND_BUFFER_TEST_BASE_H
|
||||
#define _CL_KHR_COMMAND_BUFFER_TEST_BASE_H
|
||||
|
||||
#include <CL/cl_ext.h>
|
||||
#include "harness/deviceInfo.h"
|
||||
#include "harness/testHarness.h"
|
||||
|
||||
|
||||
// Base class for setting function pointers to new extension entry points
|
||||
struct CommandBufferTestBase
|
||||
{
|
||||
CommandBufferTestBase(cl_device_id device): device(device) {}
|
||||
|
||||
cl_int init_extension_functions()
|
||||
{
|
||||
cl_platform_id platform;
|
||||
cl_int error =
|
||||
clGetDeviceInfo(device, CL_DEVICE_PLATFORM, sizeof(cl_platform_id),
|
||||
&platform, nullptr);
|
||||
test_error(error, "clGetDeviceInfo for CL_DEVICE_PLATFORM failed");
|
||||
|
||||
// If it is supported get the addresses of all the APIs here.
|
||||
#define GET_EXTENSION_ADDRESS(FUNC) \
|
||||
FUNC = reinterpret_cast<FUNC##_fn>( \
|
||||
clGetExtensionFunctionAddressForPlatform(platform, #FUNC)); \
|
||||
if (FUNC == nullptr) \
|
||||
{ \
|
||||
log_error("ERROR: clGetExtensionFunctionAddressForPlatform failed" \
|
||||
" with " #FUNC "\n"); \
|
||||
return TEST_FAIL; \
|
||||
}
|
||||
|
||||
GET_EXTENSION_ADDRESS(clCreateCommandBufferKHR);
|
||||
GET_EXTENSION_ADDRESS(clReleaseCommandBufferKHR);
|
||||
GET_EXTENSION_ADDRESS(clRetainCommandBufferKHR);
|
||||
GET_EXTENSION_ADDRESS(clFinalizeCommandBufferKHR);
|
||||
GET_EXTENSION_ADDRESS(clEnqueueCommandBufferKHR);
|
||||
GET_EXTENSION_ADDRESS(clCommandBarrierWithWaitListKHR);
|
||||
GET_EXTENSION_ADDRESS(clCommandCopyBufferKHR);
|
||||
GET_EXTENSION_ADDRESS(clCommandCopyBufferRectKHR);
|
||||
GET_EXTENSION_ADDRESS(clCommandCopyBufferToImageKHR);
|
||||
GET_EXTENSION_ADDRESS(clCommandCopyImageKHR);
|
||||
GET_EXTENSION_ADDRESS(clCommandCopyImageToBufferKHR);
|
||||
GET_EXTENSION_ADDRESS(clCommandFillBufferKHR);
|
||||
GET_EXTENSION_ADDRESS(clCommandFillImageKHR);
|
||||
GET_EXTENSION_ADDRESS(clCommandNDRangeKernelKHR);
|
||||
GET_EXTENSION_ADDRESS(clGetCommandBufferInfoKHR);
|
||||
#undef GET_EXTENSION_ADDRESS
|
||||
return CL_SUCCESS;
|
||||
}
|
||||
|
||||
clCreateCommandBufferKHR_fn clCreateCommandBufferKHR = nullptr;
|
||||
clReleaseCommandBufferKHR_fn clReleaseCommandBufferKHR = nullptr;
|
||||
clRetainCommandBufferKHR_fn clRetainCommandBufferKHR = nullptr;
|
||||
clFinalizeCommandBufferKHR_fn clFinalizeCommandBufferKHR = nullptr;
|
||||
clEnqueueCommandBufferKHR_fn clEnqueueCommandBufferKHR = nullptr;
|
||||
clCommandBarrierWithWaitListKHR_fn clCommandBarrierWithWaitListKHR =
|
||||
nullptr;
|
||||
clCommandCopyBufferKHR_fn clCommandCopyBufferKHR = nullptr;
|
||||
clCommandCopyBufferRectKHR_fn clCommandCopyBufferRectKHR = nullptr;
|
||||
clCommandCopyBufferToImageKHR_fn clCommandCopyBufferToImageKHR = nullptr;
|
||||
clCommandCopyImageKHR_fn clCommandCopyImageKHR = nullptr;
|
||||
clCommandCopyImageToBufferKHR_fn clCommandCopyImageToBufferKHR = nullptr;
|
||||
clCommandFillBufferKHR_fn clCommandFillBufferKHR = nullptr;
|
||||
clCommandFillImageKHR_fn clCommandFillImageKHR = nullptr;
|
||||
clCommandNDRangeKernelKHR_fn clCommandNDRangeKernelKHR = nullptr;
|
||||
clGetCommandBufferInfoKHR_fn clGetCommandBufferInfoKHR = nullptr;
|
||||
|
||||
cl_device_id device = nullptr;
|
||||
};
|
||||
|
||||
// Wrapper class based off generic typeWrappers.h wrappers. However, because
|
||||
// the release/retain functions are queried at runtime from the platform,
|
||||
// rather than known at compile time we cannot link the instantiated template.
|
||||
// Instead, pass an instance of `CommandBufferTestBase` on wrapper construction
|
||||
// to access the release/retain functions.
|
||||
class clCommandBufferWrapper {
|
||||
cl_command_buffer_khr object = nullptr;
|
||||
|
||||
void retain()
|
||||
{
|
||||
if (!object) return;
|
||||
|
||||
auto err = base->clRetainCommandBufferKHR(object);
|
||||
if (err != CL_SUCCESS)
|
||||
{
|
||||
print_error(err, "clRetainCommandBufferKHR() failed");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (!object) return;
|
||||
|
||||
auto err = base->clReleaseCommandBufferKHR(object);
|
||||
if (err != CL_SUCCESS)
|
||||
{
|
||||
print_error(err, "clReleaseCommandBufferKHR() failed");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
// Used to access release/retain functions
|
||||
CommandBufferTestBase *base;
|
||||
|
||||
public:
|
||||
// We always want to have base available to dereference
|
||||
clCommandBufferWrapper() = delete;
|
||||
|
||||
clCommandBufferWrapper(CommandBufferTestBase *base): base(base) {}
|
||||
|
||||
// On assignment, assume the object has a refcount of one.
|
||||
clCommandBufferWrapper &operator=(cl_command_buffer_khr rhs)
|
||||
{
|
||||
reset(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Copy semantics, increase retain count.
|
||||
clCommandBufferWrapper(clCommandBufferWrapper const &w) { *this = w; }
|
||||
clCommandBufferWrapper &operator=(clCommandBufferWrapper const &w)
|
||||
{
|
||||
reset(w.object);
|
||||
retain();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Move semantics, directly take ownership.
|
||||
clCommandBufferWrapper(clCommandBufferWrapper &&w) { *this = std::move(w); }
|
||||
clCommandBufferWrapper &operator=(clCommandBufferWrapper &&w)
|
||||
{
|
||||
reset(w.object);
|
||||
w.object = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~clCommandBufferWrapper() { reset(); }
|
||||
|
||||
// Release the existing object, if any, and own the new one, if any.
|
||||
void reset(cl_command_buffer_khr new_object = nullptr)
|
||||
{
|
||||
release();
|
||||
object = new_object;
|
||||
}
|
||||
|
||||
operator cl_command_buffer_khr() const { return object; }
|
||||
};
|
||||
|
||||
#define CHECK_COMMAND_BUFFER_EXTENSION_AVAILABLE(device) \
|
||||
{ \
|
||||
if (!is_extension_available(device, "cl_khr_command_buffer")) \
|
||||
{ \
|
||||
log_info( \
|
||||
"Device does not support 'cl_khr_command_buffer'. Skipping " \
|
||||
"the test.\n"); \
|
||||
return TEST_SKIPPED_ITSELF; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#endif // _CL_KHR_COMMAND_BUFFER_TEST_BASE_H
|
||||
35
test_conformance/extensions/cl_khr_command_buffer/main.cpp
Normal file
35
test_conformance/extensions/cl_khr_command_buffer/main.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2022 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 "procs.h"
|
||||
#include "harness/testHarness.h"
|
||||
|
||||
test_definition test_list[] = {
|
||||
ADD_TEST(single_ndrange), ADD_TEST(interleaved_enqueue),
|
||||
ADD_TEST(mixed_commands), ADD_TEST(explicit_flush),
|
||||
ADD_TEST(user_events), ADD_TEST(out_of_order)
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
// A device may report the required properties of a queue that
|
||||
// is compatible with command-buffers via the query
|
||||
// CL_DEVICE_COMMAND_BUFFER_REQUIRED_QUEUE_PROPERTIES_KHR. We account
|
||||
// for this in the tests themselves, rather than here, where we have a
|
||||
// device to query.
|
||||
const cl_command_queue_properties queue_properties = 0;
|
||||
return runTestHarnessWithCheck(argc, argv, ARRAY_SIZE(test_list), test_list,
|
||||
false, queue_properties, nullptr);
|
||||
}
|
||||
35
test_conformance/extensions/cl_khr_command_buffer/procs.h
Normal file
35
test_conformance/extensions/cl_khr_command_buffer/procs.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright (c) 2022 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.
|
||||
//
|
||||
#ifndef _CL_KHR_COMMAND_BUFFER_PROCS_H
|
||||
#define _CL_KHR_COMMAND_BUFFER_PROCS_H
|
||||
|
||||
#include <CL/cl.h>
|
||||
|
||||
// Basic command-buffer tests
|
||||
extern int test_single_ndrange(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements);
|
||||
extern int test_interleaved_enqueue(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements);
|
||||
extern int test_mixed_commands(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements);
|
||||
extern int test_explicit_flush(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements);
|
||||
extern int test_user_events(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements);
|
||||
extern int test_out_of_order(cl_device_id device, cl_context context,
|
||||
cl_command_queue queue, int num_elements);
|
||||
|
||||
#endif /*_CL_KHR_COMMAND_BUFFER_PROCS_H*/
|
||||
Reference in New Issue
Block a user