1 package net.bmahe.genetics4j.gpu.opencl;
2
3 import java.util.Arrays;
4 import java.util.List;
5 import java.util.Objects;
6
7 import org.apache.commons.lang3.Validate;
8 import org.jocl.CL;
9 import org.jocl.Pointer;
10 import org.jocl.Sizeof;
11 import org.jocl.cl_device_id;
12 import org.jocl.cl_platform_id;
13
14 /**
15 * Utility class providing convenient methods for OpenCL device discovery and information queries.
16 *
17 * <p>DeviceUtils encapsulates the low-level OpenCL API calls required for device enumeration and property retrieval,
18 * providing a higher-level interface for GPU-accelerated evolutionary algorithm implementations. This class handles the
19 * OpenCL buffer management and type conversions necessary for interacting with the native OpenCL runtime.
20 *
21 * <p>Key functionality includes:
22 * <ul>
23 * <li><strong>Device enumeration</strong>: Discover available devices on OpenCL platforms</li>
24 * <li><strong>Property queries</strong>: Retrieve device characteristics and capabilities</li>
25 * <li><strong>Type conversions</strong>: Convert between OpenCL native types and Java types</li>
26 * <li><strong>Buffer management</strong>: Handle memory allocation for OpenCL information queries</li>
27 * </ul>
28 *
29 * <p>Common usage patterns:
30 *
31 * <pre>{@code
32 * // Enumerate devices on a platform
33 * int deviceCount = DeviceUtils.numDevices(platformId);
34 * List<cl_device_id> deviceIds = DeviceUtils.getDeviceIds(platformId, deviceCount);
35 *
36 * // Query device properties
37 * String deviceName = DeviceUtils.getDeviceInfoString(deviceId, CL.CL_DEVICE_NAME);
38 * int computeUnits = DeviceUtils.getDeviceInfoInt(deviceId, CL.CL_DEVICE_MAX_COMPUTE_UNITS);
39 * long maxWorkGroupSize = DeviceUtils.getDeviceInfoLong(deviceId, CL.CL_DEVICE_MAX_WORK_GROUP_SIZE);
40 *
41 * // Query array properties
42 * int maxDimensions = DeviceUtils.getDeviceInfoInt(deviceId, CL.CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS);
43 * long[] maxWorkItemSizes = DeviceUtils
44 * .getDeviceInfoLongArray(deviceId, CL.CL_DEVICE_MAX_WORK_ITEM_SIZES, maxDimensions);
45 * }</pre>
46 *
47 * <p>Device type filtering:
48 * <ul>
49 * <li><strong>CL_DEVICE_TYPE_ALL</strong>: All available devices</li>
50 * <li><strong>CL_DEVICE_TYPE_GPU</strong>: GPU devices only</li>
51 * <li><strong>CL_DEVICE_TYPE_CPU</strong>: CPU devices only</li>
52 * <li><strong>CL_DEVICE_TYPE_ACCELERATOR</strong>: Accelerator devices only</li>
53 * </ul>
54 *
55 * <p>Error handling:
56 * <ul>
57 * <li><strong>Parameter validation</strong>: Validates all input parameters</li>
58 * <li><strong>OpenCL error propagation</strong>: OpenCL errors are propagated as runtime exceptions</li>
59 * <li><strong>Memory management</strong>: Automatically handles buffer allocation and cleanup</li>
60 * </ul>
61 *
62 * @see Device
63 * @see DeviceReader
64 * @see net.bmahe.genetics4j.gpu.opencl.model.DeviceType
65 */
66 public class DeviceUtils {
67
68 private DeviceUtils() {
69
70 }
71
72 /**
73 * Returns the number of OpenCL devices of the specified type available on the platform.
74 *
75 * @param platformId the OpenCL platform to query
76 * @param deviceType the type of devices to count (e.g., CL_DEVICE_TYPE_GPU, CL_DEVICE_TYPE_ALL)
77 * @return the number of available devices of the specified type
78 * @throws IllegalArgumentException if platformId is null
79 */
80 public static int numDevices(final cl_platform_id platformId, final long deviceType) {
81 Objects.requireNonNull(platformId);
82
83 final int[] numDevices = new int[1];
84 CL.clGetDeviceIDs(platformId, deviceType, 0, null, numDevices);
85
86 return numDevices[0];
87 }
88
89 /**
90 * Returns the total number of OpenCL devices available on the platform.
91 *
92 * <p>This is equivalent to calling {@link #numDevices(cl_platform_id, long)} with {@code CL_DEVICE_TYPE_ALL} as the
93 * device type.
94 *
95 * @param platformId the OpenCL platform to query
96 * @return the total number of available devices on the platform
97 * @throws IllegalArgumentException if platformId is null
98 */
99 public static int numDevices(final cl_platform_id platformId) {
100 return numDevices(platformId, CL.CL_DEVICE_TYPE_ALL);
101 }
102
103 /**
104 * Returns a list of OpenCL device identifiers of the specified type from the platform.
105 *
106 * @param platformId the OpenCL platform to query
107 * @param numDevices the number of devices to retrieve
108 * @param deviceType the type of devices to retrieve (e.g., CL_DEVICE_TYPE_GPU, CL_DEVICE_TYPE_ALL)
109 * @return list of OpenCL device identifiers
110 * @throws IllegalArgumentException if platformId is null or numDevices is not positive
111 */
112 public static List<cl_device_id> getDeviceIds(final cl_platform_id platformId, final int numDevices,
113 final long deviceType) {
114 Objects.requireNonNull(platformId);
115 Validate.isTrue(numDevices > 0);
116
117 cl_device_id[] deviceIds = new cl_device_id[numDevices];
118 CL.clGetDeviceIDs(platformId, deviceType, numDevices, deviceIds, null);
119
120 return Arrays.asList(deviceIds);
121 }
122
123 /**
124 * Returns a list of all OpenCL device identifiers from the platform.
125 *
126 * <p>This is equivalent to calling {@link #getDeviceIds(cl_platform_id, int, long)} with {@code CL_DEVICE_TYPE_ALL}
127 * as the device type.
128 *
129 * @param platformId the OpenCL platform to query
130 * @param numDevices the number of devices to retrieve
131 * @return list of all OpenCL device identifiers
132 * @throws IllegalArgumentException if platformId is null or numDevices is not positive
133 */
134 public static List<cl_device_id> getDeviceIds(final cl_platform_id platformId, final int numDevices) {
135 return getDeviceIds(platformId, numDevices, CL.CL_DEVICE_TYPE_ALL);
136 }
137
138 /**
139 * Queries and returns a string property of the specified OpenCL device.
140 *
141 * <p>This method handles the OpenCL API calls and buffer management required to retrieve string properties from
142 * devices, such as device name, vendor, or version information.
143 *
144 * @param deviceId the OpenCL device to query
145 * @param parameter the OpenCL parameter constant (e.g., CL_DEVICE_NAME, CL_DEVICE_VENDOR)
146 * @return the string value of the requested device property
147 * @throws IllegalArgumentException if deviceId is null
148 */
149 public static String getDeviceInfoString(final cl_device_id deviceId, final int parameter) {
150 Objects.requireNonNull(deviceId);
151
152 final long[] size = new long[1];
153 CL.clGetDeviceInfo(deviceId, parameter, 0, null, size);
154
155 final byte[] buffer = new byte[(int) size[0]];
156 CL.clGetDeviceInfo(deviceId, parameter, buffer.length, Pointer.to(buffer), null);
157
158 return new String(buffer, 0, buffer.length - 1);
159 }
160
161 /**
162 * Queries and returns a long array property of the specified OpenCL device.
163 *
164 * <p>This method is useful for retrieving array properties such as maximum work-item sizes per dimension, which
165 * require multiple values to fully describe the device capability.
166 *
167 * @param deviceId the OpenCL device to query
168 * @param parameter the OpenCL parameter constant (e.g., CL_DEVICE_MAX_WORK_ITEM_SIZES)
169 * @param size the number of long values to retrieve
170 * @return array of long values for the requested device property
171 * @throws IllegalArgumentException if deviceId is null or size is not positive
172 */
173 public static long[] getDeviceInfoLongArray(final cl_device_id deviceId, final int parameter, final int size) {
174 Objects.requireNonNull(deviceId);
175 Validate.isTrue(size > 0);
176
177 final long[] values = new long[size];
178 CL.clGetDeviceInfo(deviceId, parameter, Sizeof.cl_long * size, Pointer.to(values), null);
179
180 return values;
181 }
182
183 /**
184 * Queries and returns a single long property of the specified OpenCL device.
185 *
186 * <p>This method is useful for retrieving single long value properties such as maximum work group size, global
187 * memory size, or local memory size.
188 *
189 * @param deviceId the OpenCL device to query
190 * @param parameter the OpenCL parameter constant (e.g., CL_DEVICE_MAX_WORK_GROUP_SIZE)
191 * @return the long value of the requested device property
192 * @throws IllegalArgumentException if deviceId is null
193 */
194 public static long getDeviceInfoLong(final cl_device_id deviceId, final int parameter) {
195 Objects.requireNonNull(deviceId);
196
197 final long[] values = getDeviceInfoLongArray(deviceId, parameter, 1);
198
199 return values[0];
200 }
201
202 /**
203 * Queries and returns a single integer property of the specified OpenCL device.
204 *
205 * <p>This method is useful for retrieving integer properties such as maximum compute units, maximum clock frequency,
206 * or maximum work-item dimensions.
207 *
208 * @param deviceId the OpenCL device to query
209 * @param parameter the OpenCL parameter constant (e.g., CL_DEVICE_MAX_COMPUTE_UNITS)
210 * @return the integer value of the requested device property
211 * @throws IllegalArgumentException if deviceId is null
212 */
213 public static int getDeviceInfoInt(final cl_device_id deviceId, final int parameter) {
214 Objects.requireNonNull(deviceId);
215
216 final int[] values = new int[1];
217 CL.clGetDeviceInfo(deviceId, parameter, Sizeof.cl_int, Pointer.to(values), null);
218
219 return values[0];
220 }
221 }