1use lazy_static::lazy_static;
2use ocl::{
3 builders::BuildOpt, flags, prm::Float2, Buffer, Context, Device, OclPrm, Platform, ProQue,
4 Queue,
5};
6
7use std::sync::Mutex;
8
9use crate::{compiler::Compiler, Backend, Function, ImageBuffer, Params, Render};
10
11const PROGRAM: &str = include_str!(concat!(env!("OUT_DIR"), "/program.cl"));
12
13#[cfg_attr(docsrs, doc(cfg(feature = "opencl_backend")))]
17#[derive(Debug, Clone, Copy, Default)]
18pub struct OpenCl;
19
20impl Backend<&Function> for OpenCl {
21 type Error = ocl::Error;
22 type Program = OpenClProgram;
23
24 fn create_program(&self, function: &Function) -> Result<Self::Program, Self::Error> {
25 let compiled = Compiler::for_ocl().compile(function);
26 OpenClProgram::new(compiled)
27 }
28}
29
30#[cfg_attr(docsrs, doc(cfg(feature = "opencl_backend")))]
32#[derive(Debug)]
33pub struct OpenClProgram {
34 inner: ProQue,
35}
36
37impl OpenClProgram {
38 fn new(compiled: String) -> ocl::Result<Self> {
39 let mut program_builder = ocl::Program::builder();
40 let define = BuildOpt::IncludeDefine {
41 ident: "COMPUTE(z)".to_owned(),
42 val: compiled,
43 };
44 program_builder.bo(define).source(PROGRAM);
45
46 lazy_static! {
50 static ref MUTEX: Mutex<()> = Mutex::new(());
51 }
52 let (platform, device) = {
53 let _lock = MUTEX.lock().ok();
54 let platform = Platform::first()?;
55 (platform, Device::first(platform)?)
56 };
57
58 let context = Context::builder()
59 .platform(platform)
60 .devices(&device)
61 .build()?;
62 let inner = ProQue::new(
63 context.clone(),
64 Queue::new(&context, device, None)?,
65 program_builder.build(&context)?,
66 None::<usize>,
67 );
68 Ok(Self { inner })
69 }
70}
71
72impl Render for OpenClProgram {
73 type Error = ocl::Error;
74
75 fn render(&self, params: &Params) -> Result<ImageBuffer, Self::Error> {
76 let pixels = params.image_size[0]
77 .checked_mul(params.image_size[1])
78 .expect("Overflow in image dimensions");
79 let buffer: Buffer<u8> = Buffer::builder()
80 .queue(self.inner.queue().clone())
81 .len(pixels)
82 .flags(flags::MEM_WRITE_ONLY | flags::MEM_HOST_READ_ONLY)
83 .build()?;
84
85 let cl_params = ClParams {
86 view_center: Float2::new(params.view_center[0], params.view_center[1]),
87 view_size: Float2::new(params.view_width(), params.view_height),
88 inf_distance_sq: params.inf_distance * params.inf_distance,
89 max_iterations: params.max_iterations,
90 };
91 let kernel = self
92 .inner
93 .kernel_builder("julia")
94 .arg_named("output", &buffer)
95 .arg_named("params", cl_params)
96 .build()?;
97
98 let command = kernel.cmd().global_work_size(params.image_size);
99 unsafe { command.enq()? };
100
101 let mut image = ImageBuffer::new(params.image_size[0], params.image_size[1]);
102 buffer.read(&mut *image).enq()?;
103 Ok(image)
104 }
105}
106
107#[derive(Debug, Clone, Copy, Default, PartialEq)]
108#[repr(C, packed)]
109struct ClParams {
110 view_center: Float2,
111 view_size: Float2,
112 inf_distance_sq: f32,
113 max_iterations: u8,
114}
115
116unsafe impl OclPrm for ClParams {}