Skip to content

Latest commit

 

History

87 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

opencl

Project portfolio

Test OpenCL module and advanced example

Generated OpenCL bindings for the V programming language.

Available as antono2.opencl on VPM.

The bindings are generated from Khronos' canonical OpenCL XML registry by antono2/v_opencl_bindings. REGISTRY_COMMIT and HEADERS_COMMIT record the immutable Khronos inputs used for this release. GENERATOR_COMMIT identifies the exact canonical generator revision from which the published module was synchronized.

One-command setup

Install the native OpenCL development prerequisites and this V module:

v run setup.vsh

When running from an installed module, use v run ~/.vmodules/antono2/opencl/setup.vsh. Ubuntu and Debian, Fedora, Arch, openSUSE, macOS, and Windows are supported. v run setup.vsh --check performs a read-only diagnostic pass. On Windows, the script installs the Khronos loader and headers through vcpkg; the current GPU vendor driver still supplies the actual OpenCL implementation. macOS uses its built-in OpenCL framework.

Supported toolchains

Platform V compiler C compiler Validation level
Ubuntu 24.04 V 0.5.2 GCC Runtime kernels, images, SVM, and Vulkan-particle validation smoke tests
Ubuntu 24.04 V 0.5.2 TinyCC Vulkan-particle compile and zero-copy headless smoke test
macOS 14 V 0.5.2 Clang OpenCL framework ABI compilation
Windows Server 2022 V 0.5.2 MSVC OpenCL loader ABI compilation
Ubuntu 24.04 Current V master GCC Advisory runtime compatibility lane

V 0.5.2 is the supported baseline. A successful build confirms loader ABI compatibility; availability of devices and optional features is determined by the installed OpenCL implementation at runtime.

Install the module from VPM:

v install antono2.opencl
import antono2.opencl as cl

fn main() {
	mut count := u32(0)
	result := cl.get_platform_ids(0, unsafe { nil }, &count)
	if result != cl.success {
		panic('clGetPlatformIDs failed: ${result}')
	}
	println('OpenCL platforms: ${count}')
}

Convenience API

The generated functions remain available as the complete low-level API. An opt-in, hand-written layer adds typed errors and safe discovery helpers without hiding native OpenCL handles:

for platform in cl.platforms()! {
	println(cl.platform_info_string(platform, cl.platform_name)!)
	for device in cl.devices(platform, cl.device_type_all)! {
		println('  ${cl.device_info_string(device, cl.device_name)!}')
	}
}

Contexts and queues use explicit, idempotent cleanup:

mut context := cl.new_context(device)!
defer { context.close() or {} }
mut queue := context.command_queue(device, cl.CommandQueueProperties(0))!
defer { queue.close() or {} }

mut buffer := cl.new_buffer[f32](&context, cl.mem_read_write, 1024)!
defer { buffer.close() or {} }
buffer.write(&queue, 0, []f32{len: 1024, init: f32(index)})!

The element type used by Buffer[T], typed transfers, and kernel arguments must be a plain C-layout value without V-managed references such as strings, maps, or slices. Element-count multiplication is checked for overflow before an OpenCL allocation or transfer call.

Source compilation preserves compiler diagnostics through ProgramBuildError. Owned kernels support typed scalar and buffer arguments plus one-dimensional dispatch:

mut program := cl.build_source_program(&context, device, source, '')!
defer { program.close() or {} }
mut kernel := program.kernel('transform')!
defer { kernel.close() or {} }
kernel.set_buffer_arg(0, buffer.handle)!
kernel.set_slice_arg(1, [f32(0.5), 1.0])! // e.g. an OpenCL float2
kernel.enqueue_1d(&queue, usize(buffer.count), 0)!

Non-blocking transfers and dispatch return owned events and accept native event dependency lists. Host slices must remain alive until their transfer event completes:

mut uploaded := buffer.write_async(&queue, 0, values, []cl.Event{})!
mut dispatched := kernel.enqueue_1d_after(&queue, usize(buffer.count), 0,
	[uploaded.handle])!
mut downloaded := buffer.read_async(&queue, 0, mut result, [dispatched.handle])!
downloaded.wait()!
profile := downloaded.profile()! // queue must use cl.queue_profiling_enable
downloaded.close()!
dispatched.close()!
uploaded.close()!

For multidimensional kernels, enqueue_nd_after() accepts one to three global dimensions and either a matching local-size slice or an empty slice for an implementation-selected work-group size.

Typed 2D images validate that T represents one complete pixel, provide checked full-image and region transfers, and bind directly to kernels alongside owned samplers:

format := cl.ImageFormat{
	image_channel_order:     cl.rgba
	image_channel_data_type: cl.unorm_int8
}
mut image := cl.new_image_2d[u32](&context, cl.mem_read_write, format, 64, 64)!
defer { image.close() or {} }
mut sampler := cl.new_sampler(&context, false, cl.address_clamp_to_edge,
	cl.filter_nearest)!
