1
+ module nanoOpenCL
2
+
3
+ import OpenCL_jll
4
+ import pocl_jll
5
+
6
+ const libopencl = OpenCL_jll. libopencl # TODO directly use POCL
7
+
8
+ """
9
+ @checked function foo(...)
10
+ rv = ...
11
+ return rv
12
+ end
13
+
14
+ Macro for wrapping a function definition returning a status code. Two versions of the
15
+ function will be generated: `foo`, with the function body wrapped by an invocation of the
16
+ `check` function (to be implemented by the caller of this macro), and `unchecked_foo` where no
17
+ such invocation is present and the status code is returned to the caller.
18
+ """
19
+ macro checked (ex)
20
+ # parse the function definition
21
+ @assert Meta. isexpr (ex, :function )
22
+ sig = ex. args[1 ]
23
+ @assert Meta. isexpr (sig, :call )
24
+ body = ex. args[2 ]
25
+ @assert Meta. isexpr (body, :block )
26
+
27
+ # we need to detect the first API call, so add an initialization check
28
+ body = quote
29
+ if ! initialized[]
30
+ initialize ()
31
+ end
32
+ $ body
33
+ end
34
+
35
+ # generate a "safe" version that performs a check
36
+ safe_body = quote
37
+ check () do
38
+ $ body
39
+ end
40
+ end
41
+ safe_sig = Expr (:call , sig. args[1 ], sig. args[2 : end ]. .. )
42
+ safe_def = Expr (:function , safe_sig, safe_body)
43
+
44
+ # generate a "unchecked" version that returns the error code instead
45
+ unchecked_sig = Expr (:call , Symbol (" unchecked_" , sig. args[1 ]), sig. args[2 : end ]. .. )
46
+ unchecked_def = Expr (:function , unchecked_sig, body)
47
+
48
+ return esc (:($ safe_def, $ unchecked_def))
49
+ end
50
+
51
+ const CL_SUCCESS = 0
52
+
53
+ const CL_DEVICE_NOT_FOUND = - 1
54
+
55
+ const CL_DEVICE_NOT_AVAILABLE = - 2
56
+
57
+ const CL_PLATFORM_NOT_FOUND_KHR = - 1001
58
+
59
+ const CL_PLATFORM_PROFILE = 0x0900
60
+
61
+ const CL_PLATFORM_VERSION = 0x0901
62
+
63
+ const CL_PLATFORM_NAME = 0x0902
64
+
65
+ const CL_PLATFORM_VENDOR = 0x0903
66
+
67
+ const CL_PLATFORM_EXTENSIONS = 0x0904
68
+
69
+ const CL_PLATFORM_HOST_TIMER_RESOLUTION = 0x0905
70
+
71
+ const CL_PLATFORM_NUMERIC_VERSION = 0x0906
72
+
73
+ const CL_PLATFORM_EXTENSIONS_WITH_VERSION = 0x0907
74
+
75
+ const CL_DEVICE_TYPE_DEFAULT = 1 << 0
76
+
77
+ const CL_DEVICE_TYPE_CPU = 1 << 1
78
+
79
+ const CL_DEVICE_TYPE_GPU = 1 << 2
80
+
81
+ const CL_DEVICE_TYPE_ACCELERATOR = 1 << 3
82
+
83
+ const CL_DEVICE_TYPE_CUSTOM = 1 << 4
84
+
85
+ const CL_DEVICE_TYPE_ALL = 0xffffffff
86
+
87
+ const CL_DEVICE_TYPE = 0x1000
88
+
89
+
90
+ function check (f)
91
+ res = f ()
92
+
93
+ if res != CL_SUCCESS
94
+ throw_api_error (res)
95
+ end
96
+
97
+ return
98
+ end
99
+
100
+ const cl_int = Int32
101
+
102
+ const cl_uint = UInt32
103
+
104
+ const cl_ulong = UInt64
105
+
106
+ mutable struct _cl_platform_id end
107
+
108
+ mutable struct _cl_device_id end
109
+
110
+ const cl_platform_id = Ptr{_cl_platform_id}
111
+
112
+ const cl_device_id = Ptr{_cl_device_id}
113
+
114
+ const cl_bitfield = cl_ulong
115
+
116
+ const cl_device_type = cl_bitfield
117
+
118
+ const cl_platform_info = cl_uint
119
+
120
+ const cl_device_info = cl_uint
121
+
122
+
123
+
124
+ @checked function clGetPlatformIDs (num_entries, platforms, num_platforms)
125
+ @ccall libopencl. clGetPlatformIDs (num_entries:: cl_uint , platforms:: Ptr{cl_platform_id} ,
126
+ num_platforms:: Ptr{cl_uint} ):: cl_int
127
+ end
128
+
129
+ @checked function clGetPlatformInfo (platform, param_name, param_value_size, param_value,
130
+ param_value_size_ret)
131
+ @ccall libopencl. clGetPlatformInfo (platform:: cl_platform_id ,
132
+ param_name:: cl_platform_info ,
133
+ param_value_size:: Csize_t , param_value:: Ptr{Cvoid} ,
134
+ param_value_size_ret:: Ptr{Csize_t} ):: cl_int
135
+ end
136
+
137
+ @checked function clGetDeviceIDs (platform, device_type, num_entries, devices, num_devices)
138
+ @ccall libopencl. clGetDeviceIDs (platform:: cl_platform_id , device_type:: cl_device_type ,
139
+ num_entries:: cl_uint , devices:: Ptr{cl_device_id} ,
140
+ num_devices:: Ptr{cl_uint} ):: cl_int
141
+ end
142
+
143
+ @checked function clGetDeviceInfo (device, param_name, param_value_size, param_value,
144
+ param_value_size_ret)
145
+ @ccall libopencl. clGetDeviceInfo (device:: cl_device_id , param_name:: cl_device_info ,
146
+ param_value_size:: Csize_t , param_value:: Ptr{Cvoid} ,
147
+ param_value_size_ret:: Ptr{Csize_t} ):: cl_int
148
+ end
149
+
150
+ # Init
151
+
152
+ # lazy initialization
153
+ const initialized = Ref {Bool} (false )
154
+ @noinline function initialize ()
155
+ initialized[] = true
156
+
157
+ @static if Sys. iswindows ()
158
+ if is_high_integrity_level ()
159
+ @warn """ Running at high integrity level, preventing OpenCL.jl from loading drivers from JLLs.
160
+
161
+ Only system drivers will be available. To enable JLL drivers, do not run Julia as an administrator."""
162
+ end
163
+ end
164
+
165
+ ocd_filenames = join (OpenCL_jll. drivers, ' :' )
166
+ if haskey (ENV , " OCL_ICD_FILENAMES" )
167
+ ocd_filenames *= " :" * ENV [" OCL_ICD_FILENAMES" ]
168
+ end
169
+
170
+ withenv (" OCL_ICD_FILENAMES" => ocd_filenames) do
171
+ num_platforms = Ref {Cuint} ()
172
+ @ccall libopencl. clGetPlatformIDs (
173
+ 0 :: cl_uint , C_NULL :: Ptr{cl_platform_id} ,
174
+ num_platforms:: Ptr{cl_uint} ):: cl_int
175
+
176
+ if num_platforms[] == 0 && isempty (OpenCL_jll. drivers)
177
+ @error """ No OpenCL drivers available, either system-wide or provided by a JLL.
178
+
179
+ Please install a system-wide OpenCL driver, or load one together with OpenCL.jl,
180
+ e.g., by doing `using OpenCL, pocl_jll`."""
181
+ end
182
+ end
183
+ end
184
+
185
+ # Julia API
186
+
187
+ struct Platform
188
+ id:: cl_platform_id
189
+ end
190
+
191
+ Base. unsafe_convert (:: Type{cl_platform_id} , p:: Platform ) = p. id
192
+
193
+ function platforms ()
194
+ nplatforms = Ref {Cuint} ()
195
+ res = unchecked_clGetPlatformIDs (0 , C_NULL , nplatforms)
196
+ if res == CL_PLATFORM_NOT_FOUND_KHR || nplatforms[] == 0
197
+ return Platform[]
198
+ elseif res != CL_SUCCESS
199
+ throw (CLError (res))
200
+ end
201
+ cl_platform_ids = Vector {cl_platform_id} (undef, nplatforms[])
202
+ clGetPlatformIDs (nplatforms[], cl_platform_ids, C_NULL )
203
+ return [Platform (id) for id in cl_platform_ids]
204
+ end
205
+
206
+
207
+ function Base. getproperty (p:: Platform , s:: Symbol )
208
+ # simple string properties
209
+ version_re = r" OpenCL (?<major>\d +)\. (?<minor>\d +)(?<vendor>.+)"
210
+ @inline function get_string (prop)
211
+ sz = Ref {Csize_t} ()
212
+ clGetPlatformInfo (p, prop, 0 , C_NULL , sz)
213
+ chars = Vector {Cchar} (undef, sz[])
214
+ clGetPlatformInfo (p, prop, sz[], chars, C_NULL )
215
+ return GC. @preserve chars unsafe_string (pointer (chars))
216
+ end
217
+ if s === :profile
218
+ return get_string (CL_PLATFORM_PROFILE)
219
+ elseif s === :version
220
+ str = get_string (CL_PLATFORM_VERSION)
221
+ m = match (version_re, str)
222
+ if m === nothing
223
+ error (" Could not parse OpenCL version string: $str " )
224
+ end
225
+ return strip (m[" vendor" ])
226
+ elseif s === :opencl_version
227
+ str = get_string (CL_PLATFORM_VERSION)
228
+ m = match (version_re, str)
229
+ if m === nothing
230
+ error (" Could not parse OpenCL version string: $str " )
231
+ end
232
+ return VersionNumber (parse (Int, m[" major" ]), parse (Int, m[" minor" ]))
233
+ elseif s === :name
234
+ return get_string (CL_PLATFORM_NAME)
235
+ elseif s === :vendor
236
+ return get_string (CL_PLATFORM_VENDOR)
237
+ end
238
+
239
+ if s == :extensions
240
+ size = Ref {Csize_t} ()
241
+ clGetPlatformInfo (p, CL_PLATFORM_EXTENSIONS, 0 , C_NULL , size)
242
+ result = Vector {Cchar} (undef, size[])
243
+ clGetPlatformInfo (p, CL_PLATFORM_EXTENSIONS, size[], result, C_NULL )
244
+ return GC. @preserve result split (unsafe_string (pointer (result)))
245
+ end
246
+ return getfield (p, s)
247
+ end
248
+
249
+ struct Device
250
+ id:: cl_device_id
251
+ end
252
+
253
+ Base. unsafe_convert (:: Type{cl_device_id} , d:: Device ) = d. id
254
+
255
+ function devices (p:: Platform , dtype)
256
+ ndevices = Ref {Cuint} ()
257
+ ret = unchecked_clGetDeviceIDs (p, dtype, 0 , C_NULL , ndevices)
258
+ if ret == CL_DEVICE_NOT_FOUND || ndevices[] == 0
259
+ return Device[]
260
+ elseif ret != CL_SUCCESS
261
+ throw (CLError (ret))
262
+ end
263
+ result = Vector {cl_device_id} (undef, ndevices[])
264
+ clGetDeviceIDs (p, dtype, ndevices[], result, C_NULL )
265
+ return Device[Device (id) for id in result]
266
+ end
267
+
268
+ function default_device (p:: Platform )
269
+ devs = devices (p, CL_DEVICE_TYPE_DEFAULT)
270
+ isempty (devs) && return nothing
271
+ # XXX : clGetDeviceIDs documents CL_DEVICE_TYPE_DEFAULT should only return one device,
272
+ # but it's been observed to return multiple devices on some platforms...
273
+ return first (devs)
274
+ end
275
+
276
+ devices (p:: Platform ) = devices (p, CL_DEVICE_TYPE_ALL)
277
+
278
+ end
0 commit comments