pub struct Program { /* private fields */ }
Expand description

Abstraction that contains everything to run an OpenCL kernel on a GPU.

The majority of methods are the same as crate::cuda::Program, so you can write code using this API, which will then work with OpenCL as well as CUDA kernels.

Implementations§

source§

impl Program

source

pub fn device_name(&self) -> &str

Returns the name of the GPU, e.g. “GeForce RTX 3090”.

source

pub fn from_opencl(device: &Device, src: &str) -> Result<Program, GPUError>

Creates a program for a specific device from OpenCL source code.

Examples found in repository?
examples/add.rs (line 17)
14
15
16
17
18
19
fn opencl(device: &Device) -> Program {
    let opencl_kernel = include_str!("./add.cl");
    let opencl_device = device.opencl_device().unwrap();
    let opencl_program = opencl::Program::from_opencl(opencl_device, opencl_kernel).unwrap();
    Program::Opencl(opencl_program)
}
source

pub fn from_binary(device: &Device, bin: Vec<u8>) -> Result<Program, GPUError>

Creates a program for a specific device from a compiled OpenCL binary.

source

pub unsafe fn create_buffer<T>( &self, length: usize ) -> Result<Buffer<T>, GPUError>

Creates a new buffer that can be used for input/output with the GPU.

The length is the number of elements to create.

It is usually used to create buffers that are initialized by the GPU. If you want to directly transfer data from the host to the GPU, you would use the safe Program::create_buffer_from_slice instead.

Safety

This function isn’t actually unsafe, it’s marked as unsafe due to the CUDA version of it, where it is unsafe. This is done to have symmetry between both APIs.

Examples found in repository?
examples/add.rs (line 38)
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
pub fn main() {
    // Define some data that should be operated on.
    let aa: Vec<u32> = vec![1, 2, 3, 4];
    let bb: Vec<u32> = vec![5, 6, 7, 8];

    // This is the core. Here we write the interaction with the GPU independent of whether it is
    // CUDA or OpenCL.
    let closures = program_closures!(|program, _args| -> Result<Vec<u32>, GPUError> {
        // Make sure the input data has the same length.
        assert_eq!(aa.len(), bb.len());
        let length = aa.len();

        // Copy the data to the GPU.
        let aa_buffer = program.create_buffer_from_slice(&aa)?;
        let bb_buffer = program.create_buffer_from_slice(&bb)?;

        // The result buffer has the same length as the input buffers.
        let result_buffer = unsafe { program.create_buffer::<u32>(length)? };

        // Get the kernel.
        let kernel = program.create_kernel("add", 1, 1)?;

        // Execute the kernel.
        kernel
            .arg(&(length as u32))
            .arg(&aa_buffer)
            .arg(&bb_buffer)
            .arg(&result_buffer)
            .run()?;

        // Get the resulting data.
        let mut result = vec![0u32; length];
        program.read_into_buffer(&result_buffer, &mut result)?;

        Ok(result)
    });

    // Get the first available device.
    let device = *Device::all().first().unwrap();

    // First we run it on CUDA.
    let cuda_program = cuda(device);
    let cuda_result = cuda_program.run(closures, ()).unwrap();
    assert_eq!(cuda_result, [6, 8, 10, 12]);
    println!("CUDA result: {:?}", cuda_result);

    // Then we run it on OpenCL.
    let opencl_program = opencl(device);
    let opencl_result = opencl_program.run(closures, ()).unwrap();
    assert_eq!(opencl_result, [6, 8, 10, 12]);
    println!("OpenCL result: {:?}", opencl_result);
}
source

pub fn create_buffer_from_slice<T>( &self, slice: &[T] ) -> Result<Buffer<T>, GPUError>

Creates a new buffer on the GPU and initializes with the given slice.

Examples found in repository?
examples/add.rs (line 34)
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
pub fn main() {
    // Define some data that should be operated on.
    let aa: Vec<u32> = vec![1, 2, 3, 4];
    let bb: Vec<u32> = vec![5, 6, 7, 8];

    // This is the core. Here we write the interaction with the GPU independent of whether it is
    // CUDA or OpenCL.
    let closures = program_closures!(|program, _args| -> Result<Vec<u32>, GPUError> {
        // Make sure the input data has the same length.
        assert_eq!(aa.len(), bb.len());
        let length = aa.len();

        // Copy the data to the GPU.
        let aa_buffer = program.create_buffer_from_slice(&aa)?;
        let bb_buffer = program.create_buffer_from_slice(&bb)?;

        // The result buffer has the same length as the input buffers.
        let result_buffer = unsafe { program.create_buffer::<u32>(length)? };

        // Get the kernel.
        let kernel = program.create_kernel("add", 1, 1)?;

        // Execute the kernel.
        kernel
            .arg(&(length as u32))
            .arg(&aa_buffer)
            .arg(&bb_buffer)
            .arg(&result_buffer)
            .run()?;

        // Get the resulting data.
        let mut result = vec![0u32; length];
        program.read_into_buffer(&result_buffer, &mut result)?;

        Ok(result)
    });

    // Get the first available device.
    let device = *Device::all().first().unwrap();

    // First we run it on CUDA.
    let cuda_program = cuda(device);
    let cuda_result = cuda_program.run(closures, ()).unwrap();
    assert_eq!(cuda_result, [6, 8, 10, 12]);
    println!("CUDA result: {:?}", cuda_result);

    // Then we run it on OpenCL.
    let opencl_program = opencl(device);
    let opencl_result = opencl_program.run(closures, ()).unwrap();
    assert_eq!(opencl_result, [6, 8, 10, 12]);
    println!("OpenCL result: {:?}", opencl_result);
}
source

