diff --git a/CMakeLists.txt b/CMakeLists.txt index 048966c7..79289c38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 2.6) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) include(CheckFunctionExists) +include(PIPMacros.cmake) # Options @@ -33,7 +34,7 @@ set(PIP_SRC_CRYPT "src_crypt") set(PIP_SRC_COMPRESS "src_compress") set(PIP_SRC_USB "src_usb") set(PIP_SRC_FFTW "src_fftw") -#set(PIP_SRC_RESOURCES "src_resources") +set(PIP_SRC_OPENCL "src_opencl") set(PIP_LIBS_TARGETS pip) set(LIBS_MAIN) set(LIBS_STATUS) @@ -86,7 +87,7 @@ endif() # Sources # Main lib -set(PIP_FOLDERS "." "core" "containers" "thread" "system" "io" "console" "math" "code" "geo" "resources") +set(PIP_FOLDERS "." "core" "containers" "thread" "system" "io" "console" "math" "code" "geo" "resources" "opencl") include_directories("${PIP_SRC_MAIN}") foreach(F ${PIP_FOLDERS}) include_directories("${PIP_SRC_MAIN}/${F}") @@ -105,8 +106,8 @@ gather_src("${PIP_SRC_USB}" CPP_LIB_USB HDRS PHDRS) # FFTW lib gather_src("${PIP_SRC_FFTW}" CPP_LIB_FFTW HDRS PHDRS) -# Resources lib -#gather_src("${PIP_SRC_RESOURCES}" CPP_LIB_RESOURCES HDRS PHDRS) +# OpenCL lib +gather_src("${PIP_SRC_OPENCL}" CPP_LIB_OPENCL HDRS PHDRS) # Check Bessel functions @@ -361,10 +362,23 @@ else() endif() -# Add resource system -#add_library(pip_resources SHARED ${CPP_LIB_RESOURCES}) -#target_link_libraries(pip_resources pip ) -#list(APPEND PIP_LIBS_TARGETS pip_resources) +# Check if PIP support OpenCL +find_package(OpenCL QUIET) +if(OpenCL_FOUND) + message(STATUS "Building with OpenCL support") + include_directories(${OpenCL_INCLUDE_DIRS}) + add_definitions(-DPIP_OPENCL) + pip_resources(CL_RES "src_opencl/resources.conf") + add_library(pip_opencl SHARED ${CPP_LIB_OPENCL} ${CL_RES}) + add_dependencies(pip_opencl pip_rc) + target_link_libraries(pip_opencl pip ${OpenCL_LIBRARIES}) + list(APPEND LIBS_STATUS OpenCL) + list(APPEND PIP_LIBS_TARGETS pip_opencl) + set(OpenCL_FOUND ${OpenCL_LIBRARIES}) +else() + message(STATUS "Building without OpenCL support") +endif() + # Test program @@ -386,6 +400,7 @@ if(LIB) #message("${STDLIB}") if (STDLIB) file(COPY "${STDLIB}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/utils/code_model_generator") + file(COPY "${STDLIB}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/utils/resources_compiler") endif() endif() else() diff --git a/src_main/core/piinit.cpp b/src_main/core/piinit.cpp index f747f211..342056ba 100644 --- a/src_main/core/piinit.cpp +++ b/src_main/core/piinit.cpp @@ -347,6 +347,12 @@ bool PIInit::isBuildOptionEnabled(PIInit::BuildOption o) { true; #else false; +#endif + case OpenCL: return +#ifdef PIP_OPENCL + true; +#else + false; #endif default: return false; } @@ -364,6 +370,7 @@ PIStringList PIInit::buildOptions() { if (isBuildOptionEnabled(IntrospectionThreads)) ret << "IntrospectionThreads"; if (isBuildOptionEnabled(FFTW)) ret << "FFTW"; if (isBuildOptionEnabled(Compress)) ret << "Compress"; + if (isBuildOptionEnabled(OpenCL)) ret << "OpenCL"; return ret; } diff --git a/src_main/core/piinit.h b/src_main/core/piinit.h index 2c8313cb..5cd0ee2b 100644 --- a/src_main/core/piinit.h +++ b/src_main/core/piinit.h @@ -55,6 +55,7 @@ public: IntrospectionThreads /*! Threads introspection */ = 0x20, FFTW /*! FFTW3 support */ = 0x40, Compress /*! Zlib compression support */ = 0x80, + OpenCL /*! OpenCL support */ = 0x100, }; static PIInit * instance() {return __PIInit_Initializer__::__instance__;} static bool isBuildOptionEnabled(BuildOption o); diff --git a/src_main/opencl/piopencl.h b/src_main/opencl/piopencl.h new file mode 100644 index 00000000..38183ddb --- /dev/null +++ b/src_main/opencl/piopencl.h @@ -0,0 +1,175 @@ +#ifndef PIOPENCL_H +#define PIOPENCL_H + +#include "pivariant.h" + + +class PIOpenCL { +public: + + struct KernelArg; + struct Device; + struct Platform; + class Context; + class Program; + class Kernel; + + typedef PIVector DeviceList; + + enum AddressQualifier { + AddressGlobal, + AddressLocal, + AddressConstant, + AddressPrivate, + }; + + enum AccessQualifier { + AccessReadOnly, + AccessWriteOnly, + AccessReadWrite, + AccessNone, + }; + + enum TypeQualifier { + TypeConst, + TypeRestrict, + TypeVolatile, + TypeNone, + }; + + enum ArgType { + Char = 1, + UChar, + Short, + UShort, + Int, + UInt, + Long, + ULong, + Float, + Double, + }; + + struct KernelArg { + KernelArg(); + AddressQualifier address_qualifier; + AccessQualifier access_qualifier; + TypeQualifier type_qualifier; + PIString arg_name; + PIString type_name; + PIString base_type_name; + bool is_pointer; + ArgType arg_type; + int dims; + private: + friend class Kernel; + void init(void * _k, uint index); + }; + + struct Device { + Device() {id = platform_id = 0; max_compute_units = max_clock_frequency = 0; max_memory_size = 0;} + bool isValid() const {return id != 0;} + PIString displayText() const {return name.trimmed() + " (" + device_version.trimmed() + ")";} + void * id; + void * platform_id; + PIString name; + PIString vendor; + PIString device_version; + PIString driver_version; + int max_compute_units; + int max_clock_frequency; + ullong max_memory_size; + }; + + struct Platform { + Platform() {id = 0;} + bool isValid() const {return id != 0;} + PIString displayText() const {return name.trimmed() + " (" + version.trimmed() + ", " + profile.trimmed() + ")";} + void * id; + PIString name; + PIString vendor; + PIString profile; + PIString version; + PIStringList extensions; + PIVector devices; + }; + + class Context { + friend class Program; + public: + ~Context(); + static Context * create(const DeviceList & dl); + static Context * create(const Device & d) {return create(DeviceList() << d);} + Program * createProgram(const PIString & source, PIString * error = 0); + private: + Context(); + void zero(); + void deletePrograms(); + PIVector programs_; + PRIVATE_DECLARATION + }; + + class Program { + friend class Context; + friend class Kernel; + public: + ~Program(); + const PIString & sourceCode() const {return source_;} + const Kernel * kernel(int index = 0) const {return kernels_[index];} + const PIVector & kernels() const {return kernels_;} + private: + Program(); + void zero(); + bool initKernels(PIVector kerns); + Context * context_; + PIString source_; + PIVector kernels_; + PRIVATE_DECLARATION + }; + + class Kernel { + friend class Program; + public: + const PIString & name() const {return name_;} + const PIVector & args() const {return args_;} + template bool setArgValue(int index, const T & value) {return setArgValueV(index, PIVariant::fromValue(value));} + template bool setArgValue(const PIString & arg, const T & value) {return setArgValue(argIndex(arg), value);} + private: + Kernel(); + ~Kernel(); + void zero(); + bool init(); + bool setArgValueV(int index, const PIVariant & value); + int argIndex(const PIString & an) const; + KernelArg argByName(const PIString & an) const; + Context * context_; + Program * program_; + PIString name_; + PIVector args_; + PRIVATE_DECLARATION + }; + + static void init(); + static const PIVector & platforms(); + static const PIVector devices(); + static Device deviceByID(void * id); + static PIString prepareProgram(const PIString & prog); + +private: + static PIString prog_header; + PIOpenCL() {;} + class Initializer { + public: + Initializer(); + static Initializer * instance(); + void init(); + PIVector platforms_; + bool inited_; + }; +}; + + +PICout operator <<(PICout s, const PIOpenCL::KernelArg & v); + + +#endif // PIOPENCL_H diff --git a/src_opencl/3rd/clcomplex.h b/src_opencl/3rd/clcomplex.h new file mode 100644 index 00000000..0a719731 --- /dev/null +++ b/src_opencl/3rd/clcomplex.h @@ -0,0 +1,318 @@ +//---------------------------------------------------------------------------// +// MIT License +// +// Copyright (c) 2017 StreamComputing +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef OPENCL_COMPLEX_MATH +#define OPENCL_COMPLEX_MATH + +#define CONCAT(x, y) x##y +#define FNAME(name, sufix) c##name##sufix + +// float2 +#define clrealf(complex) complex.x; +#define climagf(complex) complex.y; + +// double2 +#define clreal(complex) complex.x; +#define climag(complex) complex.y; + +#define OPENCL_COMPLEX_MATH_FUNCS(complex_type, real_type, func_sufix, math_consts_sufix) \ + complex_type CONCAT(complex, func_sufix)(real_type r, real_type i) \ + { \ + return (complex_type)(r, i); \ + } \ + \ + complex_type FNAME(add, func_sufix)(complex_type x, complex_type y) \ + { \ + return x + y; \ + } \ + \ + complex_type FNAME(sub, func_sufix)(complex_type x, complex_type y) \ + { \ + return x - y; \ + } \ + \ + complex_type FNAME(add_real, func_sufix)(complex_type z, real_type r) \ + { \ + return (complex_type)(z.x + r, z.y); \ + } \ + \ + complex_type FNAME(sub_real, func_sufix)(complex_type z, real_type r) \ + { \ + return (complex_type)(z.x - r, z.y); \ + } \ + \ + real_type FNAME(abs, func_sufix)(complex_type z) \ + { \ + return length(z); \ + } \ + \ + real_type FNAME(arg, func_sufix)(complex_type z) \ + { \ + return atan2(z.y, z.x); \ + } \ + \ + complex_type FNAME(mul, func_sufix)(complex_type z1, complex_type z2) \ + { \ + real_type x1 = z1.x; \ + real_type y1 = z1.y; \ + real_type x2 = z2.x; \ + real_type y2 = z2.y; \ + return (complex_type)(x1 * x2 - y1 * y2, x1 * y2 + x2 * y1); \ + } \ + \ + complex_type FNAME(div, func_sufix)(complex_type z1, complex_type z2) \ + { \ + real_type x1 = z1.x; \ + real_type y1 = z1.y; \ + real_type x2 = z2.x; \ + real_type y2 = z2.y; \ + real_type iabs_z2 = CONCAT(1.0, func_sufix) / FNAME(abs, func_sufix)(z2); \ + return (complex_type)( \ + ((x1 * x2 * iabs_z2) + (y1 * y2 * iabs_z2)) * iabs_z2, \ + ((y1 * x2 * iabs_z2) - (x1 * y2 * iabs_z2)) * iabs_z2 \ + ); \ + } \ + \ + complex_type FNAME(mul_real, func_sufix)(complex_type z, real_type r) \ + { \ + return z * r; \ + } \ + \ + complex_type FNAME(div_real, func_sufix)(complex_type z, real_type r) \ + { \ + return z / r; \ + } \ + \ + complex_type FNAME(conj, func_sufix)(complex_type z) \ + { \ + return (complex_type)(z.x, -z.y); \ + } \ + \ + complex_type FNAME(proj, func_sufix)(complex_type z) \ + { \ + if(isinf(z.x) || isinf(z.y)) \ + { \ + return (complex_type)(INFINITY, (copysign(CONCAT(0.0, func_sufix), z.y))); \ + } \ + return z; \ + } \ + \ + real_type FNAME(norm, func_sufix)(complex_type z) \ + { \ + /* Returns the squared magnitude of the complex number z. */ \ + /* The norm calculated by this function is also known as */ \ + /* field norm or absolute square. */ \ + real_type x = z.x; \ + real_type y = z.y; \ + return x * x + y * y; \ + } \ + \ + complex_type FNAME(polar, func_sufix)(real_type r, real_type theta) \ + { \ + /* Returns a complex number with magnitude r and phase angle theta. */ \ + return (complex_type)(r * cos(theta), r * sin(theta)); \ + } \ + \ + complex_type FNAME(exp, func_sufix)(complex_type z) \ + { \ + /* The complex exponential function e^z for z = x+i*y */ \ + /* equals to e^x * cis(y), */ \ + /* or, e^x * (cos(y) + i*sin(y)) */ \ + real_type expx = exp(z.x); \ + return (complex_type)(expx * cos(z.y), expx * sin(z.y)); \ + } \ + \ + complex_type FNAME(log, func_sufix)(complex_type z) \ + { \ + /* log(z) = log(abs(z)) + i * arg(z) */ \ + return (complex_type)(log(FNAME(abs, func_sufix)(z)),FNAME(arg, func_sufix)(z)); \ + } \ + \ + complex_type FNAME(log10, func_sufix)(complex_type z) \ + { \ + return FNAME(log, func_sufix)(z) / log(CONCAT(10.0, func_sufix)); \ + } \ + \ + complex_type FNAME(pow, func_sufix)(complex_type z1, complex_type z2) \ + { \ + /* (z1)^(z2) = exp(z2 * log(z1)) = cexp(mul(z2, clog(z1))) */ \ + return \ + FNAME(exp, func_sufix)( \ + FNAME(mul, func_sufix)( \ + z2, \ + FNAME(log, func_sufix)(z1) \ + ) \ + ); \ + } \ + \ + complex_type FNAME(sqrt, func_sufix)(complex_type z) \ + { \ + /* */ \ + real_type x = z.x; \ + real_type y = z.y; \ + if(x == CONCAT(0.0, func_sufix)) \ + { \ + real_type t = sqrt(fabs(y) / 2); \ + return (complex_type)(t, y < CONCAT(0.0, func_sufix) ? -t : t); \ + } \ + else \ + { \ + real_type t = sqrt(2 * FNAME(abs, func_sufix)(z) + fabs(x)); \ + real_type u = t / 2; \ + return x > CONCAT(0.0, func_sufix) \ + ? (complex_type)(u, y / t) \ + : (complex_type)(fabs(y) / t, y < CONCAT(0.0, func_sufix) ? -u : u); \ + } \ + } \ + \ + complex_type FNAME(sin, func_sufix)(complex_type z) \ + { \ + const real_type x = z.x; \ + const real_type y = z.y; \ + return (complex_type)(sin(x) * cosh(y), cos(x) * sinh(y)); \ + } \ + \ + complex_type FNAME(sinh, func_sufix)(complex_type z) \ + { \ + const real_type x = z.x; \ + const real_type y = z.y; \ + return (complex_type)(sinh(x) * cos(y), cosh(x) * sin(y)); \ + } \ + \ + complex_type FNAME(cos, func_sufix)(complex_type z) \ + { \ + const real_type x = z.x; \ + const real_type y = z.y; \ + return (complex_type)(cos(x) * cosh(y), -sin(x) * sinh(y)); \ + } \ + \ + complex_type FNAME(cosh, func_sufix)(complex_type z) \ + { \ + const real_type x = z.x; \ + const real_type y = z.y; \ + return (complex_type)(cosh(x) * cos(y), sinh(x) * sin(y)); \ + } \ + \ + complex_type FNAME(tan, func_sufix)(complex_type z) \ + { \ + return FNAME(div, func_sufix)( \ + FNAME(sin, func_sufix)(z), \ + FNAME(cos, func_sufix)(z) \ + ); \ + } \ + \ + complex_type FNAME(tanh, func_sufix)(complex_type z) \ + { \ + return FNAME(div, func_sufix)( \ + FNAME(sinh, func_sufix)(z), \ + FNAME(cosh, func_sufix)(z) \ + ); \ + } \ + \ + complex_type FNAME(asinh, func_sufix)(complex_type z) \ + { \ + complex_type t = (complex_type)( \ + (z.x - z.y) * (z.x + z.y) + CONCAT(1.0, func_sufix), \ + CONCAT(2.0, func_sufix) * z.x * z.y \ + ); \ + t = FNAME(sqrt, func_sufix)(t) + z; \ + return FNAME(log, func_sufix)(t); \ + } \ + \ + complex_type FNAME(asin, func_sufix)(complex_type z) \ + { \ + complex_type t = (complex_type)(-z.y, z.x); \ + t = FNAME(asinh, func_sufix)(t); \ + return (complex_type)(t.y, -t.x); \ + } \ + \ + complex_type FNAME(acosh, func_sufix)(complex_type z) \ + { \ + return \ + CONCAT(2.0, func_sufix) * FNAME(log, func_sufix)( \ + FNAME(sqrt, func_sufix)( \ + CONCAT(0.5, func_sufix) * (z + CONCAT(1.0, func_sufix)) \ + ) \ + + FNAME(sqrt, func_sufix)( \ + CONCAT(0.5, func_sufix) * (z - CONCAT(1.0, func_sufix)) \ + ) \ + ); \ + } \ + \ + complex_type FNAME(acos, func_sufix)(complex_type z) \ + { \ + complex_type t = FNAME(asin, func_sufix)(z);\ + return (complex_type)( \ + CONCAT(M_PI_2, math_consts_sufix) - t.x, -t.y \ + ); \ + } \ + \ + complex_type FNAME(atanh, func_sufix)(complex_type z) \ + { \ + const real_type zy2 = z.y * z.y; \ + real_type n = CONCAT(1.0, func_sufix) + z.x; \ + real_type d = CONCAT(1.0, func_sufix) - z.x; \ + n = zy2 + n * n; \ + d = zy2 + d * d; \ + return (complex_type)( \ + CONCAT(0.25, func_sufix) * (log(n) - log(d)), \ + CONCAT(0.5, func_sufix) * atan2( \ + CONCAT(2.0, func_sufix) * z.y, \ + CONCAT(1.0, func_sufix) - zy2 - (z.x * z.x) \ + ) \ + ); \ + } \ + \ + complex_type FNAME(atan, func_sufix)(complex_type z) \ + { \ + const real_type zx2 = z.x * z.x; \ + real_type n = z.y + CONCAT(1.0, func_sufix); \ + real_type d = z.y - CONCAT(1.0, func_sufix); \ + n = zx2 + n * n; \ + d = zx2 + d * d; \ + return (complex_type)( \ + CONCAT(0.5, func_sufix) * atan2( \ + CONCAT(2.0, func_sufix) * z.x, \ + CONCAT(1.0, func_sufix) - zx2 - (z.y * z.y) \ + ), \ + CONCAT(0.25, func_sufix) * (log(n / d)) \ + ); \ + } + +// float complex +typedef float2 cfloat; +OPENCL_COMPLEX_MATH_FUNCS(float2, float, f, _F) + +// double complex +#ifdef cl_khr_fp64 +# ifdef OPENCL_COMPLEX_MATH_USE_DOUBLE +# pragma OPENCL EXTENSION cl_khr_fp64 : enable + typedef double2 cdouble; + OPENCL_COMPLEX_MATH_FUNCS(double2, double, , ) +# endif +#endif + +#undef FNAME +#undef CONCAT +#endif // OPENCL_COMPLEX_MATH \ No newline at end of file diff --git a/src_opencl/piopencl.cpp b/src_opencl/piopencl.cpp new file mode 100644 index 00000000..5bdab669 --- /dev/null +++ b/src_opencl/piopencl.cpp @@ -0,0 +1,452 @@ +#include "piopencl.h" +#include "CL/cl.h" + + +PRIVATE_DEFINITION_START(PIOpenCL::Context) + cl_context context; + cl_command_queue queue; + PIVector devices; +PRIVATE_DEFINITION_END(PIOpenCL::Context) + + +PRIVATE_DEFINITION_START(PIOpenCL::Program) + cl_program program; +PRIVATE_DEFINITION_END(PIOpenCL::Program) + + +PRIVATE_DEFINITION_START(PIOpenCL::Kernel) + cl_kernel kernel; +PRIVATE_DEFINITION_END(PIOpenCL::Kernel) + + + + +void PIOpenCL::init() { + Initializer::instance(); +} + + +const PIVector & PIOpenCL::platforms() { + return Initializer::instance()->platforms_; +} + + +const PIVector PIOpenCL::devices() { + PIVector ret; + PIVector pl = platforms(); + piForeachC (PIOpenCL::Platform & p, pl) + ret << p.devices; + return ret; +} + + +PIOpenCL::Device PIOpenCL::deviceByID(void * id) { + PIVector pl = platforms(); + piForeachC (PIOpenCL::Platform & p, pl) { + piForeachC (PIOpenCL::Device & d, p.devices) { + if (d.id == id) return d; + } + } + return Device(); +} + + +PIOpenCL::Initializer::Initializer() { + inited_ = false; +} + + +PIOpenCL::Initializer * PIOpenCL::Initializer::instance() { + static PIOpenCL::Initializer * ret = new PIOpenCL::Initializer(); + ret->init(); + return ret; +} + + +void PIOpenCL::Initializer::init() { + if (inited_) return; + inited_ = true; + piCout << "init OpenCL"; + platforms_.clear(); + const int max_size = 256; + cl_platform_id cl_platforms[max_size]; + char buffer[10240]; + cl_int ret = 0; + cl_uint plat_num = 0; + ret = clGetPlatformIDs(max_size, cl_platforms, &plat_num); + if (ret != 0) { + piCout << "[PIOpenCL] Error: OpenCL platforms not found!"; + return; + } + for (uint i = 0; i < plat_num; i++) { + Platform p; + p.id = cl_platforms[i]; + clGetPlatformInfo(cl_platforms[i], CL_PLATFORM_NAME, sizeof(buffer), buffer, 0); + p.name = buffer; + clGetPlatformInfo(cl_platforms[i], CL_PLATFORM_VENDOR, sizeof(buffer), buffer, 0); + p.vendor = buffer; + clGetPlatformInfo(cl_platforms[i], CL_PLATFORM_PROFILE, sizeof(buffer), buffer, 0); + p.profile = buffer; + clGetPlatformInfo(cl_platforms[i], CL_PLATFORM_VERSION, sizeof(buffer), buffer, 0); + p.version = buffer; + clGetPlatformInfo(cl_platforms[i], CL_PLATFORM_EXTENSIONS, sizeof(buffer), buffer, 0); + p.extensions = PIString(buffer).trim().split(" "); + + uint dev_num = 0; + cl_device_id cl_devices[max_size]; + ret = clGetDeviceIDs(cl_platforms[i], CL_DEVICE_TYPE_ALL, max_size, cl_devices, &dev_num); + if (ret == 0) { + //piCout << "[OpenCLBlock] OpenCL cl_devices on platform" + PIString::fromNumber(i) + "found:" << dev_num; + for (uint j = 0; j < dev_num; j++) { + uint buf_uint = 0; + ullong buf_ulong = 0; + Device d; + d.id = cl_devices[j]; + d.platform_id = p.id; + clGetDeviceInfo(cl_devices[j], CL_DEVICE_NAME, sizeof(buffer), buffer, 0); + d.name = buffer; + clGetDeviceInfo(cl_devices[j], CL_DEVICE_VENDOR, sizeof(buffer), buffer, 0); + d.vendor = buffer; + clGetDeviceInfo(cl_devices[j], CL_DEVICE_VERSION, sizeof(buffer), buffer, 0); + d.device_version = buffer; + clGetDeviceInfo(cl_devices[j], CL_DRIVER_VERSION, sizeof(buffer), buffer, 0); + d.driver_version = buffer; + clGetDeviceInfo(cl_devices[j], CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(buf_uint), &buf_uint, 0); + d.max_compute_units = buf_uint; + clGetDeviceInfo(cl_devices[j], CL_DEVICE_MAX_CLOCK_FREQUENCY, sizeof(buf_uint), &buf_uint, 0); + d.max_clock_frequency = buf_uint; + clGetDeviceInfo(cl_devices[j], CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(buf_ulong), &buf_ulong, 0); + d.max_memory_size = buf_ulong; + p.devices << d; + } + } + platforms_ << p; + } +} + + + + +PIOpenCL::Context::Context() { + zero(); +} + + +PIOpenCL::Context::~Context() { + piCout << "destroy context" << this; + deletePrograms(); + if (PRIVATE->queue) + clReleaseCommandQueue(PRIVATE->queue); + if (PRIVATE->context) + clReleaseContext(PRIVATE->context); + zero(); +} + + +void PIOpenCL::Context::zero() { + programs_.clear(); + PRIVATE->context = 0; + PRIVATE->queue = 0; + PRIVATE->devices.clear(); +} + + +void PIOpenCL::Context::deletePrograms() { + piCout << "context: delete" << programs_.size() << "progs"; + PIVector ptdl = programs_; + programs_.clear(); + piForeach (Program * p, ptdl) { + if (p) delete p; + } +} + + +PIOpenCL::Context * PIOpenCL::Context::create(const PIOpenCL::DeviceList & dl) { + if (dl.isEmpty()) return 0; + Context * rc = 0; + PIVector cldl; + for (int i = 0; i < dl.size_s(); ++i) + if (dl[i].isValid()) + cldl << (cl_device_id)dl[i].id; + //piCout << "create for" << dl[0].name << "..."; + cl_int ret = 0; + cl_context con = clCreateContext(0, cldl.size_s(), cldl.data(), 0, 0, &ret); + if (ret != 0) { + piCout << "[PIOpenCL::Context]" << "clCreateContext error" << ret; + return 0; + } + cl_command_queue comq = clCreateCommandQueue(con, cldl[0], 0, &ret); + if (ret != 0) { + piCout << "[PIOpenCL::Context]" << "clCreateCommandQueue error" << ret; + return 0; + } + piCout << "create done for" << dl[0].name; + rc = new Context(); + rc->PRIVATEWB->context = con; + rc->PRIVATEWB->queue = comq; + rc->PRIVATEWB->devices = cldl; + return rc; +} + + +PIOpenCL::Program * PIOpenCL::Context::createProgram(const PIString & source, PIString * error) { + if (error) error->clear(); + if (source.isEmpty()) { + if (error) (*error) = "Empty program!"; + return 0; + } + const char * csrc = source.dataAscii(); + size_t src_size = source.size(); + cl_int ret = 0; + cl_program prog = clCreateProgramWithSource(PRIVATE->context, 1, &csrc, &src_size, &ret); + if (ret != 0) { + piCout << "[PIOpenCL::Context]" << "clCreateProgramWithSource error" << ret; + if (error) (*error) << "clCreateProgramWithSource error " << ret; + return 0; + } + ret = clBuildProgram(prog, 0, 0, "-cl-kernel-arg-info", 0, 0); + char buffer[10240]; + clGetProgramBuildInfo(prog, PRIVATE->devices[0], CL_PROGRAM_BUILD_LOG, sizeof(buffer), buffer, 0); + if (ret != 0) { + clReleaseProgram(prog); + piCout << "[PIOpenCL::Context]" << "clBuildProgram error" << ret;// << ":" << buffer; + if (error) (*error) = buffer; + return 0; + } + size_t uret = 0; + ret = clGetProgramInfo(prog, CL_PROGRAM_NUM_KERNELS, sizeof(uret), &uret, 0); + if (ret != 0) { + clReleaseProgram(prog); + piCout << "[PIOpenCL::Context]" << "clGetProgramInfo error" << ret; + if (error) (*error) = "Can`t retrieve CL_PROGRAM_NUM_KERNELS"; + return 0; + } + const int ccnt = 10240; + char knames[ccnt]; + ret = clGetProgramInfo(prog, CL_PROGRAM_KERNEL_NAMES, ccnt, knames, 0); + if (ret != 0) { + clReleaseProgram(prog); + piCout << "[PIOpenCL::Context]" << "clGetProgramInfo error" << ret; + if (error) (*error) = "Can`t retrieve CL_PROGRAM_KERNEL_NAMES"; + return 0; + } + PIStringList knl = PIString(knames).trim().split(";"); + PIVector kerns; + piForeachC (PIString & k, knl) { + cl_kernel kern = clCreateKernel(prog, k.dataAscii(), &ret); + if (ret != 0) { + piCout << "[PIOpenCL::Context]" << "clCreateKernel" << k << "error" << ret; + if (error) (*error) << "clCreateKernel(\"" << k << "\") error " << ret; + piForeach (void* _k, kerns) + clReleaseKernel((cl_kernel)_k); + clReleaseProgram(prog); + return 0; + } + kerns << kern; + } + //piCout << knl << kerns; + Program * rp = new Program(); + rp->context_ = this; + rp->source_ = source; + rp->PRIVATEWB->program = prog; + if (!rp->initKernels(kerns)) { + delete rp; + return 0; + } + programs_ << rp; + return rp; +} + + + + +PIOpenCL::Program::Program() { + //piCout << "new program" << this; + zero(); +} + + +PIOpenCL::Program::~Program() { + //piCout << "destroy program" << this; + if (context_) + context_->programs_.removeAll(this); + piForeach (Kernel * k, kernels_) + delete k; + if (PRIVATE->program) + clReleaseProgram(PRIVATE->program); + zero(); +} + + +void PIOpenCL::Program::zero() { + context_ = 0; + kernels_.clear(); + PRIVATE->program = 0; +} + + +bool PIOpenCL::Program::initKernels(PIVector kerns) { + piForeach (void * _k, kerns) { + cl_kernel k = (cl_kernel)_k; + //piCout << "init kernel" << k; + Kernel * kern = new Kernel(); + kern->context_ = context_; + kern->program_ = this; + kern->PRIVATEWB->kernel = k; + if (kern->init()) + kernels_ << kern; + else + delete kern; + } + return !kernels_.isEmpty(); +} + + + + +PIOpenCL::Kernel::Kernel() { + zero(); + //piCout << "new Kernel" << this; +} + + +PIOpenCL::Kernel::~Kernel() { + //piCout << "del Kernel" << this; + if (PRIVATE->kernel) + clReleaseKernel(PRIVATE->kernel); +} + + +void PIOpenCL::Kernel::zero() { + PRIVATE->kernel = 0; +} + + +bool PIOpenCL::Kernel::init() { + char kname[1024]; + memset(kname, 0, 1024); + cl_int ret = 0; + ret = clGetKernelInfo(PRIVATE->kernel, CL_KERNEL_FUNCTION_NAME, 1024, kname, 0); + if (ret != 0) { + piCout << "[PIOpenCL::Kernel]" << "clGetKernelInfo(CL_KERNEL_FUNCTION_NAME) error" << ret; + return false; + } + name_ = kname; + cl_uint na = 0; + ret = clGetKernelInfo(PRIVATE->kernel, CL_KERNEL_NUM_ARGS, sizeof(na), &na, 0); + if (ret != 0) { + piCout << "[PIOpenCL::Kernel]" << "clGetKernelInfo(CL_KERNEL_NUM_ARGS) error" << ret; + return false; + } + for (cl_uint i = 0; i < na; ++i) { + KernelArg ka; + ka.init(PRIVATE->kernel, i); + args_ << ka; + } + piCout << "kname" << kname << na; + return true; +} + + +bool PIOpenCL::Kernel::setArgValueV(int index, const PIVariant & value) { + if (index < 0 || index >= args_.size_s()) { + piCout << "[PIOpenCL::Kernel]" << "setArgValue invalid index" << index; + return false; + } + + return true; +} + + +int PIOpenCL::Kernel::argIndex(const PIString & an) const { + for (int i = 0; i < args_.size_s(); ++i) + if (args_[i].arg_name == an) + return i; + return -1; +} + + +PIOpenCL::KernelArg PIOpenCL::Kernel::argByName(const PIString & an) const { + piForeachC (KernelArg & a, args_) + if (a.arg_name == an) + return a; + return KernelArg(); +} + + + + +PIOpenCL::KernelArg::KernelArg() { + address_qualifier = AddressGlobal; + access_qualifier = AccessNone; + type_qualifier = TypeNone; + is_pointer = false; + arg_type = Float; + dims = 1; +} + + +void PIOpenCL::KernelArg::init(void * _k, uint index) { + cl_kernel k = (cl_kernel)_k; + cl_int ret = 0; + char nm[1024]; + memset(nm, 0, 1024); + ret = clGetKernelArgInfo(k, index, CL_KERNEL_ARG_TYPE_NAME, 1024, nm, 0); + if (ret != 0) { + piCout << "[PIOpenCL::Kernel]" << "clGetKernelArgInfo(CL_KERNEL_ARG_TYPE_NAME) error" << ret; + } + type_name = nm; + memset(nm, 0, 1024); + ret = clGetKernelArgInfo(k, index, CL_KERNEL_ARG_NAME, 1024, nm, 0); + if (ret != 0) { + piCout << "[PIOpenCL::Kernel]" << "clGetKernelArgInfo(CL_KERNEL_ARG_NAME) error" << ret; + } + arg_name = nm; + cl_kernel_arg_address_qualifier addq = 0; + ret = clGetKernelArgInfo(k, index, CL_KERNEL_ARG_ADDRESS_QUALIFIER, sizeof(addq), &addq, 0); + if (ret != 0) { + piCout << "[PIOpenCL::Kernel]" << "clGetKernelArgInfo(CL_KERNEL_ARG_ADDRESS_QUALIFIER) error" << ret; + } + switch (addq) { + case CL_KERNEL_ARG_ADDRESS_GLOBAL : address_qualifier = AddressGlobal; break; + case CL_KERNEL_ARG_ADDRESS_LOCAL : address_qualifier = AddressLocal; break; + case CL_KERNEL_ARG_ADDRESS_CONSTANT: address_qualifier = AddressConstant; break; + case CL_KERNEL_ARG_ADDRESS_PRIVATE : address_qualifier = AddressPrivate; break; + } + cl_kernel_arg_access_qualifier accq = 0; + ret = clGetKernelArgInfo(k, index, CL_KERNEL_ARG_ACCESS_QUALIFIER, sizeof(accq), &accq, 0); + if (ret != 0) { + piCout << "[PIOpenCL::Kernel]" << "clGetKernelArgInfo(CL_KERNEL_ARG_ACCESS_QUALIFIER) error" << ret; + } + switch (accq) { + case CL_KERNEL_ARG_ACCESS_READ_ONLY : access_qualifier = AccessReadOnly; break; + case CL_KERNEL_ARG_ACCESS_WRITE_ONLY: access_qualifier = AccessWriteOnly; break; + case CL_KERNEL_ARG_ACCESS_READ_WRITE: access_qualifier = AccessReadWrite; break; + case CL_KERNEL_ARG_ACCESS_NONE : access_qualifier = AccessNone; break; + } + cl_kernel_arg_type_qualifier tq = 0; + ret = clGetKernelArgInfo(k, index, CL_KERNEL_ARG_TYPE_QUALIFIER, sizeof(tq), &tq, 0); + if (ret != 0) { + piCout << "[PIOpenCL::Kernel]" << "clGetKernelArgInfo(CL_KERNEL_ARG_TYPE_QUALIFIER) error" << ret; + } + switch (tq) { + case CL_KERNEL_ARG_TYPE_CONST : type_qualifier = TypeConst; break; + case CL_KERNEL_ARG_TYPE_RESTRICT: type_qualifier = TypeRestrict; break; + case CL_KERNEL_ARG_TYPE_VOLATILE: type_qualifier = TypeVolatile; break; + case CL_KERNEL_ARG_TYPE_NONE : type_qualifier = TypeNone; break; + } + base_type_name = type_name; + is_pointer = false; + if (type_name.endsWith("*")) { + is_pointer = true; + base_type_name.cutRight(1); + } + dims = piMaxi(1, base_type_name.right(1).toInt()); + if (dims > 1) base_type_name.cutRight(1); +} + + +PICout operator <<(PICout s, const PIOpenCL::KernelArg & v) { + s.setControl(0); s << "Arg(" << v.base_type_name << " " << v.arg_name << " (addr=" << v.address_qualifier << ",acc=" << v.access_qualifier << ",typ=" << v.type_qualifier << ",dims=" << v.dims << "))"; + s.restoreControl(); return s; +} diff --git a/src_opencl/resources.conf b/src_opencl/resources.conf new file mode 100644 index 00000000..1fe87114 --- /dev/null +++ b/src_opencl/resources.conf @@ -0,0 +1,2 @@ +[PIOpenCL] +3rd/clcomplex.h diff --git a/utils/resources_compiler/main.cpp b/utils/resources_compiler/main.cpp index 46f4b67b..0249c83e 100644 --- a/utils/resources_compiler/main.cpp +++ b/utils/resources_compiler/main.cpp @@ -8,11 +8,12 @@ using namespace PICoutManipulators; void usage() { piCout << Bold << "PIP Resources Compiler"; piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine; - piCout << Green << Bold << "Usage:" << Default << "\"pirc [-h] -i -o \"" << NewLine; + piCout << Green << Bold << "Usage:" << Default << "\"pirc [-hl] -i -o \"" << NewLine; piCout << Green << Bold << "Details:"; - piCout << "-h --help " << Green << "- display this message and exit"; - piCout << "-i --input " << Green << "- resources description file"; - piCout << "-o --out " << Green << "- output .cpp file"; + piCout << "-h --help " << Green << "- display this message and exit"; + piCout << "-i --input " << Green << "- resources description file"; + piCout << "-o --out " << Green << "- output .cpp file"; + piCout << "-l --list " << Green << "- print readed files from description and exit"; } @@ -21,10 +22,10 @@ int main (int argc, char * argv[]) { cli.addArgument("input", true); cli.addArgument("out", true); cli.addArgument("help"); + cli.addArgument("list"); - if (!cli.hasArgument("input") || - !cli.hasArgument("out") || - cli.hasArgument("help")) { + if (cli.hasArgument("help") || !cli.hasArgument("input") || + (!cli.hasArgument("out") && !cli.hasArgument("list"))) { usage(); return 0; } @@ -36,17 +37,21 @@ int main (int argc, char * argv[]) { piCout << "Error: resources description file is empty"; return 0; } - /*piForeachC (ParserSection & s, files) { - piCout << "[" << s.name << "]"; - piCout << s.files; - }*/ + if (cli.hasArgument("list")) { + piForeachC (ParserSection & s, files) { + piForeachC (ParserEntry & e, s.files) { + piCout << e.path; + } + } + return 0; + } PIString out_file = cli.argumentValue("out"); PIFile outf; if (!out_file.isEmpty()) { if (outf.open(out_file, PIIODevice::ReadWrite)) { outf.clear(); - } else piCout << "Error: while open out file"; + } else piCout << "Error: can`t open out file"; outf << "// Generated by \"PIP Resources Compiler\" " << PIDateTime::current().toString("dd.MM.yyyy hh:mm:ss\n"); outf << "// Execute command:\n"; piForeachC (PIString & _a, cli.rawArguments())