clay_core/process/
render.rs

1use std::{
2    path::Path,
3    collections::HashSet,
4    marker::PhantomData,
5    time::{Instant, Duration},
6};
7use ocl::{self, prm, builders::KernelBuilder};
8use ocl_include::{Hook, MemHook, ListHook};
9use crate::{
10    prelude::*,
11    scene::Scene,
12    view::View,
13    
14    Context,
15    process::Program,
16    buffer::RenderBuffer,
17};
18
19/// Creates new renderer builder with already included device source.
20pub fn create_renderer<S: Scene, V: View>() -> RendererBuilder<S, V> {
21    RendererBuilder {
22        list_hook:
23            ListHook::builder()
24            .add_hook(crate::source())
25            .build(),
26        phantom: PhantomData,
27    }
28}
29
30/// Responsible for building the renderer.
31pub struct RendererBuilder<S: Scene, V: View> {
32    list_hook: ListHook,
33    phantom: PhantomData<(S, V)>,
34}
35
36/// Defines the whole raytracing process.
37///
38/// It stores scene and viewer and produces workers for specific device.
39pub struct Renderer<S: Scene, V: View> {
40    program: Program,
41    dims: (usize, usize),
42    pub scene: S,
43    pub view: V,
44}
45
46/// Device data of the renderer.
47pub struct RenderData<S: Scene, V: View> {
48    screen: RenderBuffer,
49    scene_data: S::Data,
50    view_data: V::Data,
51}
52
53impl<S: Scene, V: View> Renderer<S, V> {
54    pub fn new<H: Hook + 'static>(
55        dims: (usize, usize),
56        scene: S, view: V,
57        hook: H,
58    ) -> crate::Result<Self> {
59        let mut inst_cache = HashSet::<u64>::new();
60        let list_hook = ListHook::builder()
61        .add_hook(hook)
62        .add_hook(
63            MemHook::builder()
64            .add_file(&Path::new("__gen/scene.h"), S::source(&mut inst_cache))?
65            .add_file(&Path::new("__gen/view.h"), V::source(&mut inst_cache))?
66            .build()
67        )
68        .build();
69        let program = Program::new(&list_hook, &Path::new("clay_core/render.c"))?;
70
71        Ok(Self { program, dims, scene, view })
72    }
73
74    pub fn program(&self) -> &Program {
75        &self.program
76    }
77
78    pub fn create_worker(&self, context: &Context) -> crate::Result<(RenderWorker<S, V>, String)> {
79        RenderWorker::new(
80            context,
81            self.program(),
82            self.create_data(context)?,
83        )
84    }
85}
86
87impl<S: Scene, V: View> RendererBuilder<S, V> {
88    pub fn add_hook<H: Hook + 'static>(&mut self, hook: H) {
89        self.list_hook.add_hook(hook);
90    }
91
92    pub fn build(
93        self, dims: (usize, usize),
94        scene: S, view: V,
95    ) -> crate::Result<Renderer<S, V>> {
96        Renderer::<S, V>::new(
97            dims, scene, view,
98            self.list_hook,
99        )
100    }
101}
102
103impl<S: Scene, V: View> Store for Renderer<S, V> {
104    type Data = RenderData<S, V>;
105
106    fn create_data(&self, context: &Context) -> crate::Result<Self::Data> {
107        Ok(Self::Data {
108            screen: RenderBuffer::new(context, self.dims)?,
109            scene_data: self.scene.create_data(context)?,
110            view_data: self.view.create_data(context)?,
111        })
112    }
113
114    fn update_data(&self, context: &Context, data: &mut Self::Data) -> crate::Result<()> {
115        self.scene.update_data(context, &mut data.scene_data)?;
116        self.view.update_data(context, &mut data.view_data)?;
117        Ok(())
118    }
119}
120
121impl<S: Scene, V: View> RenderData<S, V> {
122    pub fn buffer(&self) -> &RenderBuffer {
123        &self.screen
124    }
125    pub fn buffer_mut(&mut self) -> &mut RenderBuffer {
126        &mut self.screen
127    }
128    pub fn scene(&self) -> &S::Data {
129        &self.scene_data
130    }
131    pub fn scene_mut(&mut self) -> &mut S::Data {
132        &mut self.scene_data
133    }
134    pub fn view(&self) -> &V::Data {
135        &self.view_data
136    }
137    pub fn view_mut(&mut self) -> &mut V::Data {
138        &mut self.view_data
139    }
140}
141
142impl<S: Scene, V: View> Push for RenderData<S, V> {
143    fn args_count() -> usize {
144        3 + S::Data::args_count() + V::Data::args_count()
145    }
146    fn args_def(kb: &mut KernelBuilder) {
147        kb.arg(prm::Int2::zero()); // screen size
148        kb.arg(None::<&ocl::Buffer<f32>>); // color buffer
149        kb.arg(None::<&ocl::Buffer<u32>>); // random
150        S::Data::args_def(kb);
151        V::Data::args_def(kb);
152    }
153    fn args_set(&mut self, i: usize, k: &mut ocl::Kernel) -> crate::Result<()> {
154        let mut j = i;
155
156        let dims = self.screen.dims();
157        let dims_prm = prm::Int2::new(dims.0 as i32, dims.1 as i32);
158        k.set_arg(i + 0, &dims_prm)?;
159        k.set_arg(i + 1, self.screen.color_mut())?;
160        k.set_arg(i + 2, self.screen.random_mut())?;
161        j += 3;
162
163        self.scene_data.args_set(j, k)?;
164        j += S::Data::args_count();
165
166        self.view_data.args_set(j, k)?;
167        //j += V::Data::args_count();
168
169        Ok(())
170    }
171}
172
173/// Worker of the renderer.
174///
175/// It actually runs ray tracing process on the specific device and handles render data.
176pub struct RenderWorker<S: Scene, V: View> {
177    data: RenderData<S, V>,
178    kernel: ocl::Kernel,
179    context: Context,
180}
181
182impl<S: Scene, V: View> RenderWorker<S, V> {
183    pub fn new(
184        context: &Context,
185        program: &Program,
186        data: RenderData<S, V>,
187    ) -> crate::Result<(Self, String)> {
188        let queue = context.queue().clone();
189
190        let (ocl_prog, message) = program.build(context)?;
191
192        let mut kb = ocl::Kernel::builder();
193        kb.program(&ocl_prog)
194        .name("render")
195        .queue(queue.clone());
196        RenderData::<S, V>::args_def(&mut kb);
197        
198        let kernel = kb.build()?;
199
200        Ok((RenderWorker {
201            data, kernel,
202            context: context.clone(),
203        }, message))
204    }
205
206    pub fn data(&self) -> &RenderData<S, V> {
207        &self.data
208    }
209    pub fn data_mut(&mut self) -> &mut RenderData<S, V> {
210        &mut self.data
211    }
212
213    /// Run one ray tracing pass.
214    /// During this process there only one ray will be casted for each pixel.
215    pub fn run(&mut self) -> crate::Result<()> {
216        self.data.args_set(0, &mut self.kernel)?;
217        unsafe {
218            self.kernel.cmd()
219            .global_work_size(self.data.screen.dims())
220            .enq()?;
221        }
222        self.context.queue().finish()?;
223        self.data_mut().screen.pass();
224
225        Ok(())
226    }
227
228    /// Repeat ray tracing passes until elapsed time exceeds the given one.
229    pub fn run_for(&mut self, time: Duration) -> crate::Result<usize> {
230        let inst = Instant::now();
231        let mut passes = 1;
232        self.run()?;
233        while inst.elapsed() < time {
234            self.run()?;
235            passes += 1;
236        }
237        Ok(passes)
238    }
239}