pub fn create_kernel( &self, name: &str, global_work_size: usize, local_work_size: usize ) -> Result<Kernel<'_>, GPUError>

Returns a kernel.

The global_work_size does not follow the OpenCL definition. It is not the total number of threads. Instead it follows CUDA’s definition and is the number of local_work_size sized thread groups. So the total number of threads is global_work_size * local_work_size.

Examples found in repository?
examples/add.rs (line 41)
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
pub fn main() {
    // Define some data that should be operated on.
    let aa: Vec<u32> = vec![1, 2, 3, 4];
    let bb: Vec<u32> = vec![5, 6, 7, 8];

    // This is the core. Here we write the interaction with the GPU independent of whether it is
    // CUDA or OpenCL.
    let closures = program_closures!(|program, _args| -> Result<Vec<u32>, GPUError> {
        // Make sure the input data has the same length.
        assert_eq!(aa.len(), bb.len());
        let length = aa.len();

        // Copy the data to the GPU.
        let aa_buffer = program.create_buffer_from_slice(&aa)?;
        let bb_buffer = program.create_buffer_from_slice(&bb)?;

        // The result buffer has the same length as the input buffers.
        let result_buffer = unsafe { program.create_buffer::<u32>(length)? };

        // Get the kernel.
        let kernel = program.create_kernel("add", 1, 1)?;

        // Execute the kernel.
        kernel
            .arg(&(length as u32))
            .arg(&aa_buffer)
            .arg(&bb_buffer)
            .arg(&result_buffer)
            .run()?;

        // Get the resulting data.
        let mut result = vec![0u32; length];
        program.read_into_buffer(&result_buffer, &mut result)?;

        Ok(result)
    });

    // Get the first available device.
    let device = *Device::all().first().unwrap();

    // First we run it on CUDA.
    let cuda_program = cuda(device);
    let cuda_result = cuda_program.run(closures, ()).unwrap();
    assert_eq!(cuda_result, [6, 8, 10, 12]);
    println!("CUDA result: {:?}", cuda_result);

    // Then we run it on OpenCL.
    let opencl_program = opencl(device);
    let opencl_result = opencl_program.run(closures, ()).unwrap();
    assert_eq!(opencl_result, [6, 8, 10, 12]);
    println!("OpenCL result: {:?}", opencl_result);
}
source

pub fn write_from_buffer<T>( &self, buffer: &mut Buffer<T>, data: &[T] ) -> Result<(), GPUError>

Puts data from an existing buffer onto the GPU.

source

pub fn read_into_buffer<T>( &self, buffer: &Buffer<T>, data: &mut [T] ) -> Result<(), GPUError>

Reads data from the GPU into an existing buffer.

Examples found in repository?
examples/add.rs (line 53)
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
pub fn main() {
    // Define some data that should be operated on.
    let aa: Vec<u32> = vec![1, 2, 3, 4];
    let bb: Vec<u32> = vec![5, 6, 7, 8];

    // This is the core. Here we write the interaction with the GPU independent of whether it is
    // CUDA or OpenCL.
    let closures = program_closures!(|program, _args| -> Result<Vec<u32>, GPUError> {
        // Make sure the input data has the same length.
        assert_eq!(aa.len(), bb.len());
        let length = aa.len();

        // Copy the data to the GPU.
        let aa_buffer = program.create_buffer_from_slice(&aa)?;
        let bb_buffer = program.create_buffer_from_slice(&bb)?;

        // The result buffer has the same length as the input buffers.
        let result_buffer = unsafe { program.create_buffer::<u32>(length)? };

        // Get the kernel.
        let kernel = program.create_kernel("add", 1, 1)?;

        // Execute the kernel.
        kernel
            .arg(&(length as u32))
            .arg(&aa_buffer)
            .arg(&bb_buffer)
            .arg(&result_buffer)
            .run()?;

        // Get the resulting data.
        let mut result = vec![0u32; length];
        program.read_into_buffer(&result_buffer, &mut result)?;

        Ok(result)
    });

    // Get the first available device.
    let device = *Device::all().first().unwrap();

    // First we run it on CUDA.
    let cuda_program = cuda(device);
    let cuda_result = cuda_program.run(closures, ()).unwrap();
    assert_eq!(cuda_result, [6, 8, 10, 12]);
    println!("CUDA result: {:?}", cuda_result);

    // Then we run it on OpenCL.
    let opencl_program = opencl(device);
    let opencl_result = opencl_program.run(closures, ()).unwrap();
    assert_eq!(opencl_result, [6, 8, 10, 12]);
    println!("OpenCL result: {:?}", opencl_result);
}
source

pub fn run<F, R, E, A>(&self, fun: F, arg: A) -> Result<R, E>where F: FnOnce(&Self, A) -> Result<R, E>, E: From<GPUError>,

Run some code in the context of the program.

It takes the program as a parameter, so that we can use the same function body, for both the OpenCL and the CUDA code path. The only difference is the type of the program.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same<T> for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.