ocl

repository·master·Indexed 21 days ago

https://github.com/cogciprocate/ocl

A collection of Rust bindings and interfaces for OpenCL. The ecosystem includes the high-level ocl crate for intuitive interaction with minimal boilerplate, ocl-core for low-level interfaces and types, cl-sys for raw C FFI bindings, and specialized crates like ocl-interop for OpenGL interoperability and ocl-core-vector for OpenCL-compatible vector primitives.

Tokens
35.1K
Snippets
94
Records
135
Agent score
73%

What's inside ocl

  1. Use ocl-extras for common types and components

    master
    The ocl-extras package provides various types and components that are used within the ocl library's own examples and tests. While primarily intended for internal use, these components are available for developers to use in their own projects to simplify common OpenCL tasks or patterns found in the ocl ecosystem.
  2. Understand the difference between ocl and ocl-core

    master

    The ocl crate provides a simplified, intuitive interface designed to minimize boilerplate and maximize ease of use.

    If you require access to the complete, conventional OpenCL feature set in a style that closely mirrors the standard OpenCL C API (while maintaining Rust's safety and convenience), use the ocl-core crate instead.

  3. Choose between ocl-core and ocl crates

    master

    The ocl-core crate provides low-level OpenCL interfaces and types. It is more verbose and requires more manual setup.

    For a higher-level, easier-to-use, and significantly less verbose interface that compiles to virtually the same underlying code, use the ocl crate instead. You can compare the two by looking at the trivial.rs example in the ocl repository.

  4. Use the ocl crate instead of cl-sys for high-level OpenCL

    master
    The cl-sys crate provides low-level OpenCL C FFI bindings. If you require a high-level, easier-to-use, and less verbose OpenCL interface that compiles to virtually the same machine code, you should use the ocl crate instead.
  5. Use OpenCL + OpenGL Interoperability

    master

    To enable interoperability, you must first have an active OpenGL context. You can then obtain an OpenCL context that supports OpenGL interop using ocl_interop::get_context().

    Note: get_context() returns the first available GPU device on your system that supports OpenGL interop. If this does not work for your specific hardware, you may need to manually select a device and create the context.

    Once you have an OpenGL buffer, you can wrap it in an OpenCL buffer using ocl::Buffer::from_gl_buffer. To use the buffer in OpenCL kernels, you must explicitly acquire it via gl_acquire() and release it via gl_release() to manage the ownership transfer between OpenGL and OpenCL.

    extern crate ocl_interop;
    
    // 1. Ensure an OpenGL context is created and active...
    
    // 2. Create an OpenCL context with interop enabled
    let context = ocl_interop::get_context()?;
    
    // 3. Create an OpenCL buffer from an existing OpenGL buffer
    // (Assuming `queue` is an ocl::Queue and `gl_buffer` is your OpenGL buffer)
    let cl_buffer = ocl::Buffer::<f32>::from_gl_buffer(&queue, None, gl_buffer)?;
    
    // 4. Acquire the buffer to make it usable by OpenCL
    cl_buffer.cmd().gl_acquire().enq()?;
    
    // ... Use the buffer in OpenCL commands ...
    
    // 5. Release the acquisition so OpenGL can use it again
    cl_buffer.cmd().gl_release().enq()?;
  6. Install and set up ocl

    master

    To use ocl in your Rust project, ensure that an OpenCL library is installed on your platform and that a diagnostic tool like clinfo is available to verify the installation.

    1. Add ocl to your Cargo.toml dependencies.
    2. Add extern crate ocl; to your crate root (lib.rs or main.rs).
    [dependencies]
    ocl = "0.19"
    extern crate ocl;
  7. Manage Memory with Mem and MemMap

    master

    OpenCL memory is handled via two primary types:

    1. Mem: A wrapper for cl_mem objects (buffers/images). It supports cloning (incrementing reference counts) and is used for both device and host-side memory.
    2. MemMap<T>: Represents a pointer to a region of mapped (pinned) memory. This is used to access memory directly from the host.

    MemMap<T> Methods:

    • as_ptr(): Returns a *const T.
    • as_mut_ptr(): Returns a *mut T.
    • as_slice(len): Returns a &[T] slice of the mapped memory.
    • as_slice_mut(len): Returns a &mut [T] slice of the mapped memory.

    Warning: MemMap<T> does not implement Sync. It is not thread-safe without external synchronization (like a Mutex).

    // Accessing mapped memory as a slice
    let slice = mem_map.as_slice(1024);
  8. Use the FullDeviceInfo trait for convenient device queries

    master

    The FullDeviceInfo trait provides a safe and convenient interface for accessing OpenCL device information. Instead of using the standard device.info() method, which returns a DeviceInfoResult enum that requires manual pattern matching and conversion, FullDeviceInfo provides direct methods that return the appropriate type (e.g., u32, String, bool) wrapped in an OclResult.

    This trait is implemented for ocl::Device and is useful when querying multiple different types of device information to reduce boilerplate code.

    use ocl_extras::full_device_info::FullDeviceInfo;
    
    // Instead of manual matching:
    // let compute_units = match device.info(DeviceInfo::MaxComputeUnits)? {
    //     DeviceInfoResult::MaxComputeUnits(c) => c,
    //     _ => panic!("..."),
    // };
    
    // Use the trait directly:
    let compute_units = device.max_compute_units()?;
  9. How events and queues manage command ordering

    master

    In ocl, you can manage the execution order of commands using two primary mechanisms:

    1. Queues: Commands enqueued in the same queue are typically executed in order. You can use .queue(&queue) on a command builder to specify which queue to use.
    2. Events: Events allow you to create temporal dependencies between commands, even across different queues.
      • Use .enew(&mut event) to attach an event to a command that signals its completion.
      • Use .ewait(&event) on a subsequent command to ensure it does not execute until the specified event has been signaled.

    This is particularly useful when using out-of-order queues where you need to explicitly synchronize specific tasks.