mirror of
https://github.com/KhronosGroup/OpenCL-CTS.git
synced 2026-03-19 06:09:01 +00:00
The maintenance of the conformance tests is moving to Github. This commit contains all the changes that have been done in Gitlab since the first public release of the conformance tests. Signed-off-by: Kevin Petit <kevin.petit@arm.com>
423 lines
12 KiB
C++
423 lines
12 KiB
C++
//
|
|
// 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
|
|
//
|
|
// 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 "../../test_common/harness/compat.h"
|
|
|
|
#ifdef __APPLE__
|
|
#include <OpenCL/opencl.h>
|
|
#else
|
|
#include <CL/cl.h>
|
|
#endif
|
|
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <assert.h>
|
|
#include <functional>
|
|
#include <memory>
|
|
|
|
#include "../../test_common/harness/errorHelpers.h"
|
|
#include "../../test_common/harness/kernelHelpers.h"
|
|
#include "../../test_common/harness/typeWrappers.h"
|
|
#include "../../test_common/harness/clImageHelper.h"
|
|
#include "../../test_common/harness/os_helpers.h"
|
|
|
|
#include "exceptions.h"
|
|
#include "kernelargs.h"
|
|
#include "datagen.h"
|
|
#include "run_services.h"
|
|
#include "run_build_test.h"
|
|
|
|
//
|
|
// Task
|
|
//
|
|
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();
|
|
}
|
|
|
|
void Task::setErrorLog(cl_program prog) {
|
|
size_t len = 0;
|
|
std::vector<char> log;
|
|
|
|
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";
|
|
return;
|
|
}
|
|
|
|
log.resize(len, 0);
|
|
|
|
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";
|
|
return;
|
|
}
|
|
m_log.append(&log[0]);
|
|
}
|
|
|
|
//
|
|
// BuildTask
|
|
//
|
|
BuildTask::BuildTask(cl_program prog, cl_device_id dev, const char* options) :
|
|
m_program(prog), Task(dev, options) {}
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// SpirBuildTask
|
|
//
|
|
SpirBuildTask::SpirBuildTask(cl_program prog, cl_device_id dev, const char* options) :
|
|
BuildTask(prog, dev, options) {}
|
|
|
|
//
|
|
// CompileTask
|
|
//
|
|
|
|
CompileTask::CompileTask(cl_program prog, cl_device_id dev, const char* options) :
|
|
m_program(prog), Task(dev, options) {}
|
|
|
|
void CompileTask::addHeader(const char* hname, cl_program hprog) {
|
|
m_headers.push_back(std::make_pair(hname, hprog));
|
|
}
|
|
|
|
const char* first(std::pair<const char*,cl_program>& p) {
|
|
return p.first;
|
|
}
|
|
|
|
cl_program second(const std::pair<const char*, cl_program>& p) {
|
|
return p.second;
|
|
}
|
|
|
|
bool CompileTask::execute() {
|
|
// Generating the header names vector.
|
|
std::vector<const char*> names;
|
|
std::transform(m_headers.begin(), m_headers.end(), names.begin(), first);
|
|
|
|
// Generating the header programs vector.
|
|
std::vector<cl_program> programs;
|
|
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];
|
|
}
|
|
|
|
// 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;
|
|
|
|
setErrorLog(m_program);
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// SpirCompileTask
|
|
//
|
|
SpirCompileTask::SpirCompileTask(cl_program prog, cl_device_id dev, const char* options) :
|
|
CompileTask(prog, dev, options) {}
|
|
|
|
|
|
//
|
|
// LinkTask
|
|
//
|
|
LinkTask::LinkTask(cl_program* programs, int num_programs, cl_context ctxt,
|
|
cl_device_id dev, const char* options) :
|
|
m_programs(programs), m_numPrograms(num_programs), m_context(ctxt), m_executable(NULL),
|
|
Task(dev, options) {}
|
|
|
|
bool LinkTask::execute() {
|
|
cl_int err_code;
|
|
int 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);
|
|
if (CL_SUCCESS != err_code)
|
|
{
|
|
setErrorLog(m_programs[i]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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);
|
|
return false;
|
|
}
|
|
|
|
cl_program LinkTask::getExecutable() const {
|
|
return m_executable;
|
|
}
|
|
|
|
LinkTask::~LinkTask() {
|
|
if(m_executable) clReleaseProgram(m_executable);
|
|
}
|
|
|
|
//
|
|
// KernelEnumerator
|
|
//
|
|
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;
|
|
|
|
// Querying for the number of kernels.
|
|
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;
|
|
|
|
//parsing the names and inserting them to the list
|
|
std::string names(kernel_names);
|
|
assert (str_len == 1+names.size() && "incompatible string lengths");
|
|
size_t offset = 0;
|
|
for(size_t i=0 ; i<names.size() ; ++i){
|
|
//kernel names are separated by semi colons
|
|
if (names[i] == ';'){
|
|
m_kernels.push_back(names.substr(offset, i-offset));
|
|
offset = i+1;
|
|
}
|
|
}
|
|
m_kernels.push_back(names.substr(offset, names.size()-offset));
|
|
delete[] kernel_names;
|
|
}
|
|
|
|
KernelEnumerator::KernelEnumerator(cl_program prog) {
|
|
process(prog);
|
|
}
|
|
|
|
KernelEnumerator::iterator KernelEnumerator::begin(){
|
|
return m_kernels.begin();
|
|
}
|
|
|
|
KernelEnumerator::iterator KernelEnumerator::end(){
|
|
return m_kernels.end();
|
|
}
|
|
|
|
size_t KernelEnumerator::size() const {
|
|
return m_kernels.size();
|
|
}
|
|
|
|
/**
|
|
Run the single test - run the test for both CL and SPIR versions of the kernel
|
|
*/
|
|
static bool run_test(cl_context context, cl_command_queue queue, cl_program clprog,
|
|
cl_program bcprog, const std::string& kernel_name, std::string& err, const cl_device_id device)
|
|
{
|
|
WorkSizeInfo ws;
|
|
TestResult cl_result;
|
|
std::auto_ptr<TestResult> bc_result;
|
|
// first, run the single CL test
|
|
{
|
|
// make sure that the kernel will be released before the program
|
|
clKernelWrapper kernel = create_kernel_helper(clprog, kernel_name);
|
|
// based on the kernel characteristics, we are generating and initializing the arguments for both phases (cl and bc executions)
|
|
generate_kernel_data(context, kernel, ws, cl_result);
|
|
bc_result.reset(cl_result.clone(context, ws, kernel, device));
|
|
assert (compare_results(cl_result, *bc_result) && "not equal?");
|
|
run_kernel( kernel, queue, ws, cl_result );
|
|
}
|
|
// now, run the single BC test
|
|
{
|
|
// make sure that the kernel will be released before the program
|
|
clKernelWrapper kernel = create_kernel_helper(bcprog, kernel_name);
|
|
run_kernel( kernel, queue, ws, *bc_result );
|
|
}
|
|
|
|
int error = clFinish(queue);
|
|
if( CL_SUCCESS != error)
|
|
{
|
|
err = "clFinish failed\n";
|
|
return false;
|
|
}
|
|
|
|
// compare the results
|
|
if( !compare_results(cl_result, *bc_result) )
|
|
{
|
|
err = " (result diff in kernel '" + kernel_name + "').";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
int failures = 0;
|
|
// Composing the name of the CSV file.
|
|
char* dir = get_exe_dir();
|
|
std::string csvName(dir);
|
|
csvName.append(dir_sep());
|
|
csvName.append("khr.csv");
|
|
free(dir);
|
|
|
|
log_info("%s...\n", test_name);
|
|
|
|
// Figure out whether the test can run on the device. If not, we skip it.
|
|
const KhrSupport& khrDb = *KhrSupport::get(csvName);
|
|
cl_bool images = khrDb.isImagesRequired(folder, test_name);
|
|
cl_bool images3D = khrDb.isImages3DRequired(folder, test_name);
|
|
|
|
char deviceProfile[64];
|
|
clGetDeviceInfo(device, CL_DEVICE_PROFILE, sizeof(deviceProfile), &deviceProfile, NULL);
|
|
std::string device_profile(deviceProfile, 64);
|
|
|
|
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;
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
return true;
|
|
}
|
|
|
|
OclExtensions requiredExt = khrDb.getRequiredExtensions(folder, test_name);
|
|
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;
|
|
return true;
|
|
}
|
|
|
|
std::string cl_file_path, bc_file;
|
|
// Build cl file name based on the test name
|
|
get_cl_file_path(folder, test_name, cl_file_path);
|
|
// Build bc file name based on the test name
|
|
get_bc_file_path(folder, test_name, bc_file, size_t_width);
|
|
gRG.init(1);
|
|
//
|
|
// Processing each kernel in the program separately
|
|
//
|
|
clContextWrapper context;
|
|
clCommandQueueWrapper queue;
|
|
create_context_and_queue(device, &context, &queue);
|
|
clProgramWrapper clprog = create_program_from_cl(context, cl_file_path);
|
|
clProgramWrapper bcprog = create_program_from_bc(context, bc_file);
|
|
|
|
// Building the programs.
|
|
BuildTask clBuild(clprog, device, "-cl-kernel-arg-info");
|
|
if (!clBuild.execute()) {
|
|
std::cerr << clBuild.getErrorLog() << std::endl;
|
|
return false;
|
|
}
|
|
|
|
SpirBuildTask bcBuild(bcprog, device, "-x spir -spir-std=1.2 -cl-kernel-arg-info");
|
|
if (!bcBuild.execute()) {
|
|
std::cerr << bcBuild.getErrorLog() << std::endl;
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
return false;
|
|
}
|
|
KernelEnumerator::iterator it = clkernel_enumerator.begin(),
|
|
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);
|
|
if (success)
|
|
{
|
|
log_info("kernel '%s' passed.\n", kernel_name.c_str());
|
|
(*m_successHandler)(test_name, kernel_name);
|
|
}
|
|
else
|
|
{
|
|
++failures;
|
|
log_info("kernel '%s' failed.\n", kernel_name.c_str());
|
|
(*m_failureHandler)(test_name, kernel_name);
|
|
}
|
|
}
|
|
catch (std::runtime_error err)
|
|
{
|
|
++failures;
|
|
log_info("kernel '%s' failed: %s\n", kernel_name.c_str(), err.what());
|
|
(*m_failureHandler)(test_name, kernel_name);
|
|
}
|
|
}
|
|
|
|
log_info("%s %s\n", test_name, failures ? "FAILED" : "passed.");
|
|
return failures == 0;
|
|
}
|
|
|