wrend/renderers/
renderer.rs

1use crate::{
2    recording_handlers, AnimationCallback, AnimationData, Attribute, Buffer, Framebuffer, Id,
3    IdName, RecordingData, RenderCallback, RendererData, RendererDataBuilder, RendererJs,
4    RendererJsInner, Texture, Uniform,
5};
6
7use log::{error, info};
8
9use std::cell::RefCell;
10use std::ops::Deref;
11use std::rc::Rc;
12use wasm_bindgen::prelude::Closure;
13use wasm_bindgen::{JsCast, JsValue};
14use web_sys::{
15    window, HtmlCanvasElement, WebGl2RenderingContext, WebGlProgram, WebGlShader,
16    WebGlTransformFeedback, WebGlVertexArrayObject,
17};
18
19/// The `Renderer` struct takes ownership of the `RendererData`, enabling it to
20/// perform more complex operations than would otherwise be possible, such as
21/// animating renders over time or recording canvas output.
22#[derive(Debug)]
23pub struct Renderer<
24    VertexShaderId: Id,
25    FragmentShaderId: Id,
26    ProgramId: Id,
27    UniformId: Id + IdName,
28    BufferId: Id,
29    AttributeId: Id + IdName,
30    TextureId: Id,
31    FramebufferId: Id,
32    TransformFeedbackId: Id,
33    VertexArrayObjectId: Id,
34    UserCtx: Clone + 'static,
35> {
36    renderer_data: Rc<
37        RefCell<
38            RendererData<
39                VertexShaderId,
40                FragmentShaderId,
41                ProgramId,
42                UniformId,
43                BufferId,
44                AttributeId,
45                TextureId,
46                FramebufferId,
47                TransformFeedbackId,
48                VertexArrayObjectId,
49                UserCtx,
50            >,
51        >,
52    >,
53    animation_data: Rc<
54        RefCell<
55            AnimationData<
56                VertexShaderId,
57                FragmentShaderId,
58                ProgramId,
59                UniformId,
60                BufferId,
61                AttributeId,
62                TextureId,
63                FramebufferId,
64                TransformFeedbackId,
65                VertexArrayObjectId,
66                UserCtx,
67            >,
68        >,
69    >,
70    recording_data: Option<Rc<RefCell<RecordingData>>>,
71}
72
73impl<
74        VertexShaderId: 'static + Id,
75        FragmentShaderId: 'static + Id,
76        ProgramId: 'static + Id,
77        UniformId: 'static + Id + IdName,
78        BufferId: 'static + Id,
79        AttributeId: 'static + Id + IdName,
80        TextureId: 'static + Id,
81        FramebufferId: 'static + Id,
82        TransformFeedbackId: 'static + Id,
83        VertexArrayObjectId: 'static + Id,
84        UserCtx: Clone + 'static,
85    >
86    Renderer<
87        VertexShaderId,
88        FragmentShaderId,
89        ProgramId,
90        UniformId,
91        BufferId,
92        AttributeId,
93        TextureId,
94        FramebufferId,
95        TransformFeedbackId,
96        VertexArrayObjectId,
97        UserCtx,
98    >
99{
100    pub(crate) fn new(
101        renderer_data: RendererData<
102            VertexShaderId,
103            FragmentShaderId,
104            ProgramId,
105            UniformId,
106            BufferId,
107            AttributeId,
108            TextureId,
109            FramebufferId,
110            TransformFeedbackId,
111            VertexArrayObjectId,
112            UserCtx,
113        >,
114    ) -> Self {
115        Self::new_with_rc_renderer(Rc::new(RefCell::new(renderer_data)))
116    }
117
118    /// Allows providing an already-wrapped `RendererData` as an argument.
119    pub(crate) fn new_with_rc_renderer(
120        renderer_data: Rc<
121            RefCell<
122                RendererData<
123                    VertexShaderId,
124                    FragmentShaderId,
125                    ProgramId,
126                    UniformId,
127                    BufferId,
128                    AttributeId,
129                    TextureId,
130                    FramebufferId,
131                    TransformFeedbackId,
132                    VertexArrayObjectId,
133                    UserCtx,
134                >,
135            >,
136        >,
137    ) -> Self {
138        Self {
139            recording_data: None,
140            renderer_data,
141            animation_data: Rc::new(RefCell::new(AnimationData::new())),
142        }
143    }
144
145    /// Must be called before starting to record.
146    ///
147    /// This prevents unexpected initialization of a MediaRecorder, when the
148    /// user wasn't expecting to need one from the handle.
149    pub fn initialize_recorder(&mut self) {
150        if let Some(_) = &self.recording_data {
151            error!("Error initializing recorder: a recorder has already been initialized. This is a no-op");
152            return;
153        }
154
155        let canvas = {
156            let renderer_ref = self.renderer_data.borrow();
157            renderer_ref.canvas().clone()
158        };
159        let recording_data = RecordingData::new(&canvas);
160        let media_recorder = recording_data.media_recorder().clone();
161        let recording_data = Rc::new(RefCell::new(recording_data));
162
163        {
164            let mut recording_data_ref = recording_data.borrow_mut();
165            recording_data_ref
166                .add_event_listener(recording_handlers::make_handle_dataavailable(
167                    media_recorder.clone(),
168                    Rc::clone(&recording_data),
169                ))
170                .add_event_listener(recording_handlers::make_handle_start(
171                    media_recorder.clone(),
172                    Rc::clone(&recording_data),
173                ))
174                .add_event_listener(recording_handlers::make_handle_error(
175                    media_recorder.clone(),
176                    Rc::clone(&recording_data),
177                ))
178                .add_event_listener(recording_handlers::make_handle_stop(
179                    media_recorder.clone(),
180                    Rc::clone(&recording_data),
181                ))
182                .add_event_listener(recording_handlers::make_handle_pause(
183                    media_recorder.clone(),
184                    Rc::clone(&recording_data),
185                ))
186                .add_event_listener(recording_handlers::make_handle_pause(
187                    media_recorder.clone(),
188                    Rc::clone(&recording_data),
189                ))
190                .add_event_listener(recording_handlers::make_handle_resume(
191                    media_recorder,
192                    Rc::clone(&recording_data),
193                ));
194        }
195
196        self.recording_data.replace(recording_data);
197
198        info!("Recorder successfully initialized")
199    }
200
201    pub fn start_animating(&self) {
202        // cancel previous animation before starting a new one
203        if self.is_animating() {
204            error!("`start_animating` was called, but `Renderer` is already animating. Cancelling the previous animation and staring a new one");
205            self.stop_animating();
206        }
207
208        self.animation_data.borrow_mut().set_is_animating(true);
209        let f = Rc::new(RefCell::new(None));
210        let g = Rc::clone(&f);
211        let animation_data = Rc::clone(&self.animation_data);
212        let renderer_data = Rc::clone(&self.renderer_data);
213        {
214            let animation_data = Rc::clone(&self.animation_data);
215            *g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
216                // do not run callback if not animating
217                if !animation_data.borrow().is_animating() {
218                    return;
219                }
220
221                // run animation callback
222                animation_data
223                    .borrow_mut()
224                    .call_animation_callback(Rc::clone(&renderer_data));
225
226                // schedule another requestAnimationFrame callback
227                let animation_id = Self::request_animation_frame(f.borrow().as_ref().unwrap());
228                animation_data.borrow_mut().set_request_id(animation_id);
229            }) as Box<dyn Fn()>));
230        }
231
232        let id = Self::request_animation_frame(g.borrow().as_ref().unwrap());
233        animation_data.borrow_mut().set_request_id(id);
234    }
235
236    pub fn stop_animating(&self) {
237        if !self.is_animating() {
238            error!("`stop_animating` was called, but `Renderer` is not currently animating");
239            return;
240        }
241
242        self.animation_data.borrow_mut().set_is_animating(false);
243        window()
244            .unwrap()
245            .cancel_animation_frame(self.animation_data.borrow().request_id())
246            .expect("Should be able to cancel animation frame")
247    }
248
249    pub fn set_animation_callback(
250        &mut self,
251        animation_callback: Option<
252            impl Into<
253                AnimationCallback<
254                    VertexShaderId,
255                    FragmentShaderId,
256                    ProgramId,
257                    UniformId,
258                    BufferId,
259                    AttributeId,
260                    TextureId,
261                    FramebufferId,
262                    TransformFeedbackId,
263                    VertexArrayObjectId,
264                    UserCtx,
265                >,
266            >,
267        >,
268    ) {
269        self.animation_data
270            .borrow_mut()
271            .set_animation_callback(animation_callback.map(|cb| cb.into()));
272    }
273
274    pub fn start_recording(&mut self) {
275        const ERROR_START: &str = "Error trying to start video recording";
276
277        if !self.recorder_initialized() {
278            self.initialize_recorder();
279        }
280
281        if let Some(recording_data) = &self.recording_data {
282            if let Err(err) = recording_data
283                .borrow_mut()
284                .media_recorder()
285                .start_with_time_slice(RecordingData::SAVE_DATA_INTERVAL)
286            {
287                error!("{ERROR_START}: {err:?}");
288            }
289        } else {
290            error!("{ERROR_START}: there was an error initializing the recorder");
291        }
292    }
293
294    pub fn stop_recording(&self) {
295        const ERROR_START: &str = "Error trying to stop video recording";
296
297        if !self.is_recording() {
298            error!("{ERROR_START}: recorder is not currently recording");
299            return;
300        }
301
302        if let Some(recording_data) = &self.recording_data {
303            if let Err(err) = recording_data.borrow_mut().media_recorder().stop() {
304                error!("{ERROR_START}: {err:?}");
305            }
306        } else {
307            error!("{ERROR_START}: recorder was not properly initialized");
308        }
309    }
310
311    pub fn clear_recorded_data(&self) {
312        const ERROR_START: &str = "Error trying to clear video recording data";
313
314        if let Some(recording_data) = &self.recording_data {
315            recording_data.borrow_mut().recorded_chunks_mut().clear();
316        } else {
317            error!("{ERROR_START}: recorder was not properly initialized");
318        }
319    }
320
321    pub fn recorder_initialized(&self) -> bool {
322        self.recording_data.is_some()
323    }
324
325    pub fn is_animating(&self) -> bool {
326        self.animation_data.borrow().is_animating()
327    }
328
329    pub fn is_recording(&self) -> bool {
330        self.recording_data
331            .as_ref()
332            .map_or(false, |recording_data| {
333                recording_data.borrow().is_recording()
334            })
335    }
336
337    pub(crate) fn renderer_data(
338        &self,
339    ) -> Rc<
340        RefCell<
341            RendererData<
342                VertexShaderId,
343                FragmentShaderId,
344                ProgramId,
345                UniformId,
346                BufferId,
347                AttributeId,
348                TextureId,
349                FramebufferId,
350                TransformFeedbackId,
351                VertexArrayObjectId,
352                UserCtx,
353            >,
354        >,
355    > {
356        Rc::clone(&self.renderer_data)
357    }
358
359    pub(crate) fn request_animation_frame(f: &Closure<dyn Fn()>) -> i32 {
360        window()
361            .unwrap()
362            .request_animation_frame(f.as_ref().unchecked_ref())
363            .expect("should register `requestAnimationFrame` ok")
364    }
365}
366
367impl<
368        VertexShaderId: Id,
369        FragmentShaderId: Id,
370        ProgramId: Id,
371        UniformId: Id + IdName,
372        BufferId: Id,
373        AttributeId: Id + IdName,
374        TextureId: Id,
375        FramebufferId: Id,
376        TransformFeedbackId: Id,
377        VertexArrayObjectId: Id,
378        UserCtx: Clone,
379    > Drop
380    for Renderer<
381        VertexShaderId,
382        FragmentShaderId,
383        ProgramId,
384        UniformId,
385        BufferId,
386        AttributeId,
387        TextureId,
388        FramebufferId,
389        TransformFeedbackId,
390        VertexArrayObjectId,
391        UserCtx,
392    >
393{
394    fn drop(&mut self) {
395        // this would get dropped even if we didn't do it manually,
396        // but dropping the listeners here before the rest of the data gets dropped
397        // prevents them from accidentally firing when other clean up happens
398        if let Some(recording_data) = &self.recording_data {
399            recording_data.borrow_mut().remove_all_event_listeners();
400        }
401
402        if self.is_recording() {
403            self.stop_recording();
404        }
405
406        if self.is_animating() {
407            self.stop_animating();
408        }
409    }
410}
411
412impl<
413        VertexShaderId: Id,
414        FragmentShaderId: Id,
415        ProgramId: Id,
416        UniformId: Id + IdName,
417        BufferId: Id,
418        AttributeId: Id + IdName,
419        TextureId: Id,
420        FramebufferId: Id,
421        TransformFeedbackId: Id,
422        VertexArrayObjectId: Id,
423        UserCtx: Clone,
424    >
425    From<
426        RendererData<
427            VertexShaderId,
428            FragmentShaderId,
429            ProgramId,
430            UniformId,
431            BufferId,
432            AttributeId,
433            TextureId,
434            FramebufferId,
435            TransformFeedbackId,
436            VertexArrayObjectId,
437            UserCtx,
438        >,
439    >
440    for Renderer<
441        VertexShaderId,
442        FragmentShaderId,
443        ProgramId,
444        UniformId,
445        BufferId,
446        AttributeId,
447        TextureId,
448        FramebufferId,
449        TransformFeedbackId,
450        VertexArrayObjectId,
451        UserCtx,
452    >
453{
454    fn from(
455        renderer_data: RendererData<
456            VertexShaderId,
457            FragmentShaderId,
458            ProgramId,
459            UniformId,
460            BufferId,
461            AttributeId,
462            TextureId,
463            FramebufferId,
464            TransformFeedbackId,
465            VertexArrayObjectId,
466            UserCtx,
467        >,
468    ) -> Self {
469        Renderer::new(renderer_data)
470    }
471}
472
473// Re-export of the (inexpensive) functionality from `RendererData`
474impl<
475        VertexShaderId: Id,
476        FragmentShaderId: Id,
477        ProgramId: Id,
478        UniformId: Id + IdName,
479        BufferId: Id,
480        AttributeId: Id + IdName,
481        TextureId: Id,
482        FramebufferId: Id,
483        TransformFeedbackId: Id,
484        VertexArrayObjectId: Id,
485        UserCtx: Clone,
486    >
487    Renderer<
488        VertexShaderId,
489        FragmentShaderId,
490        ProgramId,
491        UniformId,
492        BufferId,
493        AttributeId,
494        TextureId,
495        FramebufferId,
496        TransformFeedbackId,
497        VertexArrayObjectId,
498        UserCtx,
499    >
500{
501    pub fn builder() -> RendererDataBuilder<
502        VertexShaderId,
503        FragmentShaderId,
504        ProgramId,
505        UniformId,
506        BufferId,
507        AttributeId,
508        TextureId,
509        FramebufferId,
510        TransformFeedbackId,
511        VertexArrayObjectId,
512        UserCtx,
513    > {
514        RendererDataBuilder::default()
515    }
516
517    pub fn canvas(&self) -> HtmlCanvasElement {
518        self.deref().borrow().canvas().to_owned()
519    }
520
521    pub fn gl(&self) -> WebGl2RenderingContext {
522        self.deref().borrow().deref().gl().to_owned()
523    }
524
525    pub fn fragment_shader(&self, fragment_shader_id: &FragmentShaderId) -> Option<WebGlShader> {
526        self.deref()
527            .borrow()
528            .fragment_shader(fragment_shader_id)
529            .map(Clone::clone)
530    }
531
532    pub fn vertex_shader(&self, vertex_shader_id: &VertexShaderId) -> Option<WebGlShader> {
533        self.deref()
534            .borrow()
535            .vertex_shader(vertex_shader_id)
536            .map(Clone::clone)
537    }
538
539    pub fn program(&self, program_id: &ProgramId) -> Option<WebGlProgram> {
540        self.deref().borrow().program(program_id).map(Clone::clone)
541    }
542
543    pub fn uniform(&self, uniform_id: &UniformId) -> Option<Uniform<ProgramId, UniformId>> {
544        self.deref().borrow().uniform(uniform_id).map(Clone::clone)
545    }
546
547    pub fn buffer(&self, buffer_id: &BufferId) -> Option<Buffer<BufferId>> {
548        self.deref().borrow().buffer(buffer_id).map(Clone::clone)
549    }
550
551    pub fn attribute(
552        &self,
553        attribute_id: &AttributeId,
554    ) -> Option<Attribute<VertexArrayObjectId, BufferId, AttributeId>> {
555        self.deref()
556            .borrow()
557            .attribute(attribute_id)
558            .map(Clone::clone)
559    }
560
561    pub fn texture(&self, texture_id: &TextureId) -> Option<Texture<TextureId>> {
562        self.deref().borrow().texture(texture_id).map(Clone::clone)
563    }
564
565    pub fn framebuffer(
566        &self,
567        framebuffer_id: &FramebufferId,
568    ) -> Option<Framebuffer<FramebufferId>> {
569        self.deref()
570            .borrow()
571            .framebuffer(framebuffer_id)
572            .map(Clone::clone)
573    }
574
575    pub fn transform_feedback(
576        &self,
577        transform_feedback_id: &TransformFeedbackId,
578    ) -> Option<WebGlTransformFeedback> {
579        self.deref()
580            .borrow()
581            .transform_feedback(transform_feedback_id)
582            .map(Clone::clone)
583    }
584
585    pub fn vao(&self, vao_id: &VertexArrayObjectId) -> Option<WebGlVertexArrayObject> {
586        self.deref().borrow().vao(vao_id).map(Clone::clone)
587    }
588
589    pub fn user_ctx(&self) -> Option<UserCtx> {
590        self.deref().borrow().user_ctx().map(Clone::clone)
591    }
592
593    pub fn use_program(&self, program_id: &ProgramId) -> &Self {
594        self.deref().borrow().use_program(program_id);
595        self
596    }
597
598    pub fn use_vao(&self, vao_id: &VertexArrayObjectId) -> &Self {
599        self.deref().borrow().use_vao(vao_id);
600        self
601    }
602    pub fn update_uniform(&self, uniform_id: &UniformId) -> &Self {
603        self.deref().borrow().update_uniform(uniform_id);
604        self
605    }
606
607    pub fn update_uniforms(&self) -> &Self {
608        self.deref().borrow().update_uniforms();
609        self
610    }
611
612    pub fn render(&self) -> &Self {
613        self.deref().borrow().render();
614        self
615    }
616
617    pub fn save_image(&self) {
618        self.deref().borrow().save_image()
619    }
620
621    pub fn render_callback(
622        &self,
623    ) -> RenderCallback<
624        VertexShaderId,
625        FragmentShaderId,
626        ProgramId,
627        UniformId,
628        BufferId,
629        AttributeId,
630        TextureId,
631        FramebufferId,
632        TransformFeedbackId,
633        VertexArrayObjectId,
634        UserCtx,
635    > {
636        self.deref().borrow().render_callback()
637    }
638}
639
640impl<
641        VertexShaderId: Id,
642        FragmentShaderId: Id,
643        ProgramId: Id,
644        UniformId: Id + IdName,
645        BufferId: Id,
646        AttributeId: Id + IdName,
647        TextureId: Id,
648        FramebufferId: Id,
649        TransformFeedbackId: Id,
650        VertexArrayObjectId: Id,
651        UserCtx: Clone,
652    >
653    From<
654        Rc<
655            RefCell<
656                RendererData<
657                    VertexShaderId,
658                    FragmentShaderId,
659                    ProgramId,
660                    UniformId,
661                    BufferId,
662                    AttributeId,
663                    TextureId,
664                    FramebufferId,
665                    TransformFeedbackId,
666                    VertexArrayObjectId,
667                    UserCtx,
668                >,
669            >,
670        >,
671    >
672    for Renderer<
673        VertexShaderId,
674        FragmentShaderId,
675        ProgramId,
676        UniformId,
677        BufferId,
678        AttributeId,
679        TextureId,
680        FramebufferId,
681        TransformFeedbackId,
682        VertexArrayObjectId,
683        UserCtx,
684    >
685{
686    fn from(
687        renderer_data: Rc<
688            RefCell<
689                RendererData<
690                    VertexShaderId,
691                    FragmentShaderId,
692                    ProgramId,
693                    UniformId,
694                    BufferId,
695                    AttributeId,
696                    TextureId,
697                    FramebufferId,
698                    TransformFeedbackId,
699                    VertexArrayObjectId,
700                    UserCtx,
701                >,
702            >,
703        >,
704    ) -> Self {
705        Renderer::new_with_rc_renderer(renderer_data)
706    }
707}
708
709impl<
710        VertexShaderId: Id,
711        FragmentShaderId: Id,
712        ProgramId: Id,
713        UniformId: Id + IdName,
714        BufferId: Id,
715        AttributeId: Id + IdName,
716        TextureId: Id,
717        FramebufferId: Id,
718        TransformFeedbackId: Id,
719        VertexArrayObjectId: Id,
720        UserCtx: Clone,
721    > Deref
722    for Renderer<
723        VertexShaderId,
724        FragmentShaderId,
725        ProgramId,
726        UniformId,
727        BufferId,
728        AttributeId,
729        TextureId,
730        FramebufferId,
731        TransformFeedbackId,
732        VertexArrayObjectId,
733        UserCtx,
734    >
735{
736    type Target = Rc<
737        RefCell<
738            RendererData<
739                VertexShaderId,
740                FragmentShaderId,
741                ProgramId,
742                UniformId,
743                BufferId,
744                AttributeId,
745                TextureId,
746                FramebufferId,
747                TransformFeedbackId,
748                VertexArrayObjectId,
749                UserCtx,
750            >,
751        >,
752    >;
753
754    fn deref(&self) -> &Self::Target {
755        &self.renderer_data
756    }
757}
758
759impl From<RendererJsInner> for JsValue {
760    fn from(js_renderer_handle_inner: RendererJsInner) -> Self {
761        let js_renderer_handle: RendererJs = js_renderer_handle_inner.into();
762        js_renderer_handle.into()
763    }
764}