Splitting up API
To address one of my concerns in #39 I would propose to only load the OpenCL API depending on the maximum version available on the machine.
So I would change the api loading in the following way.
- bootstrap.jl -> Minimum bases for finding all platforms and checking the supported version of the platform.
- api/opencl10.jl contains all undepreciated api calls from version 1.0
- api/opencl10-depreciated11.jl contains all api calls from version 1.0 that were depreciated in version 1.1
- api/opencl10-depreciated12.jl contains all api calls from version 1.0 that were depreciated in version 1.2
- api/opencl11.jl includes opencl10.jl
- etc. pp.
module OpenCL
include ("bootstrap.jl")
global OPENCL_VERSION :: VersionNumber
function __init__()
global OPENCL_VERSION = maximum(map(cl.opencl_version, cl.platforms())
if OPENCL_VERSION == v"1.2"
include("api/opencl12.jl")
elseif OPENCL_VERSION == v"1.1"
include("api/opencl11.jl")
include("api/opencl11-depreciated12.jl")
elseif
include("api/opencl10.jl")
include("api/opencl10-depreciated11.jl")
include("api/opencl10-depreciated12.jl")
end
end
include("...everything else")
end
Since we use macros to generate the api calls a include in a function actually works and adds the generated function i the correct scope.
This proposal would also make it easier to add OpenCL 2.0
On a second thought maybe we should not exclude deprecated function just because we have a maximum version of v"1.2" on my machine I have several OpenCL platforms installed one with v"1.2" and one with v"1.1" excluding all depreciated functions would prevent me from working with platform that has the only support for v"1.1".
Instead we should probably have a macro like this
platform = ....
@has_12? begin
# OpenCL 1.2 code
end : @has_11? begin
# OpenCL 1.1 code
end : begin
# Fallback to OpenCL 1.0
end
In the style of @windows, @windows_only, @linux
I really don't see a good way to do this at the moment without runtime reflection. I left the stubs in there just in case some piece of functionality came along which would enable this somehow. The problem is with multiple platforms, I often use devices supporting v1.2 along with devices that support v1.1. I think the best way for now is to reduce to the lowest common denominator api when possible and not prevent the user from calling functions his device does not support, even if it produces impenetrable errors. We could additionally have a "checked mode" which could do the runtime checks and give better error messages. This could be turned off if you didn't want the overhead.
This situation is going to get worse as more devices that support v2.0 come to market.
So with #42 it should be possible to write a macro that extends to
macro has_12?
...
end
@has_12? platfrom begin
code1
end : begin
code2
end
OpenCL.api.OPENCL_VERSION == v"1.2" => true
if OpenCL.opencl_version(platform) == v"1.2"
code1
else
code2
end
OpenCL.api.OPENCL_VERSION == v"1.2" => false
code2
In the first case we have a minimal overhead because we know that there is a platform that support OpenCL 1.2 and in the second case we know for certain that we don't have access to OpenCL1.2 and so the macro turns into a noop.
This is definitely better, but it does not feel very satisfying because it really doesn't do exactly what we would want. Maybe that is impossible currently and we should adopt this until we can figure out a better solution. The most common case is to use only one device / platform where this would be very helpful.
I have the feeling that the current behaviour is very finicky and having a proper robust mechanism in Julia would be highly beneficial.
I do agree having only one platform is the common case and we should optimize for that.
I don't know if it is relevant to the discussion that was started here, but I recently built the OpenCL_jll wrapper with BinaryBuilder.jl and am trying to refactor this package to use it instead. Would that be a good path forward?