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
61impl<
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 pub fn user_ctx(&self) -> Option<&UserCtx> {
210 self.user_ctx.as_ref()
211 }
212
213 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 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 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 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 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 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
438impl<
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 pub fn texture(&self, texture_id: &TextureId) -> Option<&Texture<TextureId>> {
469 self.textures.get(texture_id)
470 }
471
472 pub fn set_canvas(&mut self, canvas: HtmlCanvasElement) -> &mut Self {
474 self.canvas = Some(canvas);
475
476 self
477 }
478
479 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
835impl<
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 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 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 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 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 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 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 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 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 gl.bind_vertex_array(None);
1050 gl.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, Some(&webgl_buffer));
1051 gl.enable_vertex_attrib_array(*attribute_location);
1052 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 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 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 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 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 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 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 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 fn now() -> f64 {
1215 window().unwrap().performance().unwrap().now()
1216 }
1217
1218 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}