PlatformUtils.java

package net.bmahe.genetics4j.gpu.opencl;

import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.Validate;
import org.jocl.CL;
import org.jocl.Pointer;
import org.jocl.cl_platform_id;

/**
 * Utility class providing convenient methods for OpenCL platform discovery and information queries.
 * 
 * <p>PlatformUtils encapsulates the low-level OpenCL API calls required for platform enumeration and
 * property retrieval, providing a higher-level interface for GPU-accelerated evolutionary algorithm
 * implementations. This class handles the OpenCL buffer management and type conversions necessary
 * for interacting with the native OpenCL runtime.
 * 
 * <p>Key functionality includes:
 * <ul>
 * <li><strong>Platform enumeration</strong>: Discover available OpenCL platforms in the system</li>
 * <li><strong>Property queries</strong>: Retrieve platform characteristics and capabilities</li>
 * <li><strong>Device counting</strong>: Count available devices on each platform</li>
 * <li><strong>Buffer management</strong>: Handle memory allocation for OpenCL information queries</li>
 * </ul>
 * 
 * <p>Common usage patterns:
 * <pre>{@code
 * // Enumerate platforms in the system
 * int platformCount = PlatformUtils.numPlatforms();
 * List<cl_platform_id> platformIds = PlatformUtils.platformIds(platformCount);
 * 
 * // Query platform properties
 * for (cl_platform_id platformId : platformIds) {
 *     String platformName = PlatformUtils.getStringParameter(platformId, CL.CL_PLATFORM_NAME);
 *     String vendor = PlatformUtils.getStringParameter(platformId, CL.CL_PLATFORM_VENDOR);
 *     String version = PlatformUtils.getStringParameter(platformId, CL.CL_PLATFORM_VERSION);
 *     
 *     // Count devices on this platform
 *     int deviceCount = PlatformUtils.numDevices(platformId);
 *     int gpuCount = PlatformUtils.numDevices(platformId, CL.CL_DEVICE_TYPE_GPU);
 * }
 * }</pre>
 * 
 * <p>Platform discovery workflow:
 * <ol>
 * <li><strong>System enumeration</strong>: Query the number of available platforms</li>
 * <li><strong>Platform retrieval</strong>: Get platform identifiers for all available platforms</li>
 * <li><strong>Property queries</strong>: Retrieve platform characteristics for filtering</li>
 * <li><strong>Device counting</strong>: Determine available devices for each platform</li>
 * </ol>
 * 
 * <p>Error handling:
 * <ul>
 * <li><strong>Parameter validation</strong>: Validates all input parameters</li>
 * <li><strong>OpenCL error propagation</strong>: OpenCL errors are propagated as runtime exceptions</li>
 * <li><strong>Memory management</strong>: Automatically handles buffer allocation and cleanup</li>
 * <li><strong>Empty platform handling</strong>: Gracefully handles systems with no OpenCL platforms</li>
 * </ul>
 * 
 * @see Platform
 * @see PlatformReader
 * @see net.bmahe.genetics4j.gpu.opencl.model.PlatformProfile
 */
public class PlatformUtils {

	private PlatformUtils() {

	}

	/**
	 * Returns the number of OpenCL platforms available in the system.
	 * 
	 * @return the number of available OpenCL platforms
	 */
	public static int numPlatforms() {
		final int[] numPlatforms = new int[1];
		CL.clGetPlatformIDs(0, null, numPlatforms);

		return numPlatforms[0];
	}

	/**
	 * Returns a list of OpenCL platform identifiers for all available platforms.
	 * 
	 * @param numPlatforms the number of platforms to retrieve
	 * @return list of OpenCL platform identifiers, or empty list if numPlatforms is 0
	 * @throws IllegalArgumentException if numPlatforms is negative
	 */
	public static List<cl_platform_id> platformIds(final int numPlatforms) {
		Validate.isTrue(numPlatforms >= 0);

		if (numPlatforms == 0) {
			return List.of();
		}

		final cl_platform_id[] platformIds = new cl_platform_id[numPlatforms];
		CL.clGetPlatformIDs(platformIds.length, platformIds, null);

		return Arrays.asList(platformIds);
	}

	/**
	 * Queries and returns a string property of the specified OpenCL platform.
	 * 
	 * <p>This method handles the OpenCL API calls and buffer management required to retrieve
	 * string properties from platforms, such as platform name, vendor, or version information.
	 * 
	 * @param platformId the OpenCL platform to query
	 * @param platformName the OpenCL parameter constant (e.g., CL_PLATFORM_NAME, CL_PLATFORM_VENDOR)
	 * @return the string value of the requested platform property
	 * @throws IllegalArgumentException if platformId is null
	 */
	public static String getStringParameter(final cl_platform_id platformId, final int platformName) {
		Validate.notNull(platformId);

		final long[] parameterSize = new long[1];
		CL.clGetPlatformInfo(platformId, platformName, 0, null, parameterSize);

		final byte[] buffer = new byte[(int) parameterSize[0]];
		CL.clGetPlatformInfo(platformId, platformName, buffer.length, Pointer.to(buffer), null);

		return new String(buffer, 0, buffer.length - 1);
	}

	/**
	 * Returns the number of OpenCL devices of the specified type available on the platform.
	 * 
	 * @param platformId the OpenCL platform to query
	 * @param deviceType the type of devices to count (e.g., CL_DEVICE_TYPE_GPU, CL_DEVICE_TYPE_ALL)
	 * @return the number of available devices of the specified type
	 * @throws IllegalArgumentException if platformId is null
	 */
	public static int numDevices(final cl_platform_id platformId, final long deviceType) {
		Validate.notNull(platformId);

		int[] numDevices = new int[1];
		CL.clGetDeviceIDs(platformId, deviceType, 0, null, numDevices);

		return numDevices[0];
	}

	/**
	 * Returns the total number of OpenCL devices available on the platform.
	 * 
	 * <p>This is equivalent to calling {@link #numDevices(cl_platform_id, long)} with
	 * {@code CL_DEVICE_TYPE_ALL} as the device type.
	 * 
	 * @param platformId the OpenCL platform to query
	 * @return the total number of available devices on the platform
	 * @throws IllegalArgumentException if platformId is null
	 */
	public static int numDevices(final cl_platform_id platformId) {
		Validate.notNull(platformId);
		
		return numDevices(platformId, CL.CL_DEVICE_TYPE_ALL);
	}
}