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