wrend/renderer_data/
renderer_data.rs

1use crate::{
2    Attribute, AttributeLink, Bridge, Buffer, BufferLink, BuildRendererError, Callback,
3    CompileShaderError, CreateAttributeError, CreateBufferError, CreateTextureError,
4    CreateTransformFeedbackError, CreateUniformError, CreateVAOError, Framebuffer, FramebufferLink,
5    GetContextCallback, Id, IdDefault, IdName, LinkProgramError, ProgramLink, RenderCallback,
6    Renderer, RendererBuilderError, RendererDataJs, RendererDataJsInner, SaveContextError,
7    ShaderType, Texture, TextureLink, TransformFeedbackLink, Uniform, UniformContext, UniformLink,
8    WebGlContextError,
9};
10
11use std::collections::{HashMap, HashSet};
12
13use wasm_bindgen::{JsCast, JsValue};
14use web_sys::{
15    window, HtmlAnchorElement, HtmlCanvasElement, WebGl2RenderingContext, WebGlProgram,
16    WebGlShader, WebGlTransformFeedback, WebGlVertexArrayObject,
17};
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct RendererData<
21    VertexShaderId: Id = IdDefault,
22    FragmentShaderId: Id = IdDefault,
23    ProgramId: Id = IdDefault,
24    UniformId: Id + IdName = IdDefault,
25    BufferId: Id = IdDefault,
26    AttributeId: Id + IdName = IdDefault,
27    TextureId: Id = IdDefault,
28    FramebufferId: Id = IdDefault,
29    TransformFeedbackId: Id = IdDefault,
30    VertexArrayObjectId: Id = IdDefault,
31    UserCtx: Clone + 'static = (),
32> {
33    canvas: HtmlCanvasElement,
34    gl: WebGl2RenderingContext,
35    fragment_shaders: HashMap<FragmentShaderId, WebGlShader>,
36    vertex_shaders: HashMap<VertexShaderId, WebGlShader>,
37    programs: HashMap<ProgramId, WebGlProgram>,
38    render_callback: RenderCallback<
39        VertexShaderId,
40        FragmentShaderId,
41        ProgramId,
42        UniformId,
43        BufferId,
44        AttributeId,
45        TextureId,
46        FramebufferId,
47        TransformFeedbackId,
48        VertexArrayObjectId,
49        UserCtx,
50    >,
51    uniforms: HashMap<UniformId, Uniform<ProgramId, UniformId>>,
52    user_ctx: Option<UserCtx>,
53    attributes: HashMap<AttributeId, Attribute<VertexArrayObjectId, BufferId, AttributeId>>,
54    buffers: HashMap<BufferId, Buffer<BufferId>>,
55    textures: HashMap<TextureId, Texture<TextureId>>,
56    vertex_array_objects: HashMap<VertexArrayObjectId, WebGlVertexArrayObject>,
57    framebuffers: HashMap<FramebufferId, Framebuffer<FramebufferId>>,
58    transform_feedbacks: HashMap<TransformFeedbackId, WebGlTransformFeedback>,
59}
60
61/// Public API
62impl<
63        VertexShaderId: Id,
64        FragmentShaderId: Id,
65        ProgramId: Id,
66        UniformId: Id + IdName,
67        BufferId: Id,
68        AttributeId: Id + IdName,
69        TextureId: Id,
70        FramebufferId: Id,
71        TransformFeedbackId: Id,
72        VertexArrayObjectId: Id,
73        UserCtx: Clone,
74    >
75    RendererData<
76        VertexShaderId,
77        FragmentShaderId,
78        ProgramId,
79        UniformId,
80        BufferId,
81        AttributeId,
82        TextureId,
83        FramebufferId,
84        TransformFeedbackId,
85        VertexArrayObjectId,
86        UserCtx,
87    >
88{
89    pub fn builder() -> RendererDataBuilder<
90        VertexShaderId,
91        FragmentShaderId,
92        ProgramId,
93        UniformId,
94        BufferId,
95        AttributeId,
96        TextureId,
97        FramebufferId,
98        TransformFeedbackId,
99        VertexArrayObjectId,
100        UserCtx,
101    > {
102        RendererDataBuilder::default()
103    }
104
105    pub fn canvas(&self) -> &HtmlCanvasElement {
106        &self.canvas
107    }
108
109    pub fn gl(&self) -> &WebGl2RenderingContext {
110        &self.gl
111    }
112
113    pub fn fragment_shader(&self, fragment_shader_id: &FragmentShaderId) -> Option<&WebGlShader> {
114        self.fragment_shaders.get(fragment_shader_id)
115    }
116
117    pub fn fragment_shaders(&self) -> &HashMap<FragmentShaderId, WebGlShader> {
118        &self.fragment_shaders
119    }
120
121    pub fn vertex_shader(&self, vertex_shader_id: &VertexShaderId) -> Option<&WebGlShader> {
122        self.vertex_shaders.get(vertex_shader_id)
123    }
124
125    pub fn vertex_shaders(&self) -> &HashMap<VertexShaderId, WebGlShader> {
126        &self.vertex_shaders
127    }
128
129    pub fn program(&self, program_id: &ProgramId) -> Option<&WebGlProgram> {
130        self.programs.get(program_id)
131    }
132
133    pub fn programs(&self) -> &HashMap<ProgramId, WebGlProgram> {
134        &self.programs
135    }
136
137    pub fn uniform(&self, uniform_id: &UniformId) -> Option<&Uniform<ProgramId, UniformId>> {
138        self.uniforms.get(uniform_id)
139    }
140
141    pub fn uniforms(&self) -> &HashMap<UniformId, Uniform<ProgramId, UniformId>> {
142        &self.uniforms
143    }
144
145    pub fn buffer(&self, buffer_id: &BufferId) -> Option<&Buffer<BufferId>> {
146        self.buffers.get(buffer_id)
147    }
148
149    pub fn buffers(&self) -> &HashMap<BufferId, Buffer<BufferId>> {
150        &self.buffers
151    }
152
153    pub fn attribute(
154        &self,
155        attribute_id: &AttributeId,
156    ) -> Option<&Attribute<VertexArrayObjectId, BufferId, AttributeId>> {
157        self.attributes.get(attribute_id)
158    }
159
160    pub fn attributes(
161        &self,
162    ) -> &HashMap<AttributeId, Attribute<VertexArrayObjectId, BufferId, AttributeId>> {
163        &self.attributes
164    }
165
166    pub fn texture(&self, texture_id: &TextureId) -> Option<&Texture<TextureId>> {
167        self.textures.get(texture_id)
168    }
169
170    pub fn textures(&self) -> &HashMap<TextureId, Texture<TextureId>> {
171        &self.textures
172    }
173
174    pub fn textures_by_id(
175        &self,
176        texture_ids: impl Into<Bridge<TextureId>>,
177    ) -> Vec<&Texture<TextureId>> {
178        let texture_ids: Bridge<_> = texture_ids.into();
179        let texture_ids: Vec<_> = texture_ids.into();
180        let mut textures = Vec::with_capacity(texture_ids.len());
181        for texture_id in texture_ids {
182            let texture = self.texture(&texture_id);
183            if let Some(texture) = texture {
184                textures.push(texture);
185            }
186        }
187        textures
188    }
189
190    pub fn framebuffer(
191        &self,
192        framebuffer_id: &FramebufferId,
193    ) -> Option<&Framebuffer<FramebufferId>> {
194        self.framebuffers.get(framebuffer_id)
195    }
196
197    pub fn transform_feedback(
198        &self,
199        transform_feedback_id: &TransformFeedbackId,
200    ) -> Option<&WebGlTransformFeedback> {
201        self.transform_feedbacks.get(transform_feedback_id)
202    }
203
204    pub fn vao(&self, vao_id: &VertexArrayObjectId) -> Option<&WebGlVertexArrayObject> {
205        self.vertex_array_objects.get(vao_id)
206    }
207
208    // @todo - enable ctx to be returned unconditionally (depending on if it's set or not)
209    pub fn user_ctx(&self) -> Option<&UserCtx> {
210        self.user_ctx.as_ref()
211    }
212
213    /// Switches to using new program and its associated VAO
214    pub fn use_program(&self, program_id: &ProgramId) -> &Self {
215        let program = self
216            .programs
217            .get(program_id)
218            .unwrap_or_else(|| panic!("Error in `use_program`: No corresponding Program found for ProgramId: {program_id:?}"));
219
220        self.gl().use_program(Some(program));
221
222        self
223    }
224
225    pub fn use_vao(&self, vao_id: &VertexArrayObjectId) -> &Self {
226        let vao = self
227            .vertex_array_objects
228            .get(vao_id)
229            .expect("VAO should exist for ProgramId");
230
231        self.gl().bind_vertex_array(Some(vao));
232
233        self
234    }
235
236    /// Updates a single uniform using the previously given update function. If no function was supplied,
237    /// then this is a no-op.
238    ///
239    /// Calls "use_program" on the appropriate program before each uniform's update function (so this is not
240    /// necessary to do within the callback itself, unless you need to change programs, for whatever reason).
241    pub fn update_uniform(&self, uniform_id: &UniformId) -> &Self {
242        let now = Self::now();
243        let _user_ctx = self.user_ctx();
244        let gl = self.gl();
245        let programs = &self.programs;
246        let uniform = self
247            .uniforms
248            .get(uniform_id)
249            .expect("UniformId should exist in registered uniforms");
250
251        uniform.update(gl, now, programs);
252
253        self
254    }
255
256    /// Iterates through all saved uniforms and updates them using their associated update callbacks.
257    pub fn update_uniforms(&self) -> &Self {
258        for uniform_id in self.uniforms.keys() {
259            self.update_uniform(uniform_id);
260        }
261
262        self
263    }
264
265    /// Note: if a JavaScript `render` callback has been supplied, then this function will NOT call
266    /// try to call it with `RendererData` passed in, because doing so would require copying all internal
267    /// data in order to pass that data into JavaScript, which would be very slow for large `RenderData` objects.
268    ///
269    /// If you want to render using a JavaScript `render` callback, instead consider using the `RendererJs` struct
270    /// (exported to JavaScript as `Renderer`) OR the `RendererDataJs` struct (exported to JavaScript as `RendererData`),
271    /// since these two functions automatically pass in `RendererData` if the types are compatible with JavaScript.
272    pub fn render(&self) -> &Self {
273        self.render_callback.call_with_rust_arg(self);
274        self
275    }
276
277    pub fn save_image(&self) {
278        let window = window().unwrap();
279        let document = window.document().unwrap();
280        let body = document.body().unwrap();
281        let a: HtmlAnchorElement = document.create_element("a").unwrap().dyn_into().unwrap();
282        let data_url = self
283            .canvas
284            .to_data_url()
285            .unwrap()
286            .replace("image/png", "image/octet-stream");
287
288        a.style().set_css_text("display: none;");
289        a.set_href(&data_url);
290        a.set_download("image.png");
291
292        body.append_child(&a).unwrap();
293        a.click();
294        body.remove_child(&a).unwrap();
295    }
296
297    /// Moves Renderer into a `Renderer` struct, providing additional functionality like
298    /// managed animations and recording.
299    pub fn into_renderer(
300        self,
301    ) -> Renderer<
302        VertexShaderId,
303        FragmentShaderId,
304        ProgramId,
305        UniformId,
306        BufferId,
307        AttributeId,
308        TextureId,
309        FramebufferId,
310        TransformFeedbackId,
311        VertexArrayObjectId,
312        UserCtx,
313    > {
314        self.into()
315    }
316
317    /// Gets current DOMHighResTimeStamp from performance.now()
318    ///
319    /// WebGL is limited to an f32, so using performance.now() (for now) to limit the size of the f64
320    fn now() -> f64 {
321        window().unwrap().performance().unwrap().now()
322    }
323
324    pub fn render_callback(
325        &self,
326    ) -> RenderCallback<
327        VertexShaderId,
328        FragmentShaderId,
329        ProgramId,
330        UniformId,
331        BufferId,
332        AttributeId,
333        TextureId,
334        FramebufferId,
335        TransformFeedbackId,
336        VertexArrayObjectId,
337        UserCtx,
338    > {
339        self.render_callback.to_owned()
340    }
341}
342
343impl<
344        VertexShaderId: Id,
345        FragmentShaderId: Id,
346        ProgramId: Id,
347        UniformId: Id + IdName,
348        BufferId: Id,
349        AttributeId: Id + IdName,
350        TextureId: Id,
351        FramebufferId: Id,
352        TransformFeedbackId: Id,
353        VertexArrayObjectId: Id,
354        UserCtx: Clone,
355    > AsRef<HtmlCanvasElement>
356    for RendererData<
357        VertexShaderId,
358        FragmentShaderId,
359        ProgramId,
360        UniformId,
361        BufferId,
362        AttributeId,
363        TextureId,
364        FramebufferId,
365        TransformFeedbackId,
366        VertexArrayObjectId,
367        UserCtx,
368    >
369{
370    fn as_ref(&self) -> &HtmlCanvasElement {
371        self.canvas()
372    }
373}
374
375impl From<RendererDataJsInner> for JsValue {
376    fn from(renderer_data: RendererDataJsInner) -> Self {
377        let js_renderer: RendererDataJs = renderer_data.into();
378        js_renderer.into()
379    }
380}
381
382#[derive(Debug, Clone)]
383pub struct RendererDataBuilder<
384    VertexShaderId: Id = IdDefault,
385    FragmentShaderId: Id = IdDefault,
386    ProgramId: Id = IdDefault,
387    UniformId: Id + IdName = IdDefault,
388    BufferId: Id = IdDefault,
389    AttributeId: Id + IdName = IdDefault,
390    TextureId: Id = IdDefault,
391    FramebufferId: Id = IdDefault,
392    TransformFeedbackId: Id = IdDefault,
393    VertexArrayObjectId: Id = IdDefault,
394    UserCtx: Clone + 'static = (),
395> {
396    canvas: Option<HtmlCanvasElement>,
397    gl: Option<WebGl2RenderingContext>,
398    vertex_shader_sources: HashMap<VertexShaderId, String>,
399    fragment_shader_sources: HashMap<FragmentShaderId, String>,
400    vertex_shaders: HashMap<VertexShaderId, WebGlShader>,
401    fragment_shaders: HashMap<FragmentShaderId, WebGlShader>,
402    program_links: HashSet<ProgramLink<ProgramId, VertexShaderId, FragmentShaderId>>,
403    programs: HashMap<ProgramId, WebGlProgram>,
404    uniform_links: HashSet<UniformLink<ProgramId, UniformId>>,
405    uniforms: HashMap<UniformId, Uniform<ProgramId, UniformId>>,
406    buffer_links: HashSet<BufferLink<BufferId>>,
407    buffers: HashMap<BufferId, Buffer<BufferId>>,
408    attribute_links: HashSet<AttributeLink<VertexArrayObjectId, BufferId, AttributeId>>,
409    attribute_locations: HashMap<AttributeId, u32>,
410    attributes: HashMap<AttributeId, Attribute<VertexArrayObjectId, BufferId, AttributeId>>,
411    texture_links: HashSet<TextureLink<TextureId>>,
412    textures: HashMap<TextureId, Texture<TextureId>>,
413    framebuffer_links: HashSet<FramebufferLink<FramebufferId, TextureId>>,
414    framebuffers: HashMap<FramebufferId, Framebuffer<FramebufferId>>,
415    render_callback: Option<
416        RenderCallback<
417            VertexShaderId,
418            FragmentShaderId,
419            ProgramId,
420            UniformId,
421            BufferId,
422            AttributeId,
423            TextureId,
424            FramebufferId,
425            TransformFeedbackId,
426            VertexArrayObjectId,
427            UserCtx,
428        >,
429    >,
430    user_ctx: Option<UserCtx>,
431    vertex_array_object_links: HashSet<VertexArrayObjectId>,
432    vertex_array_objects: HashMap<VertexArrayObjectId, WebGlVertexArrayObject>,
433    transform_feedback_links: HashSet<TransformFeedbackLink<TransformFeedbackId>>,
434    transform_feedbacks: HashMap<TransformFeedbackId, WebGlTransformFeedback>,
435    get_context_callback: GetContextCallback,
436}
437
438/// Public API
439impl<
440        VertexShaderId: Id,
441        FragmentShaderId: Id,
442        ProgramId: Id,
443        UniformId: Id + IdName,
444        BufferId: Id,
445        AttributeId: Id + IdName,
446        TextureId: Id,
447        FramebufferId: Id,
448        TransformFeedbackId: Id,
449        VertexArrayObjectId: Id,
450        UserCtx: Clone + 'static,
451    >
452    RendererDataBuilder<
453        VertexShaderId,
454        FragmentShaderId,
455        ProgramId,
456        UniformId,
457        BufferId,
458        AttributeId,
459        TextureId,
460        FramebufferId,
461        TransformFeedbackId,
462        VertexArrayObjectId,
463        UserCtx,
464    >
465{
466    /// This is the only internal storage available publicly from the builder,
467    /// because it is necessary to use it during the build process for framebuffers.
468    pub fn texture(&self, texture_id: &TextureId) -> Option<&Texture<TextureId>> {
469        self.textures.get(texture_id)
470    }
471
472    /// Save the canvas that will be rendered to and get its associated WebGL2 rendering context
473    pub fn set_canvas(&mut self, canvas: HtmlCanvasElement) -> &mut Self {
474        self.canvas = Some(canvas);
475
476        self
477    }
478
479    /// Saves a fragment shader source and its corresponding id
480    pub fn add_fragment_shader_src(
481        &mut self,
482        id: FragmentShaderId,
483        fragment_shader_src: impl Into<String>,
484    ) -> &mut Self {
485        self.fragment_shader_sources
486            .insert(id, fragment_shader_src.into());
487
488        self
489    }
490
491    /// Saves a vertex shader source and its corresponding id
492    pub fn add_vertex_shader_src(
493        &mut self,
494        id: VertexShaderId,
495        vertex_shader_src: impl Into<String>,
496    ) -> &mut Self {
497        self.vertex_shader_sources
498            .insert(id, vertex_shader_src.into());
499
500        self
501    }
502
503    /// Saves a link between a vertex shader id and a fragment shader id.
504    ///
505    /// During the RendererData build process, this `program_link` is used to link a new WebGL2 program
506    /// together by associating the vertex shader id and the fragment shader id with their corresponding compiled shaders.
507    pub fn add_program_link(
508        &mut self,
509        program_link: impl Into<ProgramLink<ProgramId, VertexShaderId, FragmentShaderId>>,
510    ) -> &mut Self {
511        let program_link = program_link.into();
512        self.program_links.insert(program_link);
513
514        self
515    }
516
517    pub fn add_program_links(
518        &mut self,
519        program_links: impl Into<Bridge<ProgramLink<ProgramId, VertexShaderId, FragmentShaderId>>>,
520    ) -> &mut Self {
521        let program_link_bridge: Bridge<ProgramLink<ProgramId, VertexShaderId, FragmentShaderId>> =
522            program_links.into();
523        let program_links: Vec<_> = program_link_bridge.into();
524
525        for program_link in program_links {
526            self.add_program_link(program_link);
527        }
528
529        self
530    }
531
532    /// Save a callback that will be called each time it is time to render a new frame
533    pub fn set_render_callback(
534        &mut self,
535        render_callback: impl Into<
536            RenderCallback<
537                VertexShaderId,
538                FragmentShaderId,
539                ProgramId,
540                UniformId,
541                BufferId,
542                AttributeId,
543                TextureId,
544                FramebufferId,
545                TransformFeedbackId,
546                VertexArrayObjectId,
547                UserCtx,
548            >,
549        >,
550    ) -> &mut Self {
551        self.render_callback = Some(render_callback.into());
552
553        self
554    }
555
556    /// Save as arbitrary user context that can be accessed from within the render callback
557    ///
558    /// This can include stateful data and anything else that might be necessary to access
559    /// while performing a render.
560    pub fn set_user_ctx(&mut self, ctx: impl Into<UserCtx>) -> &mut Self {
561        self.user_ctx = Some(ctx.into());
562
563        self
564    }
565
566    /// Saves a link that will be used to build a uniform at build time.
567    ///
568    /// I.e. once all WebGL shaders are compiled and all programs are linked,
569    /// all uniforms will be found within their associated programs, and will be
570    /// saved with their associated update functions.
571    pub fn add_uniform_link(
572        &mut self,
573        uniform_link: impl Into<UniformLink<ProgramId, UniformId>>,
574    ) -> &mut Self {
575        self.uniform_links.insert(uniform_link.into());
576
577        self
578    }
579
580    pub fn add_uniform_links(
581        &mut self,
582        uniform_links: impl Into<Bridge<UniformLink<ProgramId, UniformId>>>,
583    ) -> &mut Self {
584        let uniform_link_bridge: Bridge<_> = uniform_links.into();
585        let uniform_links: Vec<_> = uniform_link_bridge.into();
586
587        for uniform_link in uniform_links {
588            self.add_uniform_link(uniform_link);
589        }
590
591        self
592    }
593
594    /// Saves a link that will be used to build a WebGL buffer at build time.
595    pub fn add_buffer_link(&mut self, buffer_link: impl Into<BufferLink<BufferId>>) -> &mut Self {
596        self.buffer_links.insert(buffer_link.into());
597
598        self
599    }
600
601    pub fn add_buffer_links(
602        &mut self,
603        buffer_links: impl Into<Bridge<BufferLink<BufferId>>>,
604    ) -> &mut Self {
605        let buffer_link_bridge: Bridge<_> = buffer_links.into();
606        let buffer_links: Vec<_> = buffer_link_bridge.into();
607
608        for buffer_link in buffer_links {
609            self.add_buffer_link(buffer_link);
610        }
611
612        self
613    }
614
615    /// Saves a link that will be used to build a a WebGL attribute at build time.
616    pub fn add_attribute_link(
617        &mut self,
618        attribute_link: impl Into<AttributeLink<VertexArrayObjectId, BufferId, AttributeId>>,
619    ) -> &mut Self {
620        let attribute_link = attribute_link.into();
621        let attribute_id = attribute_link.attribute_id().to_owned();
622        let new_attribute_location = self.attribute_links.len() as u32;
623        self.attribute_links.insert(attribute_link);
624        self.attribute_locations
625            .insert(attribute_id, new_attribute_location);
626
627        self
628    }
629
630    pub fn add_attribute_links(
631        &mut self,
632        attribute_links: impl Into<Bridge<AttributeLink<VertexArrayObjectId, BufferId, AttributeId>>>,
633    ) -> &mut Self {
634        let attribute_link_bridge: Bridge<_> = attribute_links.into();
635        let attribute_links: Vec<_> = attribute_link_bridge.into();
636
637        for attribute_link in attribute_links {
638            self.add_attribute_link(attribute_link);
639        }
640
641        self
642    }
643
644    /// Saves a link that will be used to build a buffer/attribute pair at build time.
645    pub fn add_texture_link(
646        &mut self,
647        texture_link: impl Into<TextureLink<TextureId>>,
648    ) -> &mut Self {
649        self.texture_links.insert(texture_link.into());
650
651        self
652    }
653
654    pub fn add_texture_links(
655        &mut self,
656        texture_links: impl Into<Bridge<TextureLink<TextureId>>>,
657    ) -> &mut Self {
658        let texture_link_bridge: Bridge<_> = texture_links.into();
659        let texture_links: Vec<_> = texture_link_bridge.into();
660
661        for texture_link in texture_links {
662            self.add_texture_link(texture_link);
663        }
664
665        self
666    }
667
668    /// Saves a link that will be used to build a framebuffer at build time
669    pub fn add_framebuffer_link(
670        &mut self,
671        framebuffer_link: impl Into<FramebufferLink<FramebufferId, TextureId>>,
672    ) -> &mut Self {
673        self.framebuffer_links.insert(framebuffer_link.into());
674
675        self
676    }
677
678    pub fn add_framebuffer_links(
679        &mut self,
680        framebuffer_links: impl Into<Bridge<FramebufferLink<FramebufferId, TextureId>>>,
681    ) -> &mut Self {
682        let framebuffer_link_bridge: Bridge<_> = framebuffer_links.into();
683        let framebuffer_links: Vec<_> = framebuffer_link_bridge.into();
684
685        for framebuffer_link in framebuffer_links {
686            self.add_framebuffer_link(framebuffer_link);
687        }
688
689        self
690    }
691
692    /// Saves a link that will be used to build a transformFeedback at build time
693    pub fn add_transform_feedback_link(
694        &mut self,
695        transform_feedback_link: impl Into<TransformFeedbackLink<TransformFeedbackId>>,
696    ) -> &mut Self {
697        self.transform_feedback_links
698            .insert(transform_feedback_link.into());
699
700        self
701    }
702
703    pub fn add_transform_feedback_links(
704        &mut self,
705        transform_feedback_links: impl Into<Bridge<TransformFeedbackLink<TransformFeedbackId>>>,
706    ) -> &mut Self {
707        let transform_feedback_link_bridge: Bridge<_> = transform_feedback_links.into();
708        let transform_feedback_links: Vec<_> = transform_feedback_link_bridge.into();
709
710        for transform_feedback_link in transform_feedback_links {
711            self.add_transform_feedback_link(transform_feedback_link);
712        }
713
714        self
715    }
716
717    /// Saves an id that will be used to create a VAO at build time
718    ///
719    /// This VAO can then be referenced by `AttributeLink`s
720    pub fn add_vao_link(
721        &mut self,
722        vertex_array_object_id: impl Into<VertexArrayObjectId>,
723    ) -> &mut Self {
724        self.vertex_array_object_links
725            .insert(vertex_array_object_id.into());
726
727        self
728    }
729
730    pub fn add_vao_links(
731        &mut self,
732        vao_links: impl Into<Bridge<VertexArrayObjectId>>,
733    ) -> &mut Self {
734        let vao_link_bridge: Bridge<_> = vao_links.into();
735        let vao_links: Vec<_> = vao_link_bridge.into();
736        let vao_links: Vec<VertexArrayObjectId> = vao_links.into_iter().collect();
737
738        for vao_link in vao_links {
739            self.add_vao_link(vao_link);
740        }
741
742        self
743    }
744
745    pub fn set_get_context_callback(
746        &mut self,
747        get_context_callback: impl Into<GetContextCallback>,
748    ) -> &mut Self {
749        self.get_context_callback = get_context_callback.into();
750        self
751    }
752
753    /// Compiles all vertex shaders and fragment shaders.
754    /// Links together any programs that have been specified.
755    /// Outputs the final RendererData, wrapped in a top-level Renderer.
756    pub fn build_renderer(
757        self,
758    ) -> Result<
759        Renderer<
760            VertexShaderId,
761            FragmentShaderId,
762            ProgramId,
763            UniformId,
764            BufferId,
765            AttributeId,
766            TextureId,
767            FramebufferId,
768            TransformFeedbackId,
769            VertexArrayObjectId,
770            UserCtx,
771        >,
772        RendererBuilderError,
773    > {
774        let renderer_data = self.build_renderer_data()?;
775        Ok(renderer_data.into())
776    }
777
778    /// Compiles all vertex shaders and fragment shaders.
779    /// Links together any programs that have been specified.
780    /// Outputs the final RendererData.
781    pub fn build_renderer_data(
782        mut self,
783    ) -> Result<
784        RendererData<
785            VertexShaderId,
786            FragmentShaderId,
787            ProgramId,
788            UniformId,
789            BufferId,
790            AttributeId,
791            TextureId,
792            FramebufferId,
793            TransformFeedbackId,
794            VertexArrayObjectId,
795            UserCtx,
796        >,
797        RendererBuilderError,
798    > {
799        // the order here is fairly important
800        self.save_webgl_context_from_canvas()?;
801        self.compile_fragment_shaders()?;
802        self.compile_vertex_shaders()?;
803        self.create_vaos()?;
804        self.link_programs()?;
805        self.create_buffers()?;
806        self.create_attributes()?;
807        self.create_uniforms()?;
808        self.create_textures()?;
809        self.create_framebuffers()?;
810        self.create_transform_feedbacks()?;
811
812        let renderer_data = RendererData {
813            canvas: self.canvas.ok_or(BuildRendererError::NoCanvas)?,
814            gl: self.gl.ok_or(BuildRendererError::NoContext)?,
815            fragment_shaders: self.fragment_shaders,
816            vertex_shaders: self.vertex_shaders,
817            programs: self.programs,
818            render_callback: self
819                .render_callback
820                .ok_or(BuildRendererError::NoRenderCallback)?,
821            user_ctx: self.user_ctx,
822            uniforms: self.uniforms,
823            buffers: self.buffers,
824            textures: self.textures,
825            framebuffers: self.framebuffers,
826            attributes: self.attributes,
827            vertex_array_objects: self.vertex_array_objects,
828            transform_feedbacks: self.transform_feedbacks,
829        };
830
831        Ok(renderer_data)
832    }
833}
834
835/// Private API
836impl<
837        VertexShaderId: Id,
838        FragmentShaderId: Id,
839        ProgramId: Id,
840        UniformId: Id + IdName,
841        BufferId: Id,
842        AttributeId: Id + IdName,
843        TextureId: Id,
844        FramebufferId: Id,
845        TransformFeedbackId: Id,
846        VertexArrayObjectId: Id,
847        UserCtx: Clone,
848    >
849    RendererDataBuilder<
850        VertexShaderId,
851        FragmentShaderId,
852        ProgramId,
853        UniformId,
854        BufferId,
855        AttributeId,
856        TextureId,
857        FramebufferId,
858        TransformFeedbackId,
859        VertexArrayObjectId,
860        UserCtx,
861    >
862{
863    /// Gets the WebGL2 context from the canvas saved in state and saves the context in state
864    fn save_webgl_context_from_canvas(&mut self) -> Result<&mut Self, RendererBuilderError> {
865        let canvas = self
866            .canvas
867            .as_ref()
868            .ok_or(SaveContextError::CanvasReturnedNoContext)?
869            .to_owned();
870        let gl = self.context_from_canvas(canvas)?;
871        self.gl = Some(gl);
872
873        Ok(self)
874    }
875
876    /// Get the WebGL2 rendering context from a canvas
877    fn context_from_canvas(
878        &self,
879        canvas: HtmlCanvasElement,
880    ) -> Result<WebGl2RenderingContext, WebGlContextError> {
881        let gl = match &*self.get_context_callback {
882            Callback::Rust(rust_callback) => (rust_callback)(canvas)?,
883            Callback::Js(js_callback) => {
884                let result = js_callback.call1(&JsValue::NULL, canvas.as_ref());
885                result.expect("Received error when trying call JavaScript `get_context_callback`")
886                    .dyn_into()
887                    .expect("Did not receive expected type `HtmlCanvasElement` from JavaScript function `get_context_callback`")
888            }
889        };
890
891        Ok(gl)
892    }
893
894    /// Takes the list of fragment shader sources and their ids and saves compiled `WebGlShader`s to state
895    fn compile_fragment_shaders(&mut self) -> Result<&mut Self, CompileShaderError> {
896        for (id, fragment_shader_src) in self.fragment_shader_sources.iter() {
897            let fragment_shader =
898                self.compile_shader(id.clone(), ShaderType::FragmentShader, fragment_shader_src)?;
899            self.fragment_shaders.insert((*id).clone(), fragment_shader);
900        }
901
902        Ok(self)
903    }
904
905    /// Takes the list of vertex shader sources and their ids and saves compiled `WebGlShader`s to state
906    fn compile_vertex_shaders(&mut self) -> Result<&mut Self, CompileShaderError> {
907        for (id, vertex_shader_src) in self.vertex_shader_sources.iter() {
908            let vertex_shader =
909                self.compile_shader(id.clone(), ShaderType::VertexShader, vertex_shader_src)?;
910            self.vertex_shaders.insert((*id).clone(), vertex_shader);
911        }
912
913        Ok(self)
914    }
915
916    fn create_transform_feedbacks(&mut self) -> Result<&mut Self, CreateTransformFeedbackError> {
917        let gl = self
918            .gl
919            .as_ref()
920            .ok_or(CreateTransformFeedbackError::NoContext)?;
921
922        for transform_feedback_link in self.transform_feedback_links.iter() {
923            let transform_feedback_id = transform_feedback_link.transform_feedback_id().clone();
924            let webgl_transform_feedback = gl
925                .create_transform_feedback()
926                .ok_or(CreateTransformFeedbackError::NoneWasReturned)?;
927            self.transform_feedbacks
928                .insert(transform_feedback_id, webgl_transform_feedback);
929        }
930
931        Ok(self)
932    }
933
934    /// Links together all of the vertex & fragment shaders that have been saved
935    /// according to any ProgramLinks that were provided.
936    ///
937    /// If a ProgramLink does not correspond to an actual shader, returns an Error.
938    fn link_programs(&mut self) -> Result<&mut Self, LinkProgramError> {
939        for program_link in self.program_links.iter() {
940            let program = self.link_program(program_link)?;
941            let program_id = program_link.program_id();
942            self.programs.insert(program_id.clone(), program);
943        }
944
945        Ok(self)
946    }
947
948    /// Find the uniform's position in a shader and constructs necessary data for each uniform.
949    fn create_uniform(
950        &self,
951        uniform_link: &UniformLink<ProgramId, UniformId>,
952    ) -> Result<Uniform<ProgramId, UniformId>, CreateUniformError> {
953        let uniform_id = uniform_link.uniform_id().clone();
954        let program_ids = uniform_link.program_ids().clone();
955        let use_init_callback_for_update = uniform_link.use_init_callback_for_update();
956        let gl = self.gl.as_ref().ok_or(CreateUniformError::NoContext)?;
957        let now = Self::now();
958        let _user_ctx = self.user_ctx.as_ref().map(Clone::clone);
959        let initialize_callback = uniform_link.initialize_callback();
960        let should_update_callback = uniform_link.should_update_callback();
961        let update_callback = uniform_link.update_callback();
962        let mut uniform_locations = HashMap::new();
963
964        for program_id in &program_ids {
965            let program = self
966                .programs
967                .get(program_id)
968                .ok_or(CreateUniformError::ProgramNotFound)?;
969
970            gl.use_program(Some(program));
971
972            let uniform_location = gl
973                .get_uniform_location(program, &uniform_id.name())
974                .ok_or_else(|| CreateUniformError::UniformLocationNotFound {
975                    uniform_id: uniform_id.name(),
976                    program_id: format!("{program_id:?}"),
977                })?;
978            let uniform_context = UniformContext::new(gl.clone(), now, uniform_location.clone());
979            initialize_callback.call_with_into_js_arg(&uniform_context);
980            uniform_locations.insert(program_id.to_owned(), uniform_location.clone());
981
982            gl.use_program(None);
983        }
984
985        let uniform = Uniform::new(
986            program_ids,
987            uniform_id,
988            uniform_locations,
989            initialize_callback,
990            update_callback,
991            should_update_callback,
992            use_init_callback_for_update,
993        );
994
995        Ok(uniform)
996    }
997
998    /// Creates all WebGL buffers, using the passed in BufferLinks
999    fn create_buffers(&mut self) -> Result<&mut Self, CreateBufferError> {
1000        let gl = self.gl.as_ref().ok_or(CreateBufferError::NoContext)?;
1001        let now = Self::now();
1002
1003        for buffer_link in &self.buffer_links {
1004            let buffer_id = buffer_link.buffer_id().clone();
1005            let webgl_buffer = buffer_link.create_buffer(gl.clone(), now);
1006            let buffer = Buffer::new(buffer_id.clone(), webgl_buffer);
1007            self.buffers.insert(buffer_id, buffer);
1008        }
1009
1010        Ok(self)
1011    }
1012
1013    fn create_vaos(&mut self) -> Result<&mut Self, CreateVAOError> {
1014        let gl = self.gl.as_ref().ok_or(CreateVAOError::NoContext)?;
1015
1016        for vao_id in self.vertex_array_object_links.iter() {
1017            let vao = gl
1018                .create_vertex_array()
1019                .ok_or(CreateVAOError::NoneWasReturned)?;
1020            self.vertex_array_objects.insert(vao_id.to_owned(), vao);
1021        }
1022
1023        Ok(self)
1024    }
1025
1026    /// Creates a WebGL attribute for each AttributeLink that was supplied using the create_callback
1027    fn create_attributes(&mut self) -> Result<&mut Self, CreateAttributeError> {
1028        let gl = self.gl.as_ref().ok_or(CreateAttributeError::NoContext)?;
1029        let now = Self::now();
1030        let _user_ctx = self.user_ctx.clone();
1031
1032        for attribute_link in &self.attribute_links {
1033            let vao_ids = attribute_link.vao_ids();
1034            let buffer_id = attribute_link.buffer_id().clone();
1035            let attribute_id = attribute_link.attribute_id().clone();
1036            let webgl_buffer = self
1037                .buffers
1038                .get(&buffer_id)
1039                .ok_or(CreateAttributeError::BufferNotFound)?
1040                .webgl_buffer()
1041                .clone();
1042            let attribute_location = self
1043                .attribute_locations
1044                .get(&attribute_id)
1045                .ok_or(CreateAttributeError::AttributeLocationNotFound)?;
1046
1047            if vao_ids.is_empty() {
1048                // initialize attribute on the default VAO context
1049                gl.bind_vertex_array(None);
1050                gl.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, Some(&webgl_buffer));
1051                gl.enable_vertex_attrib_array(*attribute_location);
1052                // create callback is expected to initialize its associated attribute
1053                // with a call to vertexAttribPointer,
1054                // which is saved in the associated VAO
1055                attribute_link.create_attribute(
1056                    gl.clone(),
1057                    now,
1058                    webgl_buffer.clone(),
1059                    attribute_location.into(),
1060                );
1061                gl.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, None);
1062            } else {
1063                // initialize attribute for each VAO that it is linked to
1064                for vao_id in vao_ids {
1065                    let vao = self
1066                        .vertex_array_objects
1067                        .get(vao_id)
1068                        .ok_or(CreateAttributeError::VAONotFound)?;
1069
1070                    gl.bind_vertex_array(Some(vao));
1071                    gl.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, Some(&webgl_buffer));
1072                    gl.enable_vertex_attrib_array(*attribute_location);
1073                    // create callback is expected to initialize its associated attribute
1074                    // with a call to vertexAttribPointer,
1075                    // which is saved in the associated VAO
1076                    attribute_link.create_attribute(
1077                        gl.clone(),
1078                        now,
1079                        webgl_buffer.clone(),
1080                        attribute_location.into(),
1081                    );
1082                    gl.bind_vertex_array(None);
1083                    gl.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, None);
1084                }
1085            }
1086
1087            let attribute = Attribute::new(
1088                vao_ids.to_vec(),
1089                buffer_id.clone(),
1090                attribute_id.clone(),
1091                webgl_buffer.clone(),
1092                attribute_location.into(),
1093            );
1094
1095            self.attributes.insert(attribute_id, attribute);
1096        }
1097
1098        Ok(self)
1099    }
1100
1101    /// Creates a WebGL texture for each Texture that was supplied using the create_texture callback
1102    fn create_textures(&mut self) -> Result<&mut Self, CreateTextureError> {
1103        let gl = self.gl.as_ref().ok_or(CreateTextureError::NoContext)?;
1104        let now = Self::now();
1105        let canvas = self.canvas.clone().ok_or(CreateTextureError::NoCanvas)?;
1106
1107        for texture_link in &self.texture_links {
1108            let texture_id = texture_link.texture_id().clone();
1109            let webgl_texture = texture_link.create_texture(gl.clone(), now, canvas.clone());
1110            let texture = Texture::new(texture_id.clone(), webgl_texture);
1111
1112            self.textures.insert(texture_id, texture);
1113        }
1114
1115        Ok(self)
1116    }
1117
1118    /// Creates a WebGL Framebuffer for each FramebufferLink that was supplied using the callback
1119    fn create_framebuffers(&mut self) -> Result<&mut Self, CreateBufferError> {
1120        let gl = self.gl.as_ref().ok_or(CreateBufferError::NoContext)?;
1121        let now = Self::now();
1122        let _user_ctx = self.user_ctx.clone();
1123
1124        for framebuffer_link in &self.framebuffer_links {
1125            let framebuffer_id = framebuffer_link.framebuffer_id().clone();
1126            let webgl_texture = framebuffer_link
1127                .texture_id()
1128                .and_then(|texture_id| self.textures.get(&texture_id))
1129                .map(|texture| texture.webgl_texture())
1130                .map(Clone::clone);
1131
1132            let webgl_framebuffer =
1133                framebuffer_link.create_framebuffer(gl.clone(), now, webgl_texture);
1134            let framebuffer = Framebuffer::new(framebuffer_id.clone(), webgl_framebuffer);
1135
1136            self.framebuffers.insert(framebuffer_id, framebuffer);
1137        }
1138
1139        Ok(self)
1140    }
1141
1142    /// Finds every uniform's position in its corresponding program and builds a wrapper for it
1143    fn create_uniforms(&mut self) -> Result<&mut Self, CreateUniformError> {
1144        for uniform_link in self.uniform_links.iter() {
1145            let uniform_id = uniform_link.uniform_id().clone();
1146            let uniform = self.create_uniform(uniform_link)?;
1147            self.uniforms.insert(uniform_id, uniform);
1148        }
1149
1150        Ok(self)
1151    }
1152
1153    fn link_program(
1154        &self,
1155        program_link: &ProgramLink<ProgramId, VertexShaderId, FragmentShaderId>,
1156    ) -> Result<WebGlProgram, LinkProgramError> {
1157        let gl = self.gl.as_ref().ok_or(LinkProgramError::NoContext)?;
1158
1159        let vertex_shader_id = program_link.vertex_shader_id();
1160        let vertex_shader = self
1161            .vertex_shaders
1162            .get(vertex_shader_id)
1163            .ok_or(LinkProgramError::VertexShaderNotFound)?;
1164
1165        let fragment_shader_id = program_link.fragment_shader_id();
1166        let fragment_shader = self
1167            .fragment_shaders
1168            .get(fragment_shader_id)
1169            .ok_or(LinkProgramError::FragmentShaderNotFound)?;
1170
1171        // @todo - make this not have to clone the slice
1172        let transform_feedback_varyings = program_link.transform_feedback_varyings().to_vec();
1173
1174        let webgl_program = gl.create_program().ok_or(LinkProgramError::NoProgram)?;
1175
1176        // assign attribute locations
1177        for (attribute_id, attribute_location) in self.attribute_locations.iter() {
1178            gl.bind_attrib_location(&webgl_program, *attribute_location, &attribute_id.name());
1179        }
1180
1181        gl.attach_shader(&webgl_program, vertex_shader);
1182        gl.attach_shader(&webgl_program, fragment_shader);
1183
1184        if !transform_feedback_varyings.is_empty() {
1185            let varyings_js_value = JsValue::from_serde(&transform_feedback_varyings)
1186                .map_err(|_| LinkProgramError::CouldNotConvertVaryingsToArray)?;
1187            gl.transform_feedback_varyings(
1188                &webgl_program,
1189                &varyings_js_value,
1190                WebGl2RenderingContext::INTERLEAVED_ATTRIBS,
1191            )
1192        }
1193
1194        gl.link_program(&webgl_program);
1195
1196        if gl
1197            .get_program_parameter(&webgl_program, WebGl2RenderingContext::LINK_STATUS)
1198            .as_bool()
1199            .unwrap_or(false)
1200        {
1201            Ok(webgl_program)
1202        } else {
1203            let inner_error = match gl.get_program_info_log(&webgl_program) {
1204                Some(known_error) => LinkProgramError::KnownError(known_error),
1205                None => LinkProgramError::UnknownError,
1206            };
1207            Err(inner_error)?
1208        }
1209    }
1210
1211    /// Gets current DOMHighResTimeStamp from performance.now()
1212    ///
1213    /// WebGL is limited to an f32, so using performance.now() (for now) to limit the size of the f64
1214    fn now() -> f64 {
1215        window().unwrap().performance().unwrap().now()
1216    }
1217
1218    /// Takes the string source of a shader and compiles to using the current WebGL2RenderingContext
1219    fn compile_shader<ShaderId: Id>(
1220        &self,
1221        shader_id: ShaderId,
1222        shader_type: ShaderType,
1223        source: &str,
1224    ) -> Result<WebGlShader, CompileShaderError> {
1225        let gl = self.gl.as_ref().ok_or(CompileShaderError::NoContext {
1226            shader_id: format!("{shader_id:#?}"),
1227        })?;
1228
1229        let shader =
1230            gl.create_shader(shader_type.into())
1231                .ok_or(CompileShaderError::NoShaderReturned {
1232                    shader_id: format!("{shader_id:#?}"),
1233                })?;
1234
1235        gl.shader_source(&shader, source);
1236        gl.compile_shader(&shader);
1237
1238        if gl
1239            .get_shader_parameter(&shader, WebGl2RenderingContext::COMPILE_STATUS)
1240            .as_bool()
1241            .unwrap_or(false)
1242        {
1243            Ok(shader)
1244        } else {
1245            let inner_error = match gl.get_shader_info_log(&shader) {
1246                Some(known_error) => CompileShaderError::KnownError {
1247                    shader_id: format!("{shader_id:#?}"),
1248                    error: known_error,
1249                },
1250                None => CompileShaderError::UnknownError {
1251                    shader_id: format!("{shader_id:#?}"),
1252                },
1253            };
1254            Err(inner_error)?
1255        }
1256    }
1257}
1258
1259impl<
1260        VertexShaderId: Id,
1261        FragmentShaderId: Id,
1262        ProgramId: Id,
1263        UniformId: Id + IdName,
1264        BufferId: Id,
1265        AttributeId: Id + IdName,
1266        TextureId: Id,
1267        FramebufferId: Id,
1268        TransformFeedbackId: Id,
1269        VertexArrayObjectId: Id,
1270        UserCtx: Clone,
1271    > Default
1272    for RendererDataBuilder<
1273        VertexShaderId,
1274        FragmentShaderId,
1275        ProgramId,
1276        UniformId,
1277        BufferId,
1278        AttributeId,
1279        TextureId,
1280        FramebufferId,
1281        TransformFeedbackId,
1282        VertexArrayObjectId,
1283        UserCtx,
1284    >
1285{
1286    fn default() -> Self {
1287        Self {
1288            canvas: Default::default(),
1289            gl: Default::default(),
1290            vertex_shader_sources: Default::default(),
1291            fragment_shader_sources: Default::default(),
1292            vertex_shaders: Default::default(),
1293            fragment_shaders: Default::default(),
1294            program_links: Default::default(),
1295            programs: Default::default(),
1296            render_callback: Default::default(),
1297            user_ctx: Default::default(),
1298            uniform_links: Default::default(),
1299            uniforms: Default::default(),
1300            buffer_links: Default::default(),
1301            buffers: Default::default(),
1302            texture_links: Default::default(),
1303            textures: Default::default(),
1304            framebuffer_links: Default::default(),
1305            framebuffers: Default::default(),
1306            attribute_links: Default::default(),
1307            attributes: Default::default(),
1308            vertex_array_object_links: Default::default(),
1309            vertex_array_objects: Default::default(),
1310            transform_feedbacks: Default::default(),
1311            transform_feedback_links: Default::default(),
1312            get_context_callback: Default::default(),
1313            attribute_locations: Default::default(),
1314        }
1315    }
1316}