嘗試編譯openvx_sample,下載相關代碼。
下載的sample code直接使用make能夠生成libopenvx.so。
使用python Build.py --os linux能夠編譯sample code。html
主要包含7個部分:node
一個平臺對就一個target,一個target包含多個kernel。python
./sample/framework/vx_context.c中的變量定義了幾種target支持, c_model, opencl, openmp:linux
vx_char targetModules[][VX_MAX_TARGET_NAME] = {
"openvx-c_model",
#if defined(EXPERIMENTAL_USE_OPENCL)
"openvx-opencl",
#endif
#if defined(EXPERIMENTAL_USE_OPENMP)
"openvx-openmp"
#endif
};
以OpenCL爲例,當用戶調用函數vxCreateContext(sample/framework/vx_context.c)時,其會調用函數ownLoadTarget (sample/framework/vx_target.c), 去dlopen打開libopenvx-opencl.so
, 使用dlsym(mod, name)獲取vxTargetInit, vxTargetAddKernel(sample/targets/opencl/vx_interface.c)等opencl的相關函數句柄。git
而在vxTargetAddKernel函數中,調用ownInitializeKernel(sample/framework/vx_kernel.c)加載了全部OpenCL實現的kernel函數。github
在sample/targets/opencl目錄下的c文件定義了一些vx_cl_kernel_description_t box3x3_clkernel變量,包括box3x3_clkernel, gaussian3x3_clkernel, and_kernel等 ,這些kernelapi
opencl kernel結構:ide
包含vx_kernel_description_t還有一些其它屬性,它把function置爲NULL,並提供了一個sourcepath變量用來存放opencl函數。函數
typedef struct _vx_cl_kernel_description_t {
vx_kernel_description_t description;
char sourcepath[VX_CL_MAX_PATH];
char kernelname[VX_MAX_KERNEL_NAME];
cl_program program[VX_CL_MAX_PLATFORMS];
cl_kernel kernels[VX_CL_MAX_PLATFORMS];
cl_uint num_kernels[VX_CL_MAX_PLATFORMS];
cl_int returns[VX_CL_MAX_PLATFORMS][VX_CL_MAX_DEVICES];
void *reserved; /* for additional data */
} vx_cl_kernel_description_t;
kernel結構:學習
typedef struct _vx_kernel_description_t {
/*! \brief The vx_kernel_e enum */
vx_enum enumeration;
/*! \brief The name that kernel will be used with \ref vxGetKernelByName. */
vx_char name[VX_MAX_KERNEL_NAME];
/*! \brief The pointer to the function to execute the kernel */
vx_kernel_f function;
/*! \brief The pointer to the array of parameter descriptors */
vx_param_description_t *parameters;
/*! \brief The number of paraemeters in the array. */
vx_uint32 numParams;
/*! \brief The parameters validator */
vx_kernel_validate_f validate;
/*! \brief The input validator (deprecated in openvx 1.1) */
void* input_validate;
/*! \brief The output validator (deprecated in openvx 1.1) */
void* output_validate;
/*! \brief The initialization function */
vx_kernel_initialize_f initialize;
/*! \brief The deinitialization function */
vx_kernel_deinitialize_f deinitialize;
} vx_kernel_description_t;
能夠看到目前雖然配置了一些參數,但OpenCL分爲主機端代碼和device端代碼,device端代碼在kernel/opencl中,而host端代碼在哪呢?如何根據設置的參數去執行Host端代碼,從而執行device端代碼:
能夠看到在vxTargetInit函數中,調用ownInitializeKernel初始化kernel時,判斷了kfunc是否爲NULL,(kfunc == NULL ? vxclCallOpenCLKernel : kfunc)若是爲NULL則使用vxclCallOpenCLKernel函數。
咱們再看vxclCallOpenCLKernel函數,咱們發現這個函數裏有clSetKernelArg,clEnqueueNDRangeKernel等OpenCL的API函數,這個即是host-side的OpenCL代碼。
在sample/framework/vx_node_api.c中定義了全部提供的可用的OpenVX結點,包括vxScaleImageNode結點,經過以下方法建立Node:
vx_kernel kernel = vxGetKernelByEnum( context, VX_KERNEL_SCALE_IMAGE );
若是函數有兩種實現,那麼按照優先級使用: opencl > openmp > c_model。(不對,感受優先使用的是c_model的函數;實際是先找到opencl kernel,但找到以後並無中止查找,找到後面的c_model就會覆蓋掉前面的opencl kernel。不知道這兒是寫錯了,仍是就是要優先使用c_model,代碼見sample/framework/vx_kernel.c中的vxGetKernelByEnum函數)
node的參數如何傳遞給kernel: 在vxCreateNodeByStructure中調用vxSetParameterByIndex將Node的參數傳遞kernel。
vx_graph.c會調用每個結點的validator函數,包括inputValidator,outputValidator,確保構建的Graph能夠跑通。
vxProcessGraph函數調用vxExecuteGraph函數,在其中調用action = target->funcs.process(target, &node, 0, 1);
,其中的funcs.process
就是各個target的vxTargetProcess
函數。
在vxTargetProcess中會調用nodes[n]->kernel->function,即咱們事先定義的host-side端代碼,傳遞結點,參數,以及參數個數:
status = nodes[n]->kernel->function((vx_node)nodes[n], (vx_reference *)nodes[n]->parameters, nodes[n]->kernel->signature.num_parameters);
而咱們的function,則主要負責內存管理,以及調用device端代碼。
幾種參數類型:
memory:
CL_MEM_OBJECT_BUFFER
CL_MEM_OBJECT_IMAGE2D
scalar:
VX_TYPE_SCALAR
threashold:
VX_TYPE_THRESHOLD
使用Makefile編譯出來的so默認是沒有opencl。
使用Build.py出來的so能夠有opencl,但結點報錯:
Target[1] is not valid!
Target[2] is not valid!
LOG: [ status = -17 ] Node: org.khronos.openvx.color_convert: parameter[1] is not a valid type 1280!
在target.mak中對SYSDEFS添加EXPERIMENTAL_USE_OPENCL,能夠編譯Opencl,但在運行時build opencl 代碼時報錯,能夠將錯誤信息打印出來,發現找不到頭文件。
查看代碼,發如今sample/targets/opencl/vx_interface.c中須要以下兩個參數,VX_CL_INCLUDE_DIR是VX頭文件位置,VX_CL_SOURCE_DIR是CL源碼位置,在環境中能夠配置這兩個參數:
char *vx_incs = getenv("VX_CL_INCLUDE_DIR");
char *cl_dirs = getenv("VX_CL_SOURCE_DIR");
/usr/include/features.h:367:12: fatal error: 'sys/cdefs.h' file not found
在cl編譯命令裏(sample/targets/opencl/vx_interface.c)添加-I /usr/include/x86_64-linux-gnu/
:
snprintf(cl_args, sizeof(cl_args), "-D VX_CL_KERNEL -I %s -I /usr/include/x86_64-linux-gnu/ -I %s %s %s", vx_incs, cl_dirs...
Linux gnu/stubs-32.h: No such file or directory
這是缺乏32位的嵌入式C庫。在嵌入式開發環境配置時,也常遇到這個問題。sudo apt-get install libc6-dev-i386
fatal error: 'stddef.h' file not found
定位stddef.h, 在cl編譯命令裏cl_args
裏添加-I /usr/include/linux/
。
vx_khr_opencl.h和vx_api.h裏有些類型進行了重定義:
不要在vx_khr_opencl.h裏include vx_api.h。
histogram.cl仍然報錯,將histogram的kernel去掉,就能夠成功編譯。
使用c_model的VX_KERNEL_NOT能夠正常運行,使用opencl的就會報以下錯誤:
clSetKernelArg: OpenCL error CL_INVALID_ARG_INDEX at vxclCallOpenCLKernel in /home/guru_ge/openvx/openvx_sample/sample/targets/opencl/vx_interface.c:639
clSetKernelArg: OpenCL error CL_INVALID_ARG_INDEX at vxclCallOpenCLKernel in /home/guru_ge/openvx/openvx_sample/sample/targets/opencl/vx_interface.c:639
clEnqueueNDRangeKernel: OpenCL error CL_INVALID_KERNEL_ARGS at vxclCallOpenCLKernel in /home/guru_ge/openvx/openvx_sample/sample/targets/opencl/vx_interface.c:724
嘗試本身寫host side的code。
寫完發現並在Load時就不經過,檢查緣由,打開log信息,發如今本身實現的代碼中有個CL_ERROR_MSG找不到,直接註釋該行代碼,程序能夠正常運行。可是獲得的結果仍是不對,全是黑色,好像是沒有將處理後的結果拷貝回來,致使結果全是0。
這是由於cl中提供兩種形式的表達,一個是image2d_t,一個是簡單的buffer,在vx_interface.c中編譯cl時,加上了CL_USE_LUMINANCE,使用的是image2d_t;而在編譯整個OpenVX時,沒有加上CL_USE_LUMINANCE,致使外面使用的是簡單的buffer,而一個image2d_t的參數若是使用buffer須要傳遞5個參數,因此致使最後設置參數時兩邊不一致出錯。修改concerto/target.mak在31行SYSDEFS里加上CL_USE_LUMINANCE就能夠了。
雖然不報錯了,可是出來的結果竟然是一條直線,而不是取反後的效果,很奇怪:
難道是傳給opencl的圖像就不對?嘗試手動拷貝圖像數據。
嘗試學習opencl c 語法,修改代碼查看結果,發現openvx在實現opencl的時候not kernel時存在一些不規範的地方,可能這些問題在其它平臺能夠運行,但到如今這個平臺上就不行了。
原來的kernel實現:
__kernel void vx_not(read_only image2d_t a, write_only image2d_t b) {
int2 coord = (get_global_id(0), get_global_id(1));
write_imageui(b, coord, ~read_imageui(a, nearest_clamp, coord));
}
首先我嘗試打印其像素座標時,發現獲得的x, y座標老是相同的,這很奇怪,這也解釋了爲何結果只有一條直線,由於它只寫了x, y座標相同的那些像素點的值。查看 API發現get_global_id返回的是size_t,因此要用(int)去顯示轉換一下,再打印時,發現座標在不停的變換,變成正常的了。
再運行,獲得的圖竟然是一幅全白的圖,說明像素值還有問題。嘗試打印原像素值,與取反後的像素值,發現相加不是255,說明這裏的取反操做也有問題。read_imageui返回的類型是uint4向量,咱們取反時,獲得的結果並不對,這裏使用255直接相減,最後代碼以下所示:
__kernel void vx_not(read_only image2d_t a, write_only image2d_t b) {
int2 coord = (int2)(get_global_id(0), get_global_id(1));
write_imageui(b, coord, 255-read_imageui(a, nearest_clamp, coord));
}
獲得的效果正確了,以下:
實現opencl scale報錯:
parameter[1] is an invalid dimension 640x240
傳遞的參數是(inputImg, outputImg, type),parameter[1]應該是輸出圖像,大小確實應該是640x240。
使用c_model中的outputvalidator就不報這個錯了,說明不能直接return VX_SUCCESS,可能validator中還須要作些其它的事情。
在validator中會記錄一些信息,以供後面verify時與實際傳入參數比對,因此不能直接返回SUCCESS:
ptr->type = VX_TYPE_IMAGE; ptr->dim.image.format = VX_DF_IMAGE_U8; ptr->dim.image.width = width; ptr->dim.image.height = height;
然而如今又報以下錯誤:
clEnqueueNDRangeKernel: OpenCL error CL_INVALID_EVENT at vxclCallOpenCLKernel in /home/guru_ge/openvx/openvx_sample/sample/targets/opencl/vx_interface.c:725
clEnqueueReadImage: OpenCL error CL_INVALID_EVENT at vxclCallOpenCLKernel in /home/guru_ge/openvx/openvx_sample/sample/targets/opencl/vx_interface.c:793
爲何event會invalid呢?嘗試本身寫host-side代碼,不使用默認的。
本身的代碼報以下錯誤:
VX_ZONE_ERROR:[vxcl_platform_notifier:59] CL_OUT_OF_RESOURCES error executing CL_COMMAND_READ_IMAGE on GeForce GTX 1080 Ti (Device 0)
spec裏解釋CL_OUT_OF_RESOURCES: if there is a failure to allocate resources required by the OpenCL implementation on the device.
這估計是使用c_model的validator致使沒有初始化cl_mem,嘗試使用cl validator。
在check scale node parameter時報以下錯誤:
LOG: [ status = -10 ] Node[3] org.khronos.openvx.image_scaling: parameter[2] failed input/bi validation!
這估計是Input validator裏只容許Image類型,沒有判斷scalar類型。
因此validator要對每一個參數逐一判斷,對於input參數,直接返回SUCCESS就能夠了;而對output參數,還須要寫一些信息。
結果仍是全黑的,在kernel中打印座標發現也不對,查看代碼發現輸入的維度是輸入圖片的大小,這兒應該是輸出圖像的大小纔對。
再運行仍是黑色,發如今取座標轉換時,沒有將float轉爲int,致使有問題(因此類型要確保徹底一致,不會替你作轉換)。修改後,能夠正常運行。
__kernel void image_scaling(read_only image2d_t in, write_only image2d_t out) {
//從glob_id中獲取目標像素座標
int2 coordinate = (int2)(get_global_id(0), get_global_id(1));
//計算歸一化浮點座標
float2 normalizedCoordinate = convert_float2(coordinate) * (float2)(2, 2);
//根據歸一化座標從原圖中讀取像素數據
uint4 colour = read_imageui(in, sampler, convert_int2(normalizedCoordinate));
//將像素數據寫入目標圖像
write_imageui(out, coordinate, colour);
}
實際比較,vx_not, vx_scale使用opencl, c_model實現時間對比:
opencl:
average time: 44099.857143 us
c_model:
average time: 68343.380952 us
程序中經過獲取VX_ZONE_MASK環境變量的值來設置Log級別,能夠經過以下將全部級別信息都打開:
export VX_ZONE_MASK=fffff
一共有以下幾個級別,每一個級別佔int的一個bit位:
enum vx_debug_zone_e {
VX_ZONE_ERROR = 0, /*!< Used for most errors */
VX_ZONE_WARNING = 1, /*!< Used to warning developers of possible issues */
VX_ZONE_API = 2, /*!< Used to trace API calls and return values */
VX_ZONE_INFO = 3, /*!< Used to show run-time processing debug */
VX_ZONE_PERF = 4, /*!< Used to show performance information */
VX_ZONE_CONTEXT = 5,
VX_ZONE_OSAL = 6,
VX_ZONE_REFERENCE = 7,
VX_ZONE_ARRAY = 8,
VX_ZONE_IMAGE = 9,
VX_ZONE_SCALAR = 10,
VX_ZONE_KERNEL = 11,
VX_ZONE_GRAPH = 12,
VX_ZONE_NODE = 13,
VX_ZONE_PARAMETER = 14,
VX_ZONE_DELAY = 15,
VX_ZONE_TARGET = 16,
VX_ZONE_LOG = 17,
VX_ZONE_MAX = 32
};
AMD openvx實現:
https://github.com/GPUOpen-ProfessionalCompute-Libraries/amdovx-core
https://github.com/GPUOpen-ProfessionalCompute-Libraries/amdovx-modules