clay_core/process/
postproc.rs

1use std::{
2    mem::swap,
3    ops::Deref,
4    path::Path,
5    collections::HashSet,
6    marker::PhantomData,
7};
8use ocl::{self, prm};
9use ocl_include::{Hook, MemHook, ListHook};
10use crate::{
11    filter::Filter,
12    Context,
13    process::Program,
14    buffer::{RenderBuffer, Image},
15};
16
17/// Collects device source code required to build postprocessor. 
18pub struct PostprocCollector<F: Filter> {
19    list_hook: ListHook,
20    phantom: PhantomData<F>,
21}
22
23/// Responsible for postprocessor building.
24pub struct PostprocBuilder<F: Filter> {
25    program: Program,
26    phantom: PhantomData<F>,
27}
28
29/// Postprocessing of raw rendered image.
30///
31/// It is responsible for collecting images from different workers,
32/// applying filters and generating resulting image.
33pub struct Postproc<F: Filter> {
34    context: Context,
35    k_mean: ocl::Kernel,
36    k_filt: ocl::Kernel,
37    k_pack: ocl::Kernel,
38    host_buffer: Vec<f32>,
39    buffers: (ocl::Buffer<f32>, ocl::Buffer<f32>),
40    image: Image,
41    dims: (usize, usize),
42    pub filter: F,
43}
44
45impl<F: Filter> PostprocBuilder<F> {
46    pub fn program(&self) -> &Program {
47        &self.program
48    }
49}
50
51/// Creates initial postprocessor collector with already included device source.
52pub fn create_postproc<F: Filter>() -> PostprocCollector<F> {
53    PostprocCollector {
54        list_hook:
55            ListHook::builder()
56            .add_hook(crate::source())
57            .build(),
58        phantom: PhantomData,
59    }
60}
61
62impl<F: Filter> PostprocCollector<F> {
63    pub fn add_hook<H: Hook + 'static>(&mut self, hook: H) {
64        self.list_hook.add_hook(hook);
65    }
66
67    fn source(cache: &mut HashSet<u64>) -> String {
68        let cpref = F::inst_name().to_uppercase();
69        [
70            format!("#define __FILTER_ARGS_DEF {}_ARGS_DEF", cpref),
71            format!("#define __FILTER_ARGS {}_ARGS", cpref),
72            format!("#define __filter_apply {}_apply", F::inst_name()),
73            F::source(cache)
74        ].join("\n")
75    }
76
77    pub fn collect(mut self) -> crate::Result<PostprocBuilder<F>> {
78        let mut cache = HashSet::<u64>::new();
79        self.list_hook.add_hook(
80            MemHook::builder()
81            .add_file(
82                &Path::new("__gen/filter.h"),
83                Self::source(&mut cache),
84            )?
85            .build()
86        );
87        let program = Program::new(
88            &self.list_hook,
89            &Path::new("clay_core/filter.c"),
90        )?;
91
92        Ok(PostprocBuilder { program, phantom: PhantomData })
93    }
94}
95
96impl<F: Filter> PostprocBuilder<F> {
97    pub fn build(
98        self,
99        context: &Context,
100        dims: (usize, usize),
101        filter: F,
102    ) -> crate::Result<(Postproc<F>, String)> {
103        Postproc::new(context, dims, filter, self.program)
104    }
105}
106
107impl<F: Filter + Default> PostprocBuilder<F> {
108    pub fn build_default(
109        self,
110        context: &Context,
111        dims: (usize, usize),
112    ) -> crate::Result<(Postproc<F>, String)> {
113        self.build(context, dims, F::default())
114    }
115}
116
117impl<F: Filter> Postproc<F> {
118    fn build_mean(context: &Context) -> crate::Result<(ocl::Kernel, String)> {
119        let queue = context.queue().clone();
120
121        let program = Program::new(
122            &crate::source(),
123            &Path::new("clay_core/mean.c"),
124        )?;
125
126        let (ocl_prog, message) = program.build(context)?;
127
128        let kernel = ocl::Kernel::builder()
129        .program(&ocl_prog)
130        .name("mean")
131        .queue(queue.clone())
132        .arg(prm::Int2::zero()) // screen size
133        .arg(0i32) // dst passes
134        .arg(0i32) // src passes
135        .arg(None::<&ocl::Buffer<f32>>) // dst buffer
136        .arg(None::<&ocl::Buffer<f32>>) // src buffer
137        .build()?;
138
139        Ok((kernel, message))
140    }
141
142    fn build_pack(context: &Context) -> crate::Result<(ocl::Kernel, String)> {
143        let queue = context.queue().clone();
144
145        let program = Program::new(
146            &crate::source(),
147            &Path::new("clay_core/pack.c"),
148        )?;
149
150        let (ocl_prog, message) = program.build(context)?;
151
152        let kernel = ocl::Kernel::builder()
153        .program(&ocl_prog)
154        .name("pack")
155        .queue(queue.clone())
156        .arg(prm::Int2::zero()) // screen size
157        .arg(None::<&ocl::Buffer<u8>>) // image buffer
158        .arg(None::<&ocl::Buffer<f32>>) // color buffer
159        .build()?;
160
161        Ok((kernel, message))
162    }
163
164    fn create_buffer(context: &Context, dims: (usize, usize)) -> crate::Result<ocl::Buffer<f32>> {
165        ocl::Buffer::<f32>::builder()
166        .queue(context.queue().clone())
167        .flags(ocl::flags::MEM_READ_WRITE)
168        .len(3*dims.0*dims.1)
169        .fill_val(0 as f32)
170        .build()
171        .map_err(|e| e.into())
172    }
173
174    pub fn new(
175        context: &Context, dims: (usize, usize),
176        filter: F, program: Program,
177    ) -> crate::Result<(Self, String)> {
178        let queue = context.queue().clone();
179
180        let (ocl_prog, message) = program.build(context)?;
181
182        let mut kb = ocl::Kernel::builder();
183        kb.program(&ocl_prog)
184        .name("filter")
185        .queue(queue.clone())
186        .arg(prm::Int2::zero()) // screen size
187        .arg(None::<&ocl::Buffer<f32>>) // dst buffer
188        .arg(None::<&ocl::Buffer<f32>>); // src buffer
189        F::args_def(&mut kb);
190
191        let k_filt = kb.build()?;
192
193        let (k_mean, _msg_mean) = Self::build_mean(context)?;
194        //println!("Build log (mean.c):\n{}", _msg_mean);
195        let (k_pack, _msg_pack) = Self::build_pack(context)?;
196        //println!("Build log (pack.c):\n{}", _msg_pack);
197
198        Ok((Postproc {
199            context: context.clone(),
200            k_mean, k_filt, k_pack,
201            host_buffer: Vec::new(),
202            buffers: (
203                Self::create_buffer(context, dims)?,
204                Self::create_buffer(context, dims)?,
205            ),
206            image: Image::new(context, dims)?,
207            dims: dims,
208            filter,
209        }, message))
210    }
211
212    pub fn resize(&mut self, dims: (usize, usize)) -> crate::Result<()> {
213        self.buffers = (
214            Self::create_buffer(&self.context, dims)?,
215            Self::create_buffer(&self.context, dims)?,
216        );
217        self.image = Image::new(&self.context, dims)?;
218        self.dims = dims;
219        Ok(())
220    }
221
222    fn dims_prm(&self) -> prm::Int2 {
223        let dims = self.dims;
224        prm::Int2::new(dims.0 as i32, dims.1 as i32)
225    }
226
227    fn apply_collect(&mut self, n_passes: usize, screen: &RenderBuffer) -> crate::Result<()> {
228        if *screen.context() != self.context {
229            let len = 3*self.dims.0*self.dims.1;
230            if self.host_buffer.len() != len {
231                self.host_buffer.resize(len, 0f32);
232            }
233
234            screen.color().cmd()
235            .offset(0)
236            .read(&mut self.host_buffer)
237            .enq()?;
238
239            self.buffers.1.cmd()
240            .offset(0)
241            .write(&self.host_buffer)
242            .enq()?;
243
244            self.context.queue().finish()?;
245        };
246
247        let d = self.dims_prm();
248        let k = &mut self.k_mean;
249        k.set_arg(0, &d)?;
250        k.set_arg(1, &(n_passes as i32))?;
251        k.set_arg(2, &(screen.n_passes() as i32))?;
252        k.set_arg(3, &mut self.buffers.0)?;
253        if *screen.context() != self.context {
254            k.set_arg(4, &self.buffers.1)?;
255        } else {
256            k.set_arg(4, screen.color())?;
257        }
258
259        unsafe {
260            k.cmd()
261            .global_work_size(self.dims)
262            .enq()?;
263        }
264
265        Ok(())
266    }
267
268    fn apply_filter(&mut self) -> crate::Result<()> {
269        let d = self.dims_prm();
270        let k = &mut self.k_filt;
271        k.set_arg(0, &d)?;
272        k.set_arg(1, &mut self.buffers.1)?;
273        k.set_arg(2, &self.buffers.0)?;
274        self.filter.args_set(3, k)?;
275
276        unsafe {
277            k.cmd()
278            .global_work_size(self.dims)
279            .enq()?;
280        }
281
282        swap(&mut self.buffers.1, &mut self.buffers.0);
283        Ok(())
284    }
285
286    pub fn process<'a, I: Iterator<Item=&'a RenderBuffer>>(
287        &mut self, screens: I,
288    ) -> crate::Result<()> {
289        let mut n_passes = 0;
290        for screen in screens {
291            self.apply_collect(n_passes, screen.deref())?;
292            n_passes += screen.n_passes();
293        }
294
295        self.apply_filter()?;
296
297        self.context.queue().finish()?;
298        Ok(())
299    }
300
301    pub fn process_one(&mut self, screen: &RenderBuffer) -> crate::Result<()> {
302        self.process([screen].into_iter().map(|s| *s))
303    }
304
305    pub fn make_image(&mut self) -> crate::Result<()> {
306        let d = self.dims_prm();
307        let k = &mut self.k_pack;
308        k.set_arg(0, &d)?;
309        k.set_arg(1, self.image.bytes_mut())?;
310        k.set_arg(2, &self.buffers.0)?;
311
312        unsafe {
313            k.cmd()
314            .global_work_size(self.dims)
315            .enq()?;
316        }
317
318        swap(&mut self.buffers.1, &mut self.buffers.0);
319        Ok(())
320    }
321
322    pub fn buffer(&self) -> &ocl::Buffer<f32> {
323        &self.buffers.0
324    }
325    pub fn image(&self) -> &Image {
326        &self.image
327    }
328    pub fn dims(&self) -> (usize, usize) {
329        self.dims
330    } 
331}