| // |
| // 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 "crc32.h" |
| #include "kernelHelpers.h" |
| #include "deviceInfo.h" |
| #include "errorHelpers.h" |
| #include "imageHelpers.h" |
| #include "typeWrappers.h" |
| #include "testHarness.h" |
| #include "parseParameters.h" |
| |
| #include <cassert> |
| #include <vector> |
| #include <string> |
| #include <fstream> |
| #include <sstream> |
| #include <iomanip> |
| |
| #if defined(_WIN32) |
| std::string slash = "\\"; |
| #else |
| std::string slash = "/"; |
| #endif |
| |
| static cl_int get_first_device_id(const cl_context context, cl_device_id &device); |
| |
| long get_file_size(const std::string &fileName) |
| { |
| std::ifstream ifs(fileName.c_str(), std::ios::binary); |
| if (!ifs.good()) |
| return 0; |
| // get length of file: |
| ifs.seekg(0, std::ios::end); |
| std::ios::pos_type length = ifs.tellg(); |
| return static_cast<long>(length); |
| } |
| |
| static std::string get_kernel_content(unsigned int numKernelLines, const char *const *kernelProgram) |
| { |
| std::string kernel; |
| for (size_t i = 0; i < numKernelLines; ++i) |
| { |
| std::string chunk(kernelProgram[i], 0, std::string::npos); |
| kernel += chunk; |
| } |
| |
| return kernel; |
| } |
| |
| std::string get_kernel_name(const std::string &source) |
| { |
| // Create list of kernel names |
| std::string kernelsList; |
| size_t kPos = source.find("kernel"); |
| while (kPos != std::string::npos) |
| { |
| // check for '__kernel' |
| size_t pos = kPos; |
| if (pos >= 2 && source[pos - 1] == '_' && source[pos - 2] == '_') |
| pos -= 2; |
| |
| //check character before 'kernel' (white space expected) |
| size_t wsPos = source.find_last_of(" \t\r\n", pos); |
| if (wsPos == std::string::npos || wsPos + 1 == pos) |
| { |
| //check character after 'kernel' (white space expected) |
| size_t akPos = kPos + sizeof("kernel") - 1; |
| wsPos = source.find_first_of(" \t\r\n", akPos); |
| if (!(wsPos == akPos)) |
| { |
| kPos = source.find("kernel", kPos + 1); |
| continue; |
| } |
| |
| bool attributeFound; |
| do |
| { |
| attributeFound = false; |
| // find '(' after kernel name name |
| size_t pPos = source.find("(", akPos); |
| if (!(pPos != std::string::npos)) |
| continue; |
| |
| // check for not empty kernel name before '(' |
| pos = source.find_last_not_of(" \t\r\n", pPos - 1); |
| if (!(pos != std::string::npos && pos > akPos)) |
| continue; |
| |
| //find character before kernel name |
| wsPos = source.find_last_of(" \t\r\n", pos); |
| if (!(wsPos != std::string::npos && wsPos >= akPos)) |
| continue; |
| |
| std::string name = source.substr(wsPos + 1, pos + 1 - (wsPos + 1)); |
| //check for kernel attribute |
| if (name == "__attribute__") |
| { |
| attributeFound = true; |
| int pCount = 1; |
| akPos = pPos + 1; |
| while (pCount > 0 && akPos != std::string::npos) |
| { |
| akPos = source.find_first_of("()", akPos + 1); |
| if (akPos != std::string::npos) |
| { |
| if (source[akPos] == '(') |
| pCount++; |
| else |
| pCount--; |
| } |
| } |
| } |
| else |
| { |
| kernelsList += name + "."; |
| } |
| } while (attributeFound); |
| } |
| kPos = source.find("kernel", kPos + 1); |
| } |
| std::ostringstream oss; |
| if (MAX_LEN_FOR_KERNEL_LIST > 0) |
| { |
| if (kernelsList.size() > MAX_LEN_FOR_KERNEL_LIST + 1) |
| { |
| kernelsList = kernelsList.substr(0, MAX_LEN_FOR_KERNEL_LIST + 1); |
| kernelsList[kernelsList.size() - 1] = '.'; |
| kernelsList[kernelsList.size() - 1] = '.'; |
| } |
| oss << kernelsList; |
| } |
| return oss.str(); |
| } |
| |
| static std::string get_offline_compilation_file_type_str(const CompilationMode compilationMode) |
| { |
| switch (compilationMode) |
| { |
| default: |
| assert(0 && "Invalid compilation mode"); |
| abort(); |
| case kOnline: |
| assert(0 && "Invalid compilation mode for offline compilation"); |
| abort(); |
| case kBinary: |
| return "binary"; |
| case kSpir_v: |
| return "SPIR-V"; |
| } |
| } |
| |
| static std::string get_unique_filename_prefix(unsigned int numKernelLines, |
| const char *const *kernelProgram, |
| const char *buildOptions) |
| { |
| std::string kernel = get_kernel_content(numKernelLines, kernelProgram); |
| std::string kernelName = get_kernel_name(kernel); |
| cl_uint kernelCrc = crc32(kernel.data(), kernel.size()); |
| std::ostringstream oss; |
| oss << kernelName << std::hex << std::setfill('0') << std::setw(8) << kernelCrc; |
| if(buildOptions) { |
| cl_uint bOptionsCrc = crc32(buildOptions, strlen(buildOptions)); |
| oss << '.' << std::hex << std::setfill('0') << std::setw(8) << bOptionsCrc; |
| } |
| return oss.str(); |
| } |
| |
| |
| static std::string |
| get_cl_build_options_filename_with_path(const std::string& filePath, |
| const std::string& fileNamePrefix) { |
| return filePath + slash + fileNamePrefix + ".options"; |
| } |
| |
| static std::string |
| get_cl_source_filename_with_path(const std::string& filePath, |
| const std::string& fileNamePrefix) { |
| return filePath + slash + fileNamePrefix + ".cl"; |
| } |
| |
| static std::string |
| get_binary_filename_with_path(CompilationMode mode, |
| cl_uint deviceAddrSpaceSize, |
| const std::string& filePath, |
| const std::string& fileNamePrefix) { |
| std::string binaryFilename = filePath + slash + fileNamePrefix; |
| if(kSpir_v == mode) { |
| std::ostringstream extension; |
| extension << ".spv" << deviceAddrSpaceSize; |
| binaryFilename += extension.str(); |
| } |
| return binaryFilename; |
| } |
| |
| static bool file_exist_on_disk(const std::string& filePath, |
| const std::string& fileName) { |
| std::string fileNameWithPath = filePath + slash + fileName; |
| bool exist = false; |
| std::ifstream ifs; |
| |
| ifs.open(fileNameWithPath.c_str(), std::ios::binary); |
| if(ifs.good()) |
| exist = true; |
| ifs.close(); |
| return exist; |
| } |
| |
| static bool should_save_kernel_source_to_disk(CompilationMode mode, |
| CompilationCacheMode cacheMode, |
| const std::string& binaryPath, |
| const std::string& binaryName) |
| { |
| bool saveToDisk = false; |
| if(cacheMode == kCacheModeDumpCl || |
| (cacheMode == kCacheModeOverwrite && mode != kOnline)) { |
| saveToDisk = true; |
| } |
| if(cacheMode == kCacheModeCompileIfAbsent && mode != kOnline) { |
| saveToDisk = !file_exist_on_disk(binaryPath, binaryName); |
| } |
| return saveToDisk; |
| } |
| |
| static int save_kernel_build_options_to_disk(const std::string& path, |
| const std::string& prefix, |
| const char *buildOptions) { |
| std::string filename = get_cl_build_options_filename_with_path(path, prefix); |
| std::ofstream ofs(filename.c_str(), std::ios::binary); |
| if (!ofs.good()) |
| { |
| log_info("Can't save kernel build options: %s\n", filename.c_str()); |
| return -1; |
| } |
| ofs.write(buildOptions, strlen(buildOptions)); |
| ofs.close(); |
| log_info("Saved kernel build options to file: %s\n", filename.c_str()); |
| return CL_SUCCESS; |
| } |
| |
| static int save_kernel_source_to_disk(const std::string& path, |
| const std::string& prefix, |
| const std::string& source) { |
| std::string filename = get_cl_source_filename_with_path(path, prefix); |
| std::ofstream ofs(filename.c_str(), std::ios::binary); |
| if (!ofs.good()) |
| { |
| log_info("Can't save kernel source: %s\n", filename.c_str()); |
| return -1; |
| } |
| ofs.write(source.c_str(), source.size()); |
| ofs.close(); |
| log_info("Saved kernel source to file: %s\n", filename.c_str()); |
| return CL_SUCCESS; |
| } |
| |
| static int save_kernel_source_and_options_to_disk(unsigned int numKernelLines, |
| const char *const *kernelProgram, |
| const char *buildOptions) |
| { |
| int error; |
| |
| std::string kernel = get_kernel_content(numKernelLines, kernelProgram); |
| std::string kernelNamePrefix = get_unique_filename_prefix(numKernelLines, |
| kernelProgram, |
| buildOptions); |
| |
| // save kernel source to disk |
| error = save_kernel_source_to_disk(gCompilationCachePath, kernelNamePrefix, kernel); |
| |
| // save kernel build options to disk if exists |
| if (buildOptions != NULL) |
| error |= save_kernel_build_options_to_disk(gCompilationCachePath, kernelNamePrefix, buildOptions); |
| |
| return error; |
| } |
| |
| static std::string get_compilation_mode_str(const CompilationMode compilationMode) |
| { |
| switch (compilationMode) |
| { |
| default: |
| assert(0 && "Invalid compilation mode"); |
| abort(); |
| case kOnline: |
| return "online"; |
| case kBinary: |
| return "binary"; |
| case kSpir_v: |
| return "spir-v"; |
| } |
| } |
| |
| #ifdef KHRONOS_OFFLINE_COMPILER |
| static std::string get_khronos_compiler_command(const cl_uint device_address_space_size, |
| const bool openclCXX, |
| const std::string &bOptions, |
| const std::string &sourceFilename, |
| const std::string &outputFilename) |
| { |
| // Set compiler options |
| // Emit SPIR-V |
| std::string compilerOptions = " -cc1 -emit-spirv"; |
| // <triple>: for 32 bit SPIR-V use spir-unknown-unknown, for 64 bit SPIR-V use spir64-unknown-unknown. |
| if(device_address_space_size == 32) |
| { |
| compilerOptions += " -triple=spir-unknown-unknown"; |
| } |
| else |
| { |
| compilerOptions += " -triple=spir64-unknown-unknown"; |
| } |
| // Set OpenCL C++ flag required by SPIR-V-ready clang (compiler provided by Khronos) |
| if(openclCXX) |
| { |
| compilerOptions = compilerOptions + " -cl-std=c++"; |
| } |
| // Set correct includes |
| if(openclCXX) |
| { |
| compilerOptions += " -I "; |
| compilerOptions += STRINGIFY_VALUE(CL_LIBCLCXX_DIR); |
| } |
| else |
| { |
| compilerOptions += " -include opencl.h"; |
| } |
| |
| #ifdef KHRONOS_OFFLINE_COMPILER_OPTIONS |
| compilerOptions += STRINGIFY_VALUE(KHRONOS_OFFLINE_COMPILER_OPTIONS); |
| #endif |
| |
| // Add build options passed to this function |
| compilerOptions += " " + bOptions; |
| compilerOptions += |
| " " + sourceFilename + |
| " -o " + outputFilename; |
| std::string runString = STRINGIFY_VALUE(KHRONOS_OFFLINE_COMPILER) + compilerOptions; |
| |
| return runString; |
| } |
| #endif // KHRONOS_OFFLINE_COMPILER |
| |
| static cl_int get_cl_device_info_str(const cl_device_id device, const cl_uint device_address_space_size, |
| const CompilationMode compilationMode, std::string &clDeviceInfo) |
| { |
| std::string extensionsString = get_device_extensions_string(device); |
| std::string versionString = get_device_version_string(device); |
| |
| std::ostringstream clDeviceInfoStream; |
| std::string file_type = get_offline_compilation_file_type_str(compilationMode); |
| clDeviceInfoStream << "# OpenCL device info affecting " << file_type << " offline compilation:" << std::endl |
| << "CL_DEVICE_ADDRESS_BITS=" << device_address_space_size << std::endl |
| << "CL_DEVICE_EXTENSIONS=\"" << extensionsString << "\"" << std::endl; |
| /* We only need the device's supported IL version(s) when compiling IL |
| * that will be loaded with clCreateProgramWithIL() */ |
| if (compilationMode == kSpir_v) |
| { |
| std::string ilVersionString = get_device_il_version_string(device); |
| clDeviceInfoStream << "CL_DEVICE_IL_VERSION=\"" << ilVersionString << "\"" << std::endl; |
| } |
| clDeviceInfoStream << "CL_DEVICE_VERSION=\"" << versionString << "\"" << std::endl; |
| clDeviceInfoStream << "CL_DEVICE_IMAGE_SUPPORT=" |
| << (0 == checkForImageSupport(device)) << std::endl; |
| clDeviceInfoStream << "CL_DEVICE_NAME=\"" << get_device_name(device).c_str() |
| << "\"" << std::endl; |
| |
| clDeviceInfo = clDeviceInfoStream.str(); |
| |
| return CL_SUCCESS; |
| } |
| |
| static int write_cl_device_info(const cl_device_id device, const cl_uint device_address_space_size, |
| const CompilationMode compilationMode, std::string &clDeviceInfoFilename) |
| { |
| std::string clDeviceInfo; |
| int error = get_cl_device_info_str(device, device_address_space_size, compilationMode, clDeviceInfo); |
| if (error != CL_SUCCESS) |
| { |
| return error; |
| } |
| |
| cl_uint crc = crc32(clDeviceInfo.data(), clDeviceInfo.size()); |
| |
| /* Get the filename for the clDeviceInfo file. |
| * Note: the file includes the hash on its content, so it is usually unnecessary to delete it. */ |
| std::ostringstream clDeviceInfoFilenameStream; |
| clDeviceInfoFilenameStream << gCompilationCachePath << slash << "clDeviceInfo-"; |
| clDeviceInfoFilenameStream << std::hex << std::setfill('0') << std::setw(8) << crc << ".txt"; |
| |
| clDeviceInfoFilename = clDeviceInfoFilenameStream.str(); |
| |
| if ((size_t) get_file_size(clDeviceInfoFilename) == clDeviceInfo.size()) |
| { |
| /* The CL device info file has already been created. |
| * Nothing to do. */ |
| return 0; |
| } |
| |
| /* The file does not exist or its length is not as expected. Create/overwrite it. */ |
| std::ofstream ofs(clDeviceInfoFilename); |
| if (!ofs.good()) |
| { |
| log_info("OfflineCompiler: can't create CL device info file: %s\n", clDeviceInfoFilename.c_str()); |
| return -1; |
| } |
| ofs << clDeviceInfo; |
| ofs.close(); |
| |
| return CL_SUCCESS; |
| } |
| |
| static std::string get_offline_compilation_command(const cl_uint device_address_space_size, |
| const CompilationMode compilationMode, |
| const std::string &bOptions, |
| const std::string &sourceFilename, |
| const std::string &outputFilename, |
| const std::string &clDeviceInfoFilename) |
| { |
| std::ostringstream wrapperOptions; |
| |
| wrapperOptions << gCompilationProgram |
| << " --mode=" << get_compilation_mode_str(compilationMode) |
| << " --source=" << sourceFilename |
| << " --output=" << outputFilename |
| << " --cl-device-info=" << clDeviceInfoFilename; |
| |
| if (bOptions != "") |
| { |
| // Add build options passed to this function |
| wrapperOptions << " -- " << bOptions; |
| } |
| |
| return wrapperOptions.str(); |
| } |
| |
| static int invoke_offline_compiler(const cl_device_id device, |
| const cl_uint device_address_space_size, |
| const CompilationMode compilationMode, |
| const std::string &bOptions, |
| const std::string &sourceFilename, |
| const std::string &outputFilename, |
| const bool openclCXX) |
| { |
| std::string runString; |
| if (openclCXX) |
| { |
| #ifndef KHRONOS_OFFLINE_COMPILER |
| log_error("CL C++ compilation is not possible: KHRONOS_OFFLINE_COMPILER was not defined.\n"); |
| return CL_INVALID_OPERATION; |
| #else |
| if (compilationMode != kSpir_v) |
| { |
| log_error("Compilation mode must be SPIR-V for Khronos compiler"); |
| return -1; |
| } |
| runString = get_khronos_compiler_command(device_address_space_size, openclCXX, bOptions, |
| sourceFilename, outputFilename); |
| #endif |
| } |
| else |
| { |
| std::string clDeviceInfoFilename; |
| |
| // See cl_offline_compiler-interface.txt for a description of the |
| // format of the CL device information file generated below, and |
| // the internal command line interface for invoking the offline |
| // compiler. |
| |
| cl_int err = write_cl_device_info(device, device_address_space_size, compilationMode, |
| clDeviceInfoFilename); |
| if (err != CL_SUCCESS) |
| { |
| log_error("Failed writing CL device info file\n"); |
| return err; |
| } |
| |
| runString = get_offline_compilation_command(device_address_space_size, compilationMode, bOptions, |
| sourceFilename, outputFilename, clDeviceInfoFilename); |
| } |
| |
| // execute script |
| log_info("Executing command: %s\n", runString.c_str()); |
| fflush(stdout); |
| int returnCode = system(runString.c_str()); |
| if (returnCode != 0) |
| { |
| log_error("ERROR: Command finished with error: 0x%x\n", returnCode); |
| return CL_COMPILE_PROGRAM_FAILURE; |
| } |
| |
| return CL_SUCCESS; |
| } |
| |
| static cl_int get_first_device_id(const cl_context context, cl_device_id &device) |
| { |
| cl_uint numDevices = 0; |
| cl_int error = clGetContextInfo(context, CL_CONTEXT_NUM_DEVICES, sizeof(cl_uint), &numDevices, NULL); |
| test_error(error, "clGetContextInfo failed getting CL_CONTEXT_NUM_DEVICES"); |
| |
| if (numDevices == 0) |
| { |
| log_error("ERROR: No CL devices found\n"); |
| return -1; |
| } |
| |
| std::vector<cl_device_id> devices(numDevices, 0); |
| error = clGetContextInfo(context, CL_CONTEXT_DEVICES, numDevices*sizeof(cl_device_id), &devices[0], NULL); |
| test_error(error, "clGetContextInfo failed getting CL_CONTEXT_DEVICES"); |
| |
| device = devices[0]; |
| return CL_SUCCESS; |
| } |
| |
| static cl_int get_device_address_bits(const cl_device_id device, cl_uint &device_address_space_size) |
| { |
| cl_int error = clGetDeviceInfo(device, CL_DEVICE_ADDRESS_BITS, sizeof(cl_uint), &device_address_space_size, NULL); |
| test_error(error, "Unable to obtain device address bits"); |
| |
| if (device_address_space_size != 32 && device_address_space_size != 64) |
| { |
| log_error("ERROR: Unexpected number of device address bits: %u\n", device_address_space_size); |
| return -1; |
| } |
| |
| return CL_SUCCESS; |
| } |
| |
| static int get_offline_compiler_output(std::ifstream &ifs, |
| const cl_device_id device, |
| cl_uint deviceAddrSpaceSize, |
| const bool openclCXX, |
| const CompilationMode compilationMode, |
| const std::string &bOptions, |
| const std::string &kernelPath, |
| const std::string &kernelNamePrefix) |
| { |
| std::string sourceFilename = get_cl_source_filename_with_path(kernelPath, kernelNamePrefix); |
| std::string outputFilename = get_binary_filename_with_path(compilationMode, |
| deviceAddrSpaceSize, |
| kernelPath, |
| kernelNamePrefix); |
| |
| ifs.open(outputFilename.c_str(), std::ios::binary); |
| if(!ifs.good()) { |
| std::string file_type = get_offline_compilation_file_type_str(compilationMode); |
| if (gCompilationCacheMode == kCacheModeForceRead) { |
| log_info("OfflineCompiler: can't open cached %s file: %s\n", |
| file_type.c_str(), outputFilename.c_str()); |
| return -1; |
| } |
| else { |
| int error = invoke_offline_compiler(device, deviceAddrSpaceSize, compilationMode, |
| bOptions, sourceFilename, outputFilename, openclCXX); |
| if (error != CL_SUCCESS) |
| return error; |
| |
| // read output file |
| ifs.open(outputFilename.c_str(), std::ios::binary); |
| if (!ifs.good()) |
| { |
| log_info("OfflineCompiler: can't read generated %s file: %s\n", |
| file_type.c_str(), outputFilename.c_str()); |
| return -1; |
| } |
| } |
| } |
| return CL_SUCCESS; |
| } |
| |
| static int create_single_kernel_helper_create_program_offline(cl_context context, |
| cl_device_id device, |
| cl_program *outProgram, |
| unsigned int numKernelLines, |
| const char *const *kernelProgram, |
| const char *buildOptions, |
| const bool openclCXX, |
| CompilationMode compilationMode) |
| { |
| if(kCacheModeDumpCl == gCompilationCacheMode) { |
| return -1; |
| } |
| |
| // Get device CL_DEVICE_ADDRESS_BITS |
| int error; |
| cl_uint device_address_space_size = 0; |
| if (device == NULL) |
| { |
| error = get_first_device_id(context, device); |
| test_error(error, "Failed to get device ID for first device"); |
| } |
| error = get_device_address_bits(device, device_address_space_size); |
| if (error != CL_SUCCESS) |
| return error; |
| |
| // set build options |
| std::string bOptions; |
| bOptions += buildOptions ? std::string(buildOptions) : ""; |
| |
| std::string kernelName = get_unique_filename_prefix(numKernelLines, |
| kernelProgram, |
| buildOptions); |
| |
| |
| |
| std::ifstream ifs; |
| error = get_offline_compiler_output(ifs, device, device_address_space_size, openclCXX, compilationMode, bOptions, gCompilationCachePath, kernelName); |
| if (error != CL_SUCCESS) |
| return error; |
| |
| // ----------------------------------------------------------------------------------- |
| // ------------- ONLY FOR OPENCL 22 CONFORMANCE TEST 22 DEVELOPMENT ------------------ |
| // ----------------------------------------------------------------------------------- |
| // Only OpenCL C++ to SPIR-V compilation |
| #if defined(DEVELOPMENT) && defined(ONLY_SPIRV_COMPILATION) |
| if(openclCXX) |
| { |
| return CL_SUCCESS; |
| } |
| #endif |
| |
| ifs.seekg(0, ifs.end); |
| int length = ifs.tellg(); |
| ifs.seekg(0, ifs.beg); |
| |
| //treat modifiedProgram as input for clCreateProgramWithBinary |
| if (compilationMode == kBinary) |
| { |
| // read binary from file: |
| std::vector<unsigned char> modifiedKernelBuf(length); |
| |
| ifs.read((char *)&modifiedKernelBuf[0], length); |
| ifs.close(); |
| |
| size_t lengths = modifiedKernelBuf.size(); |
| const unsigned char *binaries = { &modifiedKernelBuf[0] }; |
| log_info("offlineCompiler: clCreateProgramWithSource replaced with clCreateProgramWithBinary\n"); |
| *outProgram = clCreateProgramWithBinary(context, 1, &device, &lengths, &binaries, NULL, &error); |
| if (*outProgram == NULL || error != CL_SUCCESS) |
| { |
| print_error(error, "clCreateProgramWithBinary failed"); |
| return error; |
| } |
| } |
| //treat modifiedProgram as input for clCreateProgramWithIL |
| else if (compilationMode == kSpir_v) |
| { |
| // read spir-v from file: |
| std::vector<unsigned char> modifiedKernelBuf(length); |
| |
| ifs.read((char *)&modifiedKernelBuf[0], length); |
| ifs.close(); |
| |
| size_t length = modifiedKernelBuf.size(); |
| log_info("offlineCompiler: clCreateProgramWithSource replaced with clCreateProgramWithIL\n"); |
| if (gCoreILProgram) |
| { |
| *outProgram = clCreateProgramWithIL(context, &modifiedKernelBuf[0], |
| length, &error); |
| } |
| else |
| { |
| cl_platform_id platform; |
| error = clGetDeviceInfo(device, CL_DEVICE_PLATFORM, |
| sizeof(cl_platform_id), &platform, NULL); |
| print_error(error, "clGetDeviceInfo for CL_DEVICE_PLATFORM failed"); |
| clCreateProgramWithILKHR_fn clCreateProgramWithILKHR = NULL; |
| |
| clCreateProgramWithILKHR = (clCreateProgramWithILKHR_fn) |
| clGetExtensionFunctionAddressForPlatform( |
| platform, "clCreateProgramWithILKHR"); |
| if (clCreateProgramWithILKHR == NULL) |
| { |
| log_error( |
| "ERROR: clGetExtensionFunctionAddressForPlatform failed\n"); |
| return -1; |
| } |
| *outProgram = clCreateProgramWithILKHR( |
| context, &modifiedKernelBuf[0], length, &error); |
| } |
| |
| if (*outProgram == NULL || error != CL_SUCCESS) |
| { |
| if (gCoreILProgram) |
| { |
| print_error(error, "clCreateProgramWithIL failed"); |
| } |
| else |
| { |
| print_error(error, "clCreateProgramWithILKHR failed"); |
| } |
| return error; |
| } |
| } |
| |
| return CL_SUCCESS; |
| } |
| |
| static int create_single_kernel_helper_create_program(cl_context context, |
| cl_device_id device, |
| cl_program *outProgram, |
| unsigned int numKernelLines, |
| const char **kernelProgram, |
| const char *buildOptions, |
| const bool openclCXX, |
| CompilationMode compilationMode) |
| { |
| std::string filePrefix = get_unique_filename_prefix(numKernelLines, |
| kernelProgram, |
| buildOptions); |
| bool shouldSaveToDisk = should_save_kernel_source_to_disk(compilationMode, |
| gCompilationCacheMode, |
| gCompilationCachePath, |
| filePrefix); |
| |
| if(shouldSaveToDisk) |
| { |
| if(CL_SUCCESS != save_kernel_source_and_options_to_disk(numKernelLines, kernelProgram, buildOptions)) |
| { |
| log_error("Unable to dump kernel source to disk"); |
| return -1; |
| } |
| } |
| if (compilationMode == kOnline) |
| { |
| int error = CL_SUCCESS; |
| |
| /* Create the program object from source */ |
| *outProgram = clCreateProgramWithSource(context, numKernelLines, kernelProgram, NULL, &error); |
| if (*outProgram == NULL || error != CL_SUCCESS) |
| { |
| print_error(error, "clCreateProgramWithSource failed"); |
| return error; |
| } |
| return CL_SUCCESS; |
| } |
| else |
| { |
| return create_single_kernel_helper_create_program_offline(context, device, outProgram, |
| numKernelLines, kernelProgram, |
| buildOptions, openclCXX, |
| compilationMode); |
| } |
| } |
| |
| int create_single_kernel_helper_create_program(cl_context context, |
| cl_program *outProgram, |
| unsigned int numKernelLines, |
| const char **kernelProgram, |
| const char *buildOptions, |
| const bool openclCXX) |
| { |
| return create_single_kernel_helper_create_program(context, NULL, outProgram, |
| numKernelLines, kernelProgram, |
| buildOptions, openclCXX, |
| gCompilationMode); |
| } |
| |
| int create_single_kernel_helper_create_program_for_device(cl_context context, |
| cl_device_id device, |
| cl_program *outProgram, |
| unsigned int numKernelLines, |
| const char **kernelProgram, |
| const char *buildOptions, |
| const bool openclCXX) |
| { |
| return create_single_kernel_helper_create_program(context, device, outProgram, |
| numKernelLines, kernelProgram, |
| buildOptions, openclCXX, |
| gCompilationMode); |
| } |
| |
| int create_single_kernel_helper_with_build_options(cl_context context, |
| cl_program *outProgram, |
| cl_kernel *outKernel, |
| unsigned int numKernelLines, |
| const char **kernelProgram, |
| const char *kernelName, |
| const char *buildOptions, |
| const bool openclCXX) |
| { |
| return create_single_kernel_helper(context, outProgram, outKernel, numKernelLines, kernelProgram, kernelName, buildOptions, openclCXX); |
| } |
| |
| // Creates and builds OpenCL C/C++ program, and creates a kernel |
| int create_single_kernel_helper(cl_context context, |
| cl_program *outProgram, |
| cl_kernel *outKernel, |
| unsigned int numKernelLines, |
| const char **kernelProgram, |
| const char *kernelName, |
| const char *buildOptions, |
| const bool openclCXX) |
| { |
| int error; |
| // Create OpenCL C++ program |
| if(openclCXX) |
| { |
| // ----------------------------------------------------------------------------------- |
| // ------------- ONLY FOR OPENCL 22 CONFORMANCE TEST 22 DEVELOPMENT ------------------ |
| // ----------------------------------------------------------------------------------- |
| // Only OpenCL C++ to SPIR-V compilation |
| #if defined(DEVELOPMENT) && defined(ONLY_SPIRV_COMPILATION) |
| // Save global variable |
| bool tempgCompilationCacheMode = gCompilationCacheMode; |
| // Force OpenCL C++ -> SPIR-V compilation on every run |
| gCompilationCacheMode = kCacheModeOverwrite; |
| #endif |
| error = create_openclcpp_program( |
| context, outProgram, numKernelLines, kernelProgram, buildOptions |
| ); |
| if (error != CL_SUCCESS) |
| { |
| log_error("Create program failed: %d, line: %d\n", error, __LINE__); |
| return error; |
| } |
| // ----------------------------------------------------------------------------------- |
| // ------------- ONLY FOR OPENCL 22 CONFORMANCE TEST 22 DEVELOPMENT ------------------ |
| // ----------------------------------------------------------------------------------- |
| #if defined(DEVELOPMENT) && defined(ONLY_SPIRV_COMPILATION) |
| // Restore global variables |
| gCompilationCacheMode = tempgCompilationCacheMode; |
| log_info("WARNING: KERNEL %s WAS ONLY COMPILED TO SPIR-V\n", kernelName); |
| return error; |
| #endif |
| } |
| // Create OpenCL C program |
| else |
| { |
| error = create_single_kernel_helper_create_program( |
| context, outProgram, numKernelLines, kernelProgram, buildOptions |
| ); |
| if (error != CL_SUCCESS) |
| { |
| log_error("Create program failed: %d, line: %d\n", error, __LINE__); |
| return error; |
| } |
| } |
| // Remove offline-compiler-only build options |
| std::string newBuildOptions; |
| if (buildOptions != NULL) |
| { |
| newBuildOptions = buildOptions; |
| std::string offlineCompierOptions[] = { |
| "-cl-fp16-enable", |
| "-cl-fp64-enable", |
| "-cl-zero-init-local-mem-vars" |
| }; |
| for(auto& s : offlineCompierOptions) |
| { |
| std::string::size_type i = newBuildOptions.find(s); |
| if (i != std::string::npos) |
| newBuildOptions.erase(i, s.length()); |
| } |
| } |
| // Build program and create kernel |
| return build_program_create_kernel_helper( |
| context, outProgram, outKernel, numKernelLines, kernelProgram, kernelName, newBuildOptions.c_str() |
| ); |
| } |
| |
| // Creates OpenCL C++ program |
| int create_openclcpp_program(cl_context context, |
| cl_program *outProgram, |
| unsigned int numKernelLines, |
| const char **kernelProgram, |
| const char *buildOptions) |
| { |
| // Create program |
| return create_single_kernel_helper_create_program( |
| context, NULL, outProgram, numKernelLines, kernelProgram, buildOptions, true, kSpir_v |
| ); |
| } |
| |
| // Builds OpenCL C/C++ program and creates |
| int build_program_create_kernel_helper(cl_context context, |
| cl_program *outProgram, |
| cl_kernel *outKernel, |
| unsigned int numKernelLines, |
| const char **kernelProgram, |
| const char *kernelName, |
| const char *buildOptions) |
| { |
| int error; |
| /* Compile the program */ |
| int buildProgramFailed = 0; |
| int printedSource = 0; |
| error = clBuildProgram(*outProgram, 0, NULL, buildOptions, NULL, NULL); |
| if (error != CL_SUCCESS) |
| { |
| unsigned int i; |
| print_error(error, "clBuildProgram failed"); |
| buildProgramFailed = 1; |
| printedSource = 1; |
| log_error("Build options: %s\n", buildOptions); |
| log_error("Original source is: ------------\n"); |
| for (i = 0; i < numKernelLines; i++) |
| log_error("%s", kernelProgram[i]); |
| } |
| |
| // Verify the build status on all devices |
| cl_uint deviceCount = 0; |
| error = clGetProgramInfo(*outProgram, CL_PROGRAM_NUM_DEVICES, sizeof(deviceCount), &deviceCount, NULL); |
| if (error != CL_SUCCESS) |
| { |
| print_error(error, "clGetProgramInfo CL_PROGRAM_NUM_DEVICES failed"); |
| return error; |
| } |
| |
| if (deviceCount == 0) |
| { |
| log_error("No devices found for program.\n"); |
| return -1; |
| } |
| |
| cl_device_id *devices = (cl_device_id *)malloc(deviceCount * sizeof(cl_device_id)); |
| if (NULL == devices) |
| return -1; |
| BufferOwningPtr<cl_device_id> devicesBuf(devices); |
| |
| memset(devices, 0, deviceCount * sizeof(cl_device_id)); |
| error = clGetProgramInfo(*outProgram, CL_PROGRAM_DEVICES, sizeof(cl_device_id) * deviceCount, devices, NULL); |
| if (error != CL_SUCCESS) |
| { |
| print_error(error, "clGetProgramInfo CL_PROGRAM_DEVICES failed"); |
| return error; |
| } |
| |
| cl_uint z; |
| bool buildFailed = false; |
| for (z = 0; z < deviceCount; z++) |
| { |
| char deviceName[4096] = ""; |
| error = clGetDeviceInfo(devices[z], CL_DEVICE_NAME, sizeof(deviceName), deviceName, NULL); |
| if (error != CL_SUCCESS || deviceName[0] == '\0') |
| { |
| log_error("Device \"%d\" failed to return a name\n", z); |
| print_error(error, "clGetDeviceInfo CL_DEVICE_NAME failed"); |
| } |
| |
| cl_build_status buildStatus; |
| error = clGetProgramBuildInfo(*outProgram, devices[z], CL_PROGRAM_BUILD_STATUS, sizeof(buildStatus), &buildStatus, NULL); |
| if (error != CL_SUCCESS) |
| { |
| print_error(error, "clGetProgramBuildInfo CL_PROGRAM_BUILD_STATUS failed"); |
| return error; |
| } |
| |
| if (buildStatus == CL_BUILD_SUCCESS && buildProgramFailed && deviceCount == 1) |
| { |
| buildFailed = true; |
| log_error("clBuildProgram returned an error, but buildStatus is marked as CL_BUILD_SUCCESS.\n"); |
| } |
| |
| if (buildStatus != CL_BUILD_SUCCESS) |
| { |
| |
| char statusString[64] = ""; |
| if (buildStatus == (cl_build_status)CL_BUILD_SUCCESS) |
| sprintf(statusString, "CL_BUILD_SUCCESS"); |
| else if (buildStatus == (cl_build_status)CL_BUILD_NONE) |
| sprintf(statusString, "CL_BUILD_NONE"); |
| else if (buildStatus == (cl_build_status)CL_BUILD_ERROR) |
| sprintf(statusString, "CL_BUILD_ERROR"); |
| else if (buildStatus == (cl_build_status)CL_BUILD_IN_PROGRESS) |
| sprintf(statusString, "CL_BUILD_IN_PROGRESS"); |
| else |
| sprintf(statusString, "UNKNOWN (%d)", buildStatus); |
| |
| if (buildStatus != CL_BUILD_SUCCESS) |
| log_error("Build not successful for device \"%s\", status: %s\n", deviceName, statusString); |
| size_t paramSize = 0; |
| error = clGetProgramBuildInfo(*outProgram, devices[z], CL_PROGRAM_BUILD_LOG, 0, NULL, ¶mSize); |
| if (error != CL_SUCCESS) |
| { |
| |
| print_error(error, "clGetProgramBuildInfo CL_PROGRAM_BUILD_LOG failed"); |
| return error; |
| } |
| |
| std::string log; |
| log.resize(paramSize / sizeof(char)); |
| error = clGetProgramBuildInfo(*outProgram, devices[z], CL_PROGRAM_BUILD_LOG, paramSize, &log[0], NULL); |
| if (error != CL_SUCCESS || log[0] == '\0') |
| { |
| log_error("Device %d (%s) failed to return a build log\n", z, deviceName); |
| if (error) |
| { |
| print_error(error, "clGetProgramBuildInfo CL_PROGRAM_BUILD_LOG failed"); |
| return error; |
| } |
| else |
| { |
| log_error("clGetProgramBuildInfo returned an empty log.\n"); |
| return -1; |
| } |
| } |
| // In this case we've already printed out the code above. |
| if (!printedSource) |
| { |
| unsigned int i; |
| log_error("Original source is: ------------\n"); |
| for (i = 0; i < numKernelLines; i++) |
| log_error("%s", kernelProgram[i]); |
| printedSource = 1; |
| } |
| log_error("Build log for device \"%s\" is: ------------\n", deviceName); |
| log_error("%s\n", log.c_str()); |
| log_error("\n----------\n"); |
| return -1; |
| } |
| } |
| |
| if (buildFailed) |
| { |
| return -1; |
| } |
| |
| /* And create a kernel from it */ |
| if (kernelName != NULL) |
| { |
| *outKernel = clCreateKernel(*outProgram, kernelName, &error); |
| if (*outKernel == NULL || error != CL_SUCCESS) |
| { |
| print_error(error, "Unable to create kernel"); |
| return error; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int get_max_allowed_work_group_size( cl_context context, cl_kernel kernel, size_t *outMaxSize, size_t *outLimits ) |
| { |
| cl_device_id *devices; |
| size_t size, maxCommonSize = 0; |
| int numDevices, i, j, error; |
| cl_uint numDims; |
| size_t outSize; |
| size_t sizeLimit[]={1,1,1}; |
| |
| |
| /* Assume fewer than 16 devices will be returned */ |
| error = clGetContextInfo( context, CL_CONTEXT_DEVICES, 0, NULL, &outSize ); |
| test_error( error, "Unable to obtain list of devices size for context" ); |
| devices = (cl_device_id *)malloc(outSize); |
| BufferOwningPtr<cl_device_id> devicesBuf(devices); |
| |
| error = clGetContextInfo( context, CL_CONTEXT_DEVICES, outSize, devices, NULL ); |
| test_error( error, "Unable to obtain list of devices for context" ); |
| |
| numDevices = (int)( outSize / sizeof( cl_device_id ) ); |
| |
| for( i = 0; i < numDevices; i++ ) |
| { |
| error = clGetDeviceInfo( devices[i], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof( size ), &size, NULL ); |
| test_error( error, "Unable to obtain max work group size for device" ); |
| if( size < maxCommonSize || maxCommonSize == 0) |
| maxCommonSize = size; |
| |
| error = clGetKernelWorkGroupInfo( kernel, devices[i], CL_KERNEL_WORK_GROUP_SIZE, sizeof( size ), &size, NULL ); |
| test_error( error, "Unable to obtain max work group size for device and kernel combo" ); |
| if( size < maxCommonSize || maxCommonSize == 0) |
| maxCommonSize = size; |
| |
| error= clGetDeviceInfo( devices[i], CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, sizeof( numDims ), &numDims, NULL); |
| test_error( error, "clGetDeviceInfo failed for CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS"); |
| sizeLimit[0] = 1; |
| error= clGetDeviceInfo( devices[i], CL_DEVICE_MAX_WORK_ITEM_SIZES, numDims*sizeof(size_t), sizeLimit, NULL); |
| test_error( error, "clGetDeviceInfo failed for CL_DEVICE_MAX_WORK_ITEM_SIZES"); |
| |
| if (outLimits != NULL) |
| { |
| if (i == 0) { |
| for (j=0; j<3; j++) |
| outLimits[j] = sizeLimit[j]; |
| } else { |
| for (j=0; j<(int)numDims; j++) { |
| if (sizeLimit[j] < outLimits[j]) |
| outLimits[j] = sizeLimit[j]; |
| } |
| } |
| } |
| } |
| |
| *outMaxSize = (unsigned int)maxCommonSize; |
| return 0; |
| } |
| |
| |
| extern int get_max_allowed_1d_work_group_size_on_device( cl_device_id device, cl_kernel kernel, size_t *outSize ) |
| { |
| cl_uint maxDim; |
| size_t maxWgSize; |
| size_t *maxWgSizePerDim; |
| int error; |
| |
| error = clGetKernelWorkGroupInfo( kernel, device, CL_KERNEL_WORK_GROUP_SIZE, sizeof( size_t ), &maxWgSize, NULL ); |
| test_error( error, "clGetKernelWorkGroupInfo CL_KERNEL_WORK_GROUP_SIZE failed" ); |
| |
| error = clGetDeviceInfo( device, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, sizeof( cl_uint ), &maxDim, NULL ); |
| test_error( error, "clGetDeviceInfo CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS failed" ); |
| maxWgSizePerDim = (size_t*)malloc( maxDim * sizeof( size_t ) ); |
| if( !maxWgSizePerDim ) |
| { |
| log_error( "Unable to allocate maxWgSizePerDim\n" ); |
| return -1; |
| } |
| |
| error = clGetDeviceInfo( device, CL_DEVICE_MAX_WORK_ITEM_SIZES, maxDim * sizeof( size_t ), maxWgSizePerDim, NULL ); |
| if( error != CL_SUCCESS) |
| { |
| log_error( "clGetDeviceInfo CL_DEVICE_MAX_WORK_ITEM_SIZES failed\n" ); |
| free( maxWgSizePerDim ); |
| return error; |
| } |
| |
| // "maxWgSize" is limited to that of the first dimension. |
| if( maxWgSize > maxWgSizePerDim[0] ) |
| { |
| maxWgSize = maxWgSizePerDim[0]; |
| } |
| |
| free( maxWgSizePerDim ); |
| |
| *outSize = maxWgSize; |
| return 0; |
| } |
| |
| |
| int get_max_common_work_group_size( cl_context context, cl_kernel kernel, |
| size_t globalThreadSize, size_t *outMaxSize ) |
| { |
| size_t sizeLimit[3]; |
| int error = get_max_allowed_work_group_size( context, kernel, outMaxSize, sizeLimit ); |
| if( error != 0 ) |
| return error; |
| |
| /* Now find the largest factor of globalThreadSize that is <= maxCommonSize */ |
| /* Note for speed, we don't need to check the range of maxCommonSize, b/c once it gets to 1, |
| the modulo test will succeed and break the loop anyway */ |
| for( ; ( globalThreadSize % *outMaxSize ) != 0 || (*outMaxSize > sizeLimit[0]); (*outMaxSize)-- ) |
| ; |
| return 0; |
| } |
| |
| int get_max_common_2D_work_group_size( cl_context context, cl_kernel kernel, |
| size_t *globalThreadSizes, size_t *outMaxSizes ) |
| { |
| size_t sizeLimit[3]; |
| size_t maxSize; |
| int error = get_max_allowed_work_group_size( context, kernel, &maxSize, sizeLimit ); |
| if( error != 0 ) |
| return error; |
| |
| /* Now find a set of factors, multiplied together less than maxSize, but each a factor of the global |
| sizes */ |
| |
| /* Simple case */ |
| if( globalThreadSizes[ 0 ] * globalThreadSizes[ 1 ] <= maxSize ) |
| { |
| if (globalThreadSizes[ 0 ] <= sizeLimit[0] && globalThreadSizes[ 1 ] <= sizeLimit[1]) { |
| outMaxSizes[ 0 ] = globalThreadSizes[ 0 ]; |
| outMaxSizes[ 1 ] = globalThreadSizes[ 1 ]; |
| return 0; |
| } |
| } |
| |
| size_t remainingSize, sizeForThisOne; |
| remainingSize = maxSize; |
| int i, j; |
| for (i=0 ; i<2; i++) { |
| if (globalThreadSizes[i] > remainingSize) |
| sizeForThisOne = remainingSize; |
| else |
| sizeForThisOne = globalThreadSizes[i]; |
| for (; (globalThreadSizes[i] % sizeForThisOne) != 0 || (sizeForThisOne > sizeLimit[i]); sizeForThisOne--) ; |
| outMaxSizes[i] = sizeForThisOne; |
| remainingSize = maxSize; |
| for (j=0; j<=i; j++) |
| remainingSize /=outMaxSizes[j]; |
| } |
| |
| return 0; |
| } |
| |
| int get_max_common_3D_work_group_size( cl_context context, cl_kernel kernel, |
| size_t *globalThreadSizes, size_t *outMaxSizes ) |
| { |
| size_t sizeLimit[3]; |
| size_t maxSize; |
| int error = get_max_allowed_work_group_size( context, kernel, &maxSize, sizeLimit ); |
| if( error != 0 ) |
| return error; |
| /* Now find a set of factors, multiplied together less than maxSize, but each a factor of the global |
| sizes */ |
| |
| /* Simple case */ |
| if( globalThreadSizes[ 0 ] * globalThreadSizes[ 1 ] * globalThreadSizes[ 2 ] <= maxSize ) |
| { |
| if (globalThreadSizes[ 0 ] <= sizeLimit[0] && globalThreadSizes[ 1 ] <= sizeLimit[1] && globalThreadSizes[ 2 ] <= sizeLimit[2]) { |
| outMaxSizes[ 0 ] = globalThreadSizes[ 0 ]; |
| outMaxSizes[ 1 ] = globalThreadSizes[ 1 ]; |
| outMaxSizes[ 2 ] = globalThreadSizes[ 2 ]; |
| return 0; |
| } |
| } |
| |
| size_t remainingSize, sizeForThisOne; |
| remainingSize = maxSize; |
| int i, j; |
| for (i=0 ; i<3; i++) { |
| if (globalThreadSizes[i] > remainingSize) |
| sizeForThisOne = remainingSize; |
| else |
| sizeForThisOne = globalThreadSizes[i]; |
| for (; (globalThreadSizes[i] % sizeForThisOne) != 0 || (sizeForThisOne > sizeLimit[i]); sizeForThisOne--) ; |
| outMaxSizes[i] = sizeForThisOne; |
| remainingSize = maxSize; |
| for (j=0; j<=i; j++) |
| remainingSize /=outMaxSizes[j]; |
| } |
| |
| return 0; |
| } |
| |
| /* Helper to determine if a device supports an image format */ |
| int is_image_format_supported( cl_context context, cl_mem_flags flags, cl_mem_object_type image_type, const cl_image_format *fmt ) |
| { |
| cl_image_format *list; |
| cl_uint count = 0; |
| cl_int err = clGetSupportedImageFormats( context, flags, image_type, 128, NULL, &count ); |
| if( count == 0 ) |
| return 0; |
| |
| list = (cl_image_format*) malloc( count * sizeof( cl_image_format ) ); |
| if( NULL == list ) |
| { |
| log_error( "Error: unable to allocate %ld byte buffer for image format list at %s:%d (err = %d)\n", count * sizeof( cl_image_format ), __FILE__, __LINE__, err ); |
| return 0; |
| } |
| BufferOwningPtr<cl_image_format> listBuf(list); |
| |
| |
| cl_int error = clGetSupportedImageFormats( context, flags, image_type, count, list, NULL ); |
| if( error ) |
| { |
| log_error( "Error: failed to obtain supported image type list at %s:%d (err = %d)\n", __FILE__, __LINE__, err ); |
| return 0; |
| } |
| |
| // iterate looking for a match. |
| cl_uint i; |
| for( i = 0; i < count; i++ ) |
| { |
| if( fmt->image_channel_data_type == list[ i ].image_channel_data_type && |
| fmt->image_channel_order == list[ i ].image_channel_order ) |
| break; |
| } |
| |
| return ( i < count ) ? 1 : 0; |
| } |
| |
| size_t get_pixel_bytes( const cl_image_format *fmt ); |
| size_t get_pixel_bytes( const cl_image_format *fmt ) |
| { |
| size_t chanCount; |
| switch( fmt->image_channel_order ) |
| { |
| case CL_R: |
| case CL_A: |
| case CL_Rx: |
| case CL_INTENSITY: |
| case CL_LUMINANCE: |
| case CL_DEPTH: |
| chanCount = 1; |
| break; |
| case CL_RG: |
| case CL_RA: |
| case CL_RGx: |
| chanCount = 2; |
| break; |
| case CL_RGB: |
| case CL_RGBx: |
| case CL_sRGB: |
| case CL_sRGBx: |
| chanCount = 3; |
| break; |
| case CL_RGBA: |
| case CL_ARGB: |
| case CL_BGRA: |
| case CL_sBGRA: |
| case CL_sRGBA: |
| #ifdef CL_1RGB_APPLE |
| case CL_1RGB_APPLE: |
| #endif |
| #ifdef CL_BGR1_APPLE |
| case CL_BGR1_APPLE: |
| #endif |
| chanCount = 4; |
| break; |
| default: |
| log_error("Unknown channel order at %s:%d!\n", __FILE__, __LINE__ ); |
| abort(); |
| break; |
| } |
| |
| switch( fmt->image_channel_data_type ) |
| { |
| case CL_UNORM_SHORT_565: |
| case CL_UNORM_SHORT_555: |
| return 2; |
| |
| case CL_UNORM_INT_101010: |
| return 4; |
| |
| case CL_SNORM_INT8: |
| case CL_UNORM_INT8: |
| case CL_SIGNED_INT8: |
| case CL_UNSIGNED_INT8: |
| return chanCount; |
| |
| case CL_SNORM_INT16: |
| case CL_UNORM_INT16: |
| case CL_HALF_FLOAT: |
| case CL_SIGNED_INT16: |
| case CL_UNSIGNED_INT16: |
| #ifdef CL_SFIXED14_APPLE |
| case CL_SFIXED14_APPLE: |
| #endif |
| return chanCount * 2; |
| |
| case CL_SIGNED_INT32: |
| case CL_UNSIGNED_INT32: |
| case CL_FLOAT: |
| return chanCount * 4; |
| |
| default: |
| log_error("Unknown channel data type at %s:%d!\n", __FILE__, __LINE__ ); |
| abort(); |
| } |
| |
| return 0; |
| } |
| |
| test_status verifyImageSupport( cl_device_id device ) |
| { |
| int result = checkForImageSupport( device ); |
| if( result == 0 ) |
| { |
| return TEST_PASS; |
| } |
| if( result == CL_IMAGE_FORMAT_NOT_SUPPORTED ) |
| { |
| log_error( "SKIPPED: Device does not supported images as required by this test!\n" ); |
| return TEST_SKIP; |
| } |
| return TEST_FAIL; |
| } |
| |
| int checkForImageSupport( cl_device_id device ) |
| { |
| cl_uint i; |
| int error; |
| |
| |
| /* Check the device props to see if images are supported at all first */ |
| error = clGetDeviceInfo( device, CL_DEVICE_IMAGE_SUPPORT, sizeof( i ), &i, NULL ); |
| test_error( error, "Unable to query device for image support" ); |
| if( i == 0 ) |
| { |
| return CL_IMAGE_FORMAT_NOT_SUPPORTED; |
| } |
| |
| /* So our support is good */ |
| return 0; |
| } |
| |
| int checkFor3DImageSupport( cl_device_id device ) |
| { |
| cl_uint i; |
| int error; |
| |
| /* Check the device props to see if images are supported at all first */ |
| error = clGetDeviceInfo( device, CL_DEVICE_IMAGE_SUPPORT, sizeof( i ), &i, NULL ); |
| test_error( error, "Unable to query device for image support" ); |
| if( i == 0 ) |
| { |
| return CL_IMAGE_FORMAT_NOT_SUPPORTED; |
| } |
| |
| char profile[128]; |
| error = clGetDeviceInfo( device, CL_DEVICE_PROFILE, sizeof(profile ), profile, NULL ); |
| test_error( error, "Unable to query device for CL_DEVICE_PROFILE" ); |
| if( 0 == strcmp( profile, "EMBEDDED_PROFILE" ) ) |
| { |
| size_t width = -1L; |
| size_t height = -1L; |
| size_t depth = -1L; |
| error = clGetDeviceInfo( device, CL_DEVICE_IMAGE3D_MAX_WIDTH, sizeof(width), &width, NULL ); |
| test_error( error, "Unable to get CL_DEVICE_IMAGE3D_MAX_WIDTH" ); |
| error = clGetDeviceInfo( device, CL_DEVICE_IMAGE3D_MAX_HEIGHT, sizeof(height), &height, NULL ); |
| test_error( error, "Unable to get CL_DEVICE_IMAGE3D_MAX_HEIGHT" ); |
| error = clGetDeviceInfo( device, CL_DEVICE_IMAGE3D_MAX_DEPTH, sizeof(depth), &depth, NULL ); |
| test_error( error, "Unable to get CL_DEVICE_IMAGE3D_MAX_DEPTH" ); |
| |
| if( 0 == (height | width | depth )) |
| return CL_IMAGE_FORMAT_NOT_SUPPORTED; |
| } |
| |
| /* So our support is good */ |
| return 0; |
| } |
| |
| size_t get_min_alignment(cl_context context) |
| { |
| static cl_uint align_size = 0; |
| |
| if( 0 == align_size ) |
| { |
| cl_device_id * devices; |
| size_t devices_size = 0; |
| cl_uint result = 0; |
| cl_int error; |
| int i; |
| |
| error = clGetContextInfo (context, |
| CL_CONTEXT_DEVICES, |
| 0, |
| NULL, |
| &devices_size); |
| test_error_ret(error, "clGetContextInfo failed", 0); |
| |
| devices = (cl_device_id*)malloc(devices_size); |
| if (devices == NULL) { |
| print_error( error, "malloc failed" ); |
| return 0; |
| } |
| |
| error = clGetContextInfo (context, |
| CL_CONTEXT_DEVICES, |
| devices_size, |
| (void*)devices, |
| NULL); |
| test_error_ret(error, "clGetContextInfo failed", 0); |
| |
| for (i = 0; i < (int)(devices_size/sizeof(cl_device_id)); i++) |
| { |
| cl_uint alignment = 0; |
| |
| error = clGetDeviceInfo (devices[i], |
| CL_DEVICE_MEM_BASE_ADDR_ALIGN, |
| sizeof(cl_uint), |
| (void*)&alignment, |
| NULL); |
| |
| if (error == CL_SUCCESS) |
| { |
| alignment >>= 3; // convert bits to bytes |
| result = (alignment > result) ? alignment : result; |
| } |
| else |
| print_error( error, "clGetDeviceInfo failed" ); |
| } |
| |
| align_size = result; |
| free(devices); |
| } |
| |
| return align_size; |
| } |
| |
| cl_device_fp_config get_default_rounding_mode( cl_device_id device ) |
| { |
| char profileStr[128] = ""; |
| cl_device_fp_config single = 0; |
| int error = clGetDeviceInfo( device, CL_DEVICE_SINGLE_FP_CONFIG, sizeof( single ), &single, NULL ); |
| if( error ) |
| test_error_ret( error, "Unable to get device CL_DEVICE_SINGLE_FP_CONFIG", 0 ); |
| |
| if( single & CL_FP_ROUND_TO_NEAREST ) |
| return CL_FP_ROUND_TO_NEAREST; |
| |
| if( 0 == (single & CL_FP_ROUND_TO_ZERO) ) |
| test_error_ret( -1, "FAILURE: device must support either CL_DEVICE_SINGLE_FP_CONFIG or CL_FP_ROUND_TO_NEAREST", 0 ); |
| |
| // Make sure we are an embedded device before allowing a pass |
| if( (error = clGetDeviceInfo( device, CL_DEVICE_PROFILE, sizeof( profileStr ), &profileStr, NULL ) )) |
| test_error_ret( error, "FAILURE: Unable to get CL_DEVICE_PROFILE", 0 ); |
| |
| if( strcmp( profileStr, "EMBEDDED_PROFILE" ) ) |
| test_error_ret( error, "FAILURE: non-EMBEDDED_PROFILE devices must support CL_FP_ROUND_TO_NEAREST", 0 ); |
| |
| return CL_FP_ROUND_TO_ZERO; |
| } |
| |
| int checkDeviceForQueueSupport( cl_device_id device, cl_command_queue_properties prop ) |
| { |
| cl_command_queue_properties realProps; |
| cl_int error = clGetDeviceInfo( device, CL_DEVICE_QUEUE_ON_HOST_PROPERTIES, sizeof( realProps ), &realProps, NULL ); |
| test_error_ret( error, "FAILURE: Unable to get device queue properties", 0 ); |
| |
| return ( realProps & prop ) ? 1 : 0; |
| } |
| |
| int printDeviceHeader( cl_device_id device ) |
| { |
| char deviceName[ 512 ], deviceVendor[ 512 ], deviceVersion[ 512 ], cLangVersion[ 512 ]; |
| int error; |
| |
| error = clGetDeviceInfo( device, CL_DEVICE_NAME, sizeof( deviceName ), deviceName, NULL ); |
| test_error( error, "Unable to get CL_DEVICE_NAME for device" ); |
| |
| error = clGetDeviceInfo( device, CL_DEVICE_VENDOR, sizeof( deviceVendor ), deviceVendor, NULL ); |
| test_error( error, "Unable to get CL_DEVICE_VENDOR for device" ); |
| |
| error = clGetDeviceInfo( device, CL_DEVICE_VERSION, sizeof( deviceVersion ), deviceVersion, NULL ); |
| test_error( error, "Unable to get CL_DEVICE_VERSION for device" ); |
| |
| error = clGetDeviceInfo( device, CL_DEVICE_OPENCL_C_VERSION, sizeof( cLangVersion ), cLangVersion, NULL ); |
| test_error( error, "Unable to get CL_DEVICE_OPENCL_C_VERSION for device" ); |
| |
| log_info("Compute Device Name = %s, Compute Device Vendor = %s, Compute Device Version = %s%s%s\n", |
| deviceName, deviceVendor, deviceVersion, ( error == CL_SUCCESS ) ? ", CL C Version = " : "", |
| ( error == CL_SUCCESS ) ? cLangVersion : "" ); |
| |
| return CL_SUCCESS; |
| } |