defer { sampler.close() or {} }
image.write(&queue, pixels)!
image.set_kernel_arg(&kernel, 0)!
kernel.set_sampler_arg(1, &sampler)!

Shared virtual memory is similarly typed and capability-gated. Coarse-grained allocations can use checked copies or explicit map/unmap transitions, and can be bound directly to a kernel:

svm_capabilities := cl.device_svm_support(device)!
if svm_capabilities & (cl.device_svm_coarse_grain_buffer |
	cl.device_svm_fine_grain_buffer) != 0 {
	mut shared := cl.new_svm[u32](&context, cl.mem_read_write, 1024, 0)!
	defer { shared.close() }
	shared.write(&queue, 0, values)!
	shared.set_kernel_arg(&kernel, 0)!
}

Apple's OpenCL 1.2 framework does not expose SVM entry points, so SVM capability discovery reports the feature as unavailable on macOS. Image support remains available according to the selected device's advertised formats.

Optional features can be discovered once without substring matching or unsafe UUID buffers:

capabilities := cl.device_capabilities(device)!
if capabilities.has_all(['cl_khr_external_memory',
	'cl_khr_external_memory_opaque_fd']) && capabilities.device_uuid {
	device_uuid := capabilities.uuid()!
}

Opaque-FD external objects use the same explicit ownership and event model. File descriptors are obtained from the exporting API; its handle-ownership rules still apply:

memory_interop := cl.load_external_memory_interop(platform, capabilities)!
mut shared := memory_interop.import_opaque_fd_buffer[f32](&context, memory_fd,
	element_count, cl.mem_read_write)!
defer { shared.close() or {} }

semaphore_interop := cl.load_external_semaphore_interop(platform, capabilities)!
mut ready := semaphore_interop.import_opaque_fd(&context, semaphore_fd)!
defer { ready.close() or {} }
mut waited := ready.wait(&queue, [])!
mut acquired := memory_interop.acquire(&queue, [shared.handle], [waited.handle])!
defer { acquired.close() or {} }
defer { waited.close() or {} }

Owned events expose explicit wait lists without manual reference counting:

mut uploaded := queue.marker([]cl.Event{})!
defer { uploaded.close() or {} }
mut ready := queue.barrier([uploaded.handle])!
defer { ready.close() or {} }
ready.wait()!

See API_DESIGN.md for the conventions shared with the companion Vulkan convenience layer. See OWNERSHIP.md for the current copy and cleanup rules.

Advanced example

examples/vector_add is a compact introduction to the owned convenience API. It runs asynchronous buffer uploads, a kernel, profiled readback, and explicit cleanup.

examples/image_svm copies a typed RGBA image through an image kernel and owned sampler, then executes a second kernel directly over a typed SVM allocation when the selected device advertises buffer SVM support.

examples/vulkan_particles is an interactive particle-galaxy example that combines OpenCL compute with Vulkan presentation. On UUID-matched devices it imports one exported Vulkan allocation into OpenCL and synchronizes access with reusable opaque-FD semaphores. It also includes a portable host-staged fallback, swapchain recreation, velocity trails, interactive controls, and display-independent interoperability smoke tests.

The example is a separate nested V module, so its vulkan and glfw dependencies are not dependencies of applications that only import opencl.

The module exposes all 114 cumulative OpenCL 1.0 through 3.0 commands and 14 portable Khronos extension entry points with V-style snake-case wrappers, including platform and device discovery, contexts, queues, memory and images, programs, kernels, events, profiling, synchronization, and object lifecycle. The bindings generator reads command prototypes, types, pointer depth, and all OpenCL 1.0 through 3.0 core constants from Khronos' XML registry. Constants are exposed using their corresponding OpenCL typedefs. Core command callbacks use named V function types, allowing callback signatures to be checked at compile time while optional callbacks still accept unsafe { nil }. The initial extension set covers cl_khr_il_program, cl_khr_create_command_queue, cl_khr_subgroups, and cl_khr_suggested_local_work_size. Zero-copy synchronization support covers cl_khr_semaphore, cl_khr_external_semaphore, and cl_khr_external_memory, including opaque-FD, DMA-BUF, and sync-file handle variants. cl_khr_device_uuid provides UUID, LUID, and node-mask device queries for matching an OpenCL device with another compute or graphics API. Optional extension commands are resolved through the ICD at runtime instead of being required linker symbols, so applications that do not use them can still build against older OpenCL loaders.

CI exercises complete typed buffer, image/sampler, and SVM kernel paths, OpenCL 1.1 user events, an OpenCL 1.2 marker-with-wait-list dependency, and OpenCL 2.0 property-list queue creation and SVM allocation on PoCL. OpenCL 2.1 coverage additionally checks synchronized device and host timer queries; IL programs, kernel cloning, subgroup queries, and SVM migration are present in the generated API. OpenCL 2.2 adds program specialization constants and program-release callbacks. OpenCL 3.0 adds numeric version helpers, NameVersion, context destructor callbacks, and property-based buffer and image creation.

About

Generated OpenCL 1.0–3.0 bindings for V, including external-memory and semaphore interoperability.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages