I'm currently trying to edit a project, that already uses OpenCL.dll, to make it load the library dynamically. I want to be able to use it in no OpenCL systems, with only a error mesage and a feature disabled.
First, I added some wrappers to the functions. (This code in inside a C++ class, and is public)
typedef cl_int h_clGetPlatformIDs(cl_uint, cl_platform_id *, cl_uint *);
typedef cl_int h_clGetDeviceIDs(cl_platform_id, cl_device_type, cl_uint, cl_device_id *, cl_uint *);
typedef cl_context h_clCreateContext(cl_context_properties *, cl_uint, const cl_device_id *, void *(const char *, const void *, size_t, void *), void *, cl_int *);
typedef cl_command_queue h_clCreateCommandQueue(cl_context, cl_device_id, cl_command_queue_properties, cl_int *);
typedef cl_program h_clCreateProgramWithSource(cl_context, cl_uint, const char **, const size_t *, cl_int *);
typedef cl_int (CALLBACK h_clBuildProgram)(cl_program, cl_uint,const cl_device_id *, const char *, void (*)(cl_program, void * ), void * ) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int h_clGetProgramBuildInfo(cl_program, cl_device_id, cl_program_build_info, size_t, void *, size_t *);
typedef cl_kernel h_clCreateKernel(cl_program, const char *, cl_int *);
typedef cl_mem h_clCreateBuffer(cl_context, cl_mem_flags, size_t, void *, cl_int *);
typedef cl_int h_clEnqueueWriteBuffer(cl_command_queue, cl_mem, cl_bool, size_t, size_t, const void *, cl_uint, const cl_event *, cl_event *);
typedef cl_int h_clSetKernelArg(cl_kernel, cl_uint, size_t, const void *);
typedef cl_int h_clEnqueueNDRangeKernel(cl_command_queue, cl_kernel, cl_uint, const size_t*, const size_t*, const size_t*, cl_uint, const cl_event*, cl_event*);
typedef cl_int h_clFlush(cl_command_queue);
typedef cl_int h_clEnqueueReadBuffer(cl_command_queue, cl_mem, cl_bool, size_t, size_t, void *, cl_uint, const cl_event *, cl_event *);
typedef cl_int h_clWaitForEvents(cl_uint, const cl_event *);
typedef cl_int h_clReleaseMemObject(cl_mem );
typedef cl_int h_clReleaseEvent(cl_event );
typedef cl_int h_clReleaseProgram(cl_program );
typedef cl_int h_clReleaseKernel(cl_kernel);
typedef cl_int h_clReleaseCommandQueue(cl_command_queue );
typedef cl_int h_clReleaseContext(cl_context );
h_clGetPlatformIDs* clGetPlatformIDs;
h_clGetDeviceIDs* clGetDeviceIDs;
h_clCreateContext* clCreateContext;
h_clCreateCommandQueue* clCreateCommandQueue;
h_clCreateProgramWithSource* clCreateProgramWithSource;
h_clBuildProgram* clBu开发者_StackOverflowildProgram;
h_clGetProgramBuildInfo* clGetProgramBuildInfo;
h_clCreateKernel* clCreateKernel;
h_clCreateBuffer* clCreateBuffer;
h_clEnqueueWriteBuffer* clEnqueueWriteBuffer;
h_clSetKernelArg* clSetKernelArg;
h_clEnqueueNDRangeKernel* clEnqueueNDRangeKernel;
h_clFlush* clFlush;
h_clEnqueueReadBuffer* clEnqueueReadBuffer;
h_clWaitForEvents* clWaitForEvents;
h_clReleaseMemObject* clReleaseMemObject;
h_clReleaseEvent* clReleaseEvent;
h_clReleaseProgram* clReleaseProgram;
h_clReleaseKernel* clReleaseKernel;
h_clReleaseCommandQueue* clReleaseCommandQueue;
h_clReleaseContext* clReleaseContext;
With that, I'm able to directly assign the handlers what GetProcAdress returns, and after that just call the function (of course, I load the DLL first).
clReleaseContext = (h_clReleaseContext*) GetProcAddress(ocl_lib_handle, "clReleaseContext");
And a common call example:
clReleaseContext((cl_context)context);
But I always get SEGFAULT calling this one:
clBuildProgram(program, 0, NULL, "-cl-fast-relaxed-math", NULL, NULL);
It's strange, since all the other ones work OK. I post here the clBuildProgram Args as reference:
extern CL_API_ENTRY cl_int CL_API_CALL
clBuildProgram(cl_program /* program */,
cl_uint /* num_devices */,
const cl_device_id * /* device_list */,
const char * /* options */,
void (*pfn_notify)(cl_program /* program */, void * /* user_data */),
void * /* user_data */) CL_API_SUFFIX__VERSION_1_0;
Thanks!
Your typedefs have to match the declaration in the OpenCL headers exactly. They don't, you don't use CL_API_ENTRY, CL_API_CALL. I don't see CALLBACK in the original declaration for the 1st argument.
This is of course horrible code to write and maintain. The clBuildProgram() function has otherwise plenty of opportunities to bomb on an access violation without you helping. Smoke this out first with a test program that gets your main code running properly. Beg, steal or borrow to take advantage of the MSVC linker's /DELAYLOAD feature.
Each time you call GetProcAddress
you should check the return for NULL to see if you were able to find the function in the DLL.
If it returns NULL when you try to load clBuildProgram, then something is wrong with the function name lookup.
If it gives you a valid pointer, but the typedef doesn't match the signature exactly, then you would pass corrupt data to the function, and possibly crash.
If you've got a valid pointer and a correct typedef, then perhaps you're just passing it bad arguments? I see a lot of 0
and NULL
in your call to clBuildProgram
- perhaps the bug is in the CL code? Can you call clBuildProgram
with the same arguments when you call it directly (i.e. without the dynamic loading)?
One possible int : you're merging APIs in C and C++ which don't have the same ABI, passing parameter from the binary is different (that's why when using C inside C++, you need to use
extern "C" {
#include "c_api.h"
}
You should google around this subject, to see how you can force your handlers to be called using the C call convention / mangling. Not sure it's the solution, but it's definitely worth investigating as it would lead to this exact result.
I don't know what exacty fixed the problem. I rewrote a .h file with a copy of the typedefs of OpenCL files with the add of __stdcall. Then use extern "C".
And is working!!
Thank you all!
精彩评论