murrelet_gpu/
gpu_macros.rs

1#![allow(dead_code)]
2use std::{cell::RefCell, collections::HashMap, fs, path::PathBuf, rc::Rc};
3
4use glam::Vec2;
5use murrelet_common::MurreletTime;
6use serde::Serialize;
7
8use crate::{
9    device_state::{DeviceStateForRender, GraphicsAssets, GraphicsWindowConf},
10    gpu_livecode::ControlGraphicsRef,
11    graphics_ref::{
12        BasicUniform, Graphics, GraphicsCreator, GraphicsRef, GraphicsRefWithControlFn,
13        DEFAULT_LOADED_TEXTURE_FORMAT,
14    },
15    shader_str::*,
16};
17
18#[cfg(feature = "nannou")]
19use wgpu_for_nannou as wgpu;
20
21#[cfg(not(feature = "nannou"))]
22use wgpu_for_latest as wgpu;
23
24// const DEFAULT_LOADED_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Uint;
25
26const DEFAULT_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float;
27
28/// For example
29/// Examples:
30///
31///    build_shader!{
32///     raw r###"
33///     fn shape(in: vec3<f32>) -> f32 {
34///         let rep_period = vec3<f32>(10.0);
35///         let q: vec3<f32> = (mod(in + 0.5 * rep_period, rep_period)) - 0.5 * rep_period;
36///         let sphere = length(q) - 1.5;
37
38///         // let sphere = length(in) - 3.2;
39///         return sphere;
40///     }
41///     "###;
42///     use "ray_march"
43///    }
44///
45
46#[macro_export]
47macro_rules! build_shader {
48    (@parse ()) => {{""}}; // done!
49
50    // includes, should figure out how to include strs now..
51    // (@parse (use $loc:expr;$($tail:tt)*)) => {
52    //     {
53    //         let data = load_macro_shader!($loc);
54    //         let rest = build_shader!(@parse ($($tail)*));
55    //         format!("{}\n{}", data, rest)
56    //     }
57    // };
58
59    // for raw things in the prefix
60    (@parse (raw $raw:expr;$($tail:tt)*)) => {
61        {
62            let rest = build_shader!(@parse ($($tail)*));
63            format!("{}\n{}", $raw, rest)
64        }
65    };
66
67    (@parsecode ()) => {{""}}; // done!
68
69    (@parsecode (raw $raw:expr;$($tail:tt)*)) => {
70        {
71            let rest = build_shader!(@parsecode ($($tail)*));
72            format!("{}\n{}", $raw, rest)
73        }
74    };
75
76    // wrap the main code itself in ()
77    (@parse (($($tail:tt)*))) => {
78        {
79            let prefix = ShaderStr::Prefix.to_str();
80            let rest = build_shader!(@parsecode ($($tail)*));
81            let suffix = ShaderStr::Suffix.to_str();
82            format!("{}\n{}\n{}", prefix, rest, suffix)
83        }
84    }; // includes
85
86    // arm for funky parsing
87    (@parse $($raw:tt)*) => {
88        {
89            println!("???");
90            "???"
91            // unreachable!();
92        }
93    };
94
95    // capture the initial one
96    ($($raw:tt)*) => {
97        {
98            format!(
99                "{}\n{}\n{}",
100                ShaderStr::Binding1Tex.to_str(),
101                ShaderStr::Includes.to_str(),
102                build_shader!(@parse ($($raw)*)),
103            )
104        }
105    };
106}
107
108// i think i originally added this so i could use it?
109pub enum ShaderStr {
110    Binding1Tex,
111    Binding2Tex,
112    Binding3d,
113    Includes,
114    Prefix,
115    Suffix,
116}
117impl ShaderStr {
118    pub fn to_str(&self) -> &str {
119        match self {
120            ShaderStr::Binding1Tex => BINDING_1TEX,
121            ShaderStr::Binding2Tex => BINDING_2TEX,
122            ShaderStr::Includes => INCLUDES,
123            ShaderStr::Prefix => PREFIX,
124            ShaderStr::Suffix => SUFFIX,
125            ShaderStr::Binding3d => BINDING_3D,
126        }
127    }
128}
129
130#[macro_export]
131macro_rules! build_shader_2tex {
132    // capture the initial one
133    ($($raw:tt)*) => {
134        {
135            format!(
136                "{}\n{}\n{}",
137                ShaderStr::Binding2Tex.to_str(),
138                ShaderStr::Includes.to_str(),
139                build_shader!(@parse ($($raw)*)),
140            )
141        }
142    };
143}
144
145#[macro_export]
146macro_rules! build_shader_3d {
147    ($($raw:tt)*) => {
148        {
149            format!(
150                "{}\n{}\n{}",
151                ShaderStr::Binding3d.to_str(),
152                ShaderStr::Includes.to_str(),
153                build_shader!(@parse ($($raw)*)),
154            )
155        }
156    };
157}
158
159#[derive(Serialize)]
160pub struct RenderDebugPrint {
161    pub src: String,
162    pub dest: String,
163}
164
165#[derive(Serialize)]
166pub struct WrappedRenderDebugPrint {
167    pub idx: String,
168    pub r: RenderDebugPrint,
169}
170
171pub trait RenderTrait {
172    fn render(&self, device_state_for_render: &DeviceStateForRender);
173    fn debug_print(&self) -> Vec<RenderDebugPrint>;
174
175    fn dest(&self) -> Option<GraphicsRef>;
176
177    // hmm, i don't know how to do this with the boxes
178    fn is_choice(&self) -> bool {
179        false
180    }
181
182    fn adjust_choice(&mut self, _choice_val: usize) {}
183}
184
185pub struct SimpleRender {
186    pub source: GraphicsRef,
187    pub dest: GraphicsRef,
188}
189impl SimpleRender {
190    pub fn new_box(source: GraphicsRef, dest: GraphicsRef) -> Box<SimpleRender> {
191        Box::new(SimpleRender { source, dest })
192    }
193
194    fn dest(&self) -> Option<GraphicsRef> {
195        Some(self.dest.clone())
196    }
197}
198
199impl RenderTrait for SimpleRender {
200    fn render(&self, device: &DeviceStateForRender) {
201        self.source.render(device.device_state(), &self.dest);
202    }
203
204    fn debug_print(&self) -> Vec<RenderDebugPrint> {
205        vec![RenderDebugPrint {
206            src: self.source.name(),
207            dest: self.dest.name(),
208        }]
209    }
210
211    fn dest(&self) -> Option<GraphicsRef> {
212        Some(self.dest.clone())
213    }
214}
215
216pub struct TwoSourcesRender {
217    pub source_main: GraphicsRef,
218    pub source_other: GraphicsRef,
219    pub dest: GraphicsRef,
220}
221impl TwoSourcesRender {
222    pub fn new_box(
223        source_main: GraphicsRef,
224        source_other: GraphicsRef,
225        dest: GraphicsRef,
226    ) -> Box<TwoSourcesRender> {
227        Box::new(TwoSourcesRender {
228            source_main,
229            source_other,
230            dest,
231        })
232    }
233
234    fn dest(&self) -> Option<GraphicsRef> {
235        Some(self.dest.clone())
236    }
237}
238
239impl RenderTrait for TwoSourcesRender {
240    fn render(&self, device: &DeviceStateForRender) {
241        self.source_main.render(device.device_state(), &self.dest);
242        self.source_other
243            .render_2tex(device.device_state(), &self.dest);
244    }
245
246    fn debug_print(&self) -> Vec<RenderDebugPrint> {
247        vec![
248            RenderDebugPrint {
249                src: self.source_main.name(),
250                dest: self.dest.name(),
251            },
252            RenderDebugPrint {
253                src: self.source_other.name(),
254                dest: self.dest.name(),
255            },
256        ]
257    }
258
259    fn dest(&self) -> Option<GraphicsRef> {
260        Some(self.dest.clone())
261    }
262}
263
264// holds a gpu pipeline :O
265pub struct PipelineRender<GraphicsConf> {
266    pub source: GraphicsRef,
267    pub pipeline: GPUPipelineRef<GraphicsConf>,
268    pub dest: GraphicsRef,
269}
270
271impl<GraphicsConf> PipelineRender<GraphicsConf> {
272    pub fn new_box(
273        source: GraphicsRef,
274        pipeline: GPUPipelineRef<GraphicsConf>,
275        dest: GraphicsRef,
276    ) -> Box<Self> {
277        Box::new(Self {
278            source,
279            pipeline,
280            dest,
281        })
282    }
283}
284impl<GraphicsConf> RenderTrait for PipelineRender<GraphicsConf> {
285    fn render(&self, device_state_for_render: &DeviceStateForRender) {
286        // write source to pipeline source
287        self.source.render(
288            device_state_for_render.device_state(),
289            &self.pipeline.source(),
290        );
291        self.pipeline.render(device_state_for_render);
292    }
293
294    fn debug_print(&self) -> Vec<RenderDebugPrint> {
295        self.pipeline.debug_print()
296    }
297
298    fn dest(&self) -> Option<GraphicsRef> {
299        Some(self.dest.clone())
300    }
301}
302
303// given a list of inputs, choose which one to use
304pub struct ChoiceRender {
305    pub sources: Vec<GraphicsRef>,
306    pub dest: GraphicsRef,
307    choice: usize,
308}
309impl ChoiceRender {
310    pub fn new_box(sources: Vec<GraphicsRef>, dest: GraphicsRef) -> Box<ChoiceRender> {
311        Box::new(ChoiceRender {
312            sources,
313            dest,
314            choice: 0,
315        })
316    }
317}
318
319impl RenderTrait for ChoiceRender {
320    fn render(&self, device: &DeviceStateForRender) {
321        let source = &self.sources[self.choice % self.sources.len()];
322        let dest = &self.dest;
323        source.render(device.device_state(), dest);
324    }
325
326    fn debug_print(&self) -> Vec<RenderDebugPrint> {
327        // let source_names = self.sources.borrow_mut();
328        // let dest = self.dest.borrow_mut();
329        // vec![RenderDebugPrint{src: source_main.name.clone(), dest: dest.name.clone()}, RenderDebugPrint{src: source_other.name.clone(), dest: dest.name.clone()}]
330        todo!()
331    }
332
333    fn dest(&self) -> Option<GraphicsRef> {
334        Some(self.dest.clone())
335    }
336
337    fn is_choice(&self) -> bool {
338        true
339    }
340
341    // wraps if wrong
342    fn adjust_choice(&mut self, choice_val: usize) {
343        self.choice = choice_val % self.sources.len()
344    }
345}
346
347pub struct PingPongRender {
348    pub k: usize,
349    pub ping: GraphicsRef, // it'll end up here
350    pub pong: GraphicsRef,
351}
352
353impl PingPongRender {
354    pub fn new_box(k: usize, ping: GraphicsRef, pong: GraphicsRef) -> Box<PingPongRender> {
355        Box::new(PingPongRender { k, ping, pong })
356    }
357}
358
359impl RenderTrait for PingPongRender {
360    fn render(&self, device: &DeviceStateForRender) {
361        let ping = &self.ping;
362        let pong = &self.pong;
363        for _ in 0..self.k {
364            ping.render(device.device_state(), &pong);
365            pong.render(device.device_state(), &ping);
366        }
367    }
368
369    fn debug_print(&self) -> Vec<RenderDebugPrint> {
370        let ping = &self.ping;
371        let pong = &self.pong;
372        vec![
373            RenderDebugPrint {
374                src: ping.name(),
375                dest: pong.name(),
376            },
377            RenderDebugPrint {
378                src: pong.name(),
379                dest: ping.name(),
380            },
381            RenderDebugPrint {
382                src: ping.name(),
383                dest: pong.name(),
384            },
385            RenderDebugPrint {
386                src: pong.name(),
387                dest: ping.name(),
388            },
389        ]
390    }
391
392    fn dest(&self) -> Option<GraphicsRef> {
393        Some(self.pong.clone())
394    }
395}
396
397pub struct TextureViewRender {
398    pub source: GraphicsRef,
399    pub dest: wgpu::TextureView,
400}
401
402impl TextureViewRender {
403    pub fn new_box(source: GraphicsRef, dest: wgpu::TextureView) -> Box<TextureViewRender> {
404        Box::new(TextureViewRender { source, dest })
405    }
406}
407
408impl RenderTrait for TextureViewRender {
409    fn render(&self, device: &DeviceStateForRender) {
410        let source = &self.source;
411        source.render_to_texture(device.device_state(), &self.dest);
412    }
413    fn debug_print(&self) -> Vec<RenderDebugPrint> {
414        let source = &self.source;
415        vec![RenderDebugPrint {
416            src: source.name(),
417            dest: "texture view!".to_string(),
418        }]
419    }
420
421    fn dest(&self) -> Option<GraphicsRef> {
422        None
423    }
424}
425
426pub struct DisplayRender {
427    pub source: GraphicsRef,
428}
429
430impl DisplayRender {
431    pub fn new_box(source: GraphicsRef) -> Box<DisplayRender> {
432        Box::new(DisplayRender { source })
433    }
434}
435
436impl RenderTrait for DisplayRender {
437    fn render(&self, device: &DeviceStateForRender) {
438        let source = &self.source;
439        source.render_to_texture(device.device_state(), device.display_view());
440    }
441    fn debug_print(&self) -> Vec<RenderDebugPrint> {
442        let source = &self.source;
443        vec![RenderDebugPrint {
444            src: source.name(),
445            dest: "output!".to_string(),
446        }]
447    }
448
449    fn dest(&self) -> Option<GraphicsRef> {
450        None
451    }
452}
453
454pub struct GPUPipeline<GraphicConf> {
455    pub dag: Vec<Box<dyn RenderTrait>>,
456    choices: Vec<usize>,
457    names: HashMap<String, GraphicsRef>, // todo, do i need this with ctrl?
458    ctrl: Vec<GraphicsRefWithControlFn<GraphicConf>>,
459    source: Option<String>,
460}
461
462impl<GraphicConf> GPUPipeline<GraphicConf> {
463    pub fn new() -> GPUPipeline<GraphicConf> {
464        GPUPipeline {
465            dag: Vec::new(),
466            choices: Vec::new(),
467            names: HashMap::new(),
468            ctrl: Vec::new(),
469            source: None,
470        }
471    }
472
473    pub fn add_control_graphics(
474        &mut self,
475        _label: &str,
476        control_graphics_fn: GraphicsRefWithControlFn<GraphicConf>,
477    ) {
478        self.ctrl.push(control_graphics_fn)
479    }
480
481    pub fn control_graphics(&self, t: &GraphicConf) -> Vec<ControlGraphicsRef> {
482        let mut v = vec![];
483        for c in &self.ctrl {
484            v.extend(c.control_graphics(t).into_iter());
485        }
486        v
487    }
488
489    pub fn set_source(&mut self, src: &str) {
490        self.source = Some(src.to_string());
491    }
492
493    pub fn add_step(&mut self, d: Box<dyn RenderTrait>) {
494        let curr_idx = self.dag.len();
495
496        {
497            // handle the special case of choices, where we should register it
498            if d.is_choice() {
499                self.choices.push(curr_idx);
500            }
501        }
502
503        self.dag.push(d);
504    }
505
506    pub fn add_label(&mut self, name: &str, g: GraphicsRef) {
507        self.names.insert(name.to_string(), g);
508    }
509
510    pub fn get_graphic(&self, name: &str) -> Option<GraphicsRef> {
511        self.names.get(name).cloned()
512    }
513
514    // no-op if it doesn't exist
515    pub fn adjust_choice(&mut self, choice_idx: usize, choice_val: usize) {
516        // use the choice idx to find the right one.
517        if choice_idx < self.choices.len() {
518            self.dag[self.choices[choice_idx]].adjust_choice(choice_val);
519        } else {
520            println!("what, that choice {:?} doesn't exist", choice_idx);
521        }
522    }
523
524    pub fn render(&self, device: &DeviceStateForRender) {
525        self.dag.iter().for_each(|x| x.render(device))
526    }
527
528    pub fn debug_print(&self) -> Vec<RenderDebugPrint> {
529        self.dag.iter().flat_map(|x| x.debug_print()).collect()
530    }
531
532    fn source(&self) -> GraphicsRef {
533        // hm this should happen on start
534        let name = self
535            .source
536            .as_ref()
537            .expect("should have set a source if you're gonna get it source");
538        self.get_graphic(&name)
539            .expect(&format!("gave a source {} that doesn't exist", name))
540    }
541}
542
543impl<GraphicsConf> Default for GPUPipeline<GraphicsConf> {
544    fn default() -> Self {
545        Self::new()
546    }
547}
548
549#[derive(Clone)]
550pub struct GPUPipelineRef<GraphicsConf>(Rc<RefCell<GPUPipeline<GraphicsConf>>>);
551
552impl<GraphicsConf> GPUPipelineRef<GraphicsConf> {
553    pub fn new(pipeline: GPUPipeline<GraphicsConf>) -> Self {
554        GPUPipelineRef(Rc::new(RefCell::new(pipeline)))
555    }
556
557    pub fn render(&self, device: &DeviceStateForRender) {
558        self.0.borrow().render(device)
559    }
560
561    pub fn debug_print(&self) -> Vec<RenderDebugPrint> {
562        self.0.borrow().debug_print()
563    }
564
565    pub fn source(&self) -> GraphicsRef {
566        self.0.borrow().source()
567    }
568
569    pub fn get_graphic(&self, name: &str) -> Option<GraphicsRef> {
570        self.0.borrow().get_graphic(name)
571    }
572
573    pub fn control_graphics(&self, conf: &GraphicsConf) -> Vec<ControlGraphicsRef> {
574        self.0.borrow().control_graphics(conf)
575    }
576}
577
578pub struct SingleTextureRender {
579    pub source: ImageTextureRef,
580    pub dest: GraphicsRef,
581}
582
583impl SingleTextureRender {
584    pub fn new_box(source: ImageTextureRef, dest: GraphicsRef) -> Box<SingleTextureRender> {
585        Box::new(SingleTextureRender { source, dest })
586    }
587}
588
589impl RenderTrait for SingleTextureRender {
590    // whenver it's called, it'll increment! check if it's overdue before rendering!
591    fn render(&self, device_state_for_render: &DeviceStateForRender) {
592        let source_texture = &self.source;
593        let dest = &self.dest;
594        source_texture.render(device_state_for_render, dest);
595    }
596
597    fn debug_print(&self) -> Vec<RenderDebugPrint> {
598        let source = &self.source;
599        let dest = &self.dest;
600        vec![RenderDebugPrint {
601            src: source.name(),
602            dest: dest.name(),
603        }]
604    }
605
606    fn dest(&self) -> Option<GraphicsRef> {
607        Some(self.dest.clone())
608    }
609}
610
611// makes it easier to ad control grpahics
612#[macro_export]
613macro_rules! with_control_graphics {
614    ($name:ident = $instance:expr, |$param:ident: $ttype:ident| $body:expr) => {
615        let $name = $instance.with_control_graphics(
616            stringify!($name),
617            Arc::new(|$param: &$ttype| Box::new($body) as Box<dyn ControlGraphics>),
618        );
619    };
620}
621
622// this is basically ran for every node, so can add label and such
623#[macro_export]
624macro_rules! pipeline_add_label {
625    ($pipeline:ident, $val:ident) => {{
626        $pipeline.add_label(stringify!($val), $val.graphics());
627        if let Some(ctrl) = $val.control_graphics_fn() {
628            $pipeline.add_control_graphics(stringify!($val), ctrl);
629        }
630    }};
631}
632
633#[macro_export]
634macro_rules! build_shader_pipeline {
635    () => {}; // empty
636    (@parse $pipeline:ident ()) => {}; // done!
637
638    // write to display: a -> DISPLAY, this is the view that will be passed in the pipeline render call
639    (@parse $pipeline:ident ($source:ident -> DISPLAY;$($tail:tt)*)) => {
640        {
641            println!("add display");
642            $pipeline.add_step(
643                DisplayRender::new_box(
644                    $source.graphics(),
645                )
646            );
647            pipeline_add_label!($pipeline, $source);
648
649            build_shader_pipeline!(@parse $pipeline ($($tail)*));
650        }
651    };
652
653    // process texture render: *a -> t
654    (@parse $pipeline:ident (*$source:ident -> $dest:ident;$($tail:tt)*)) => {
655        {
656            println!("add display");
657            $pipeline.add_step(
658                TextureRender::new_box(
659                    $source.graphics(),
660                    $dest.graphics(),
661                )
662            );
663            // pipeline_add_label!($pipeline, $source);
664            pipeline_add_label!($pipeline, $dest);
665
666            build_shader_pipeline!(@parse $pipeline ($($tail)*));
667        }
668    };
669
670    // process single texture: +a -> t
671    (@parse $pipeline:ident (+$source:ident -> $dest:ident;$($tail:tt)*)) => {
672        {
673            println!("add display");
674            $pipeline.add_step(
675                SingleTextureRender::new_box(
676                    $source.clone(),
677                    $dest.graphics(),
678                )
679            );
680            pipeline_add_label!($pipeline, $dest);
681
682            build_shader_pipeline!(@parse $pipeline ($($tail)*));
683        }
684    };
685
686    // process ping pong render: (a <-> b) -> t
687    (@parse $pipeline:ident (($ping:ident <-> $pong:ident) * $count:expr;$($tail:tt)*)) => {
688        {
689            println!("add ping pong");
690            $pipeline.add_step(
691                PingPongRender::new_box(
692                    $count,
693                    $ping.graphics(),
694                    $pong.graphics())
695            );
696            pipeline_add_label!($pipeline, $ping);
697            pipeline_add_label!($pipeline, $pong);
698
699            build_shader_pipeline!(@parse $pipeline ($($tail)*));
700        }
701    };
702
703    // process choice render: [a | b | c] -> t
704    (@parse $pipeline:ident ([$source:ident$( | $source_rest:ident)*] -> $dest:ident;$($tail:tt)*)) => {
705        {
706            println!("add choice render $dest");
707            $pipeline.add_step(
708                ChoiceRender::new_box(
709                    // todo, allow for more than two
710                    vec![
711                        $source.graphics(),
712                        $($source_rest.graphics(), )*
713                    ],
714                    $dest.graphics()
715                )
716            );
717            pipeline_add_label!($pipeline, $source);
718            $(pipeline_add_label!($pipeline, $source_rest);)*
719            pipeline_add_label!($pipeline, $dest);
720
721            build_shader_pipeline!(@parse $pipeline ($($tail)*));
722        }
723    };
724
725    // two sources to output: (a, b) -> t
726    (@parse $pipeline:ident (($source1:ident, $source2:ident) -> $dest:ident;$($tail:tt)*)) => {
727        {
728            println!("add two sources");
729
730            $pipeline.add_step(
731                TwoSourcesRender::new_box(
732                    $source1.graphics(),
733                    $source2.graphics(),
734                    $dest.graphics())
735            );
736            pipeline_add_label!($pipeline, $source1);
737            pipeline_add_label!($pipeline, $source2);
738            pipeline_add_label!($pipeline, $dest);
739
740            build_shader_pipeline!(@parse $pipeline ($($tail)*));
741        }
742    };
743
744    // one source to create one graphicsref: a -> T => t;
745    (@parse $pipeline:ident ($source:ident -> $subpipe:ident => $dest:ident;$($tail:tt)*)) => {
746        {
747            println!("add pipeline");
748            let $dest = $subpipe.out().clone();
749            $pipeline.add_step(
750                PipelineRender::new_box(
751                    $source.graphics(),
752                    $subpipe.gpu_pipeline(),
753                    $dest.graphics()
754                )
755            );
756            pipeline_add_label!($pipeline, $source);
757            pipeline_add_label!($pipeline, $dest);
758
759            build_shader_pipeline!(@parse $pipeline ($($tail)*));
760        }
761    };
762
763    // one source to output: a -> t
764    (@parse $pipeline:ident ($source:ident -> $dest:ident;$($tail:tt)*)) => {
765        {
766            println!("add simple");
767            $pipeline.add_step(
768                SimpleRender::new_box(
769                    $source.graphics(),
770                    $dest.graphics()
771                )
772            );
773            pipeline_add_label!($pipeline, $source);
774            pipeline_add_label!($pipeline, $dest);
775
776            build_shader_pipeline!(@parse $pipeline ($($tail)*));
777        }
778    };
779
780    // arm for funky parsing
781    (@parse $($raw:tt)*) => {
782        {
783            println!("???");
784            unreachable!();
785        }
786    };
787
788    // capture the initial one and prefix it with @parse
789    ($($raw:tt)*) => {
790        {
791            println!("new pipeline!");
792            let mut pipeline = GPUPipeline::new();  // create our new pipeline
793            build_shader_pipeline!(@parse pipeline ($($raw)*));
794            pipeline
795        }
796    };
797}
798
799#[derive(Clone)]
800pub struct ImageTextureRef(Rc<RefCell<ImageTexture>>);
801impl ImageTextureRef {
802    pub fn render(&self, device_state_for_render: &DeviceStateForRender, dest: &GraphicsRef) {
803        self.0.borrow().render(device_state_for_render, dest)
804    }
805    pub fn name(&self) -> String {
806        self.0.borrow().graphics.name()
807    }
808
809    pub fn from_image_texture(im: ImageTexture) -> Self {
810        Self(Rc::new(RefCell::new(im)))
811    }
812}
813
814#[derive(Clone)]
815pub struct VideoTextureRef(Rc<RefCell<VideoTexture>>);
816pub struct VideoTexture {
817    name: String,
818    pub graphics: GraphicsRef,
819    pub binds: Vec<wgpu::BindGroup>, // path to pngs, probably keep it smapp
820    pub fps: u64,
821    last_time: Option<MurreletTime>,
822    curr_i: usize, // current index in src_paths, starts at 0
823}
824
825impl VideoTexture {
826    // todo, how to make this work on web?
827    fn _load_path(path: &PathBuf, boomerang: bool) -> Vec<PathBuf> {
828        println!("reading path {:?}", path);
829        let paths = fs::read_dir(path).unwrap();
830        let mut p = paths
831            .filter_map(|entry| {
832                let entry = entry.unwrap();
833                let path = entry.path();
834                let metadata = fs::metadata(&path).unwrap();
835
836                if metadata.is_file()
837                    && path
838                        .extension()
839                        .and_then(|x| x.to_str())
840                        .map(|y| y == "png")
841                        .unwrap_or(false)
842                {
843                    println!("{:?}", entry);
844                    Some(path)
845                } else {
846                    None
847                }
848            })
849            .collect::<Vec<PathBuf>>();
850        p.sort_by(|a, b| a.file_name().cmp(&b.file_name()));
851
852        if boomerang {
853            // play back and forth, but don't repeat frames
854            let mut p2 = p.clone();
855            p2.reverse();
856            p2.pop();
857            p2.remove(0);
858
859            p.append(&mut p2);
860        }
861        p
862    }
863
864    pub fn overdue_for_an_update(&self) -> bool {
865        self.last_time
866            // .map(|x| right_now() - x >= (1000 / self.fps as u128))
867            .map(|x| (MurreletTime::now() - x).as_millis_u128() >= (1000 / self.fps as u128))
868            .unwrap_or(true)
869    }
870
871    pub fn next_frame(&mut self) {
872        self.curr_i = (self.curr_i + 1) % self.binds.len();
873        self.last_time = Some(MurreletTime::now());
874    }
875
876    pub fn render(
877        &self,
878        device_state_for_render: &DeviceStateForRender,
879        output_texture_view: &wgpu::TextureView,
880    ) {
881        let bind_group = &self.binds[self.curr_i];
882        self.graphics.render_with_custom_bind_group(
883            device_state_for_render.device_state(),
884            output_texture_view,
885            bind_group,
886        )
887    }
888
889    pub fn new_mut(
890        c: &GraphicsWindowConf,
891        name: &str,
892        path: &[&str],
893        fps: u64,
894        boomerang: bool,
895    ) -> VideoTextureRef {
896        VideoTextureRef(Rc::new(RefCell::new(VideoTexture::new(
897            c, name, path, fps, boomerang,
898        ))))
899    }
900
901    pub fn new(
902        c: &GraphicsWindowConf,
903        name: &str,
904        raw_path: &[&str],
905        fps: u64,
906        boomerang: bool,
907    ) -> VideoTexture {
908        let device = c.device();
909
910        let assets_path = c.assets_path.force_path_buf();
911
912        let mut path = assets_path;
913        for loc in raw_path {
914            path = path.join(loc);
915        }
916
917        let src_paths = VideoTexture::_load_path(&path, boomerang);
918        assert!(src_paths.len() < 61); // i don't know, i'm just scared
919
920        // load one as dummy to get image
921        // let source_dims = wgpu::Texture::from_path(c.window, &src_paths[0]).unwrap().size();
922        let source_dims = c.dims; // ??
923
924        // let target_dims =  _dims_from_window(c);
925        let target_dims = c.dims;
926
927        let gradient_shader: String = build_shader! {
928            (
929                raw r###"
930                let multiplier = uniforms.more_info.x;
931                let source = uniforms.more_info_other.xy;
932                let targ = uniforms.more_info_other.za;
933
934                let multi = targ / source / multiplier;
935
936                let result: vec4<f32> = textureSample(tex, tex_sampler, tex_coords * multi);
937                "###;
938            )
939        };
940
941        let _uniforms = BasicUniform::from_dims(c.dims);
942
943        let conf = GraphicsCreator::default()
944            .with_first_texture_format(DEFAULT_TEXTURE_FORMAT)
945            .with_dst_format(DEFAULT_TEXTURE_FORMAT)
946            .with_mag_filter(wgpu::FilterMode::Linear)
947            .with_address_mode(wgpu::AddressMode::Repeat);
948
949        let graphics = GraphicsRef::new(name, c, &gradient_shader, &conf);
950        graphics.update_uniforms_other(
951            c,
952            [1.0, 0.0, 0.0, 0.0],
953            [
954                source_dims[0] as f32,
955                source_dims[1] as f32,
956                target_dims[0] as f32,
957                target_dims[1] as f32,
958            ],
959        );
960
961        // todo, move this into GraphicsAssets
962        let binds = src_paths
963            .iter()
964            .map(|path| {
965                // let texture = wgpu::Texture::from_path(c.window, path).unwrap(); // load the path
966                let texture_and_desc =
967                    Graphics::texture(source_dims, c.device(), DEFAULT_LOADED_TEXTURE_FORMAT);
968                GraphicsAssets::LocalFilesystem(path.to_path_buf())
969                    .maybe_load_texture(c.device, &texture_and_desc.texture);
970                let texture_view =
971                    texture_and_desc
972                        .texture
973                        .create_view(&wgpu::TextureViewDescriptor {
974                            ..Default::default()
975                        });
976                println!("texture {:?}", texture_view);
977                graphics
978                    .graphics
979                    .borrow()
980                    .make_new_custom_bind_group(device, &texture_view)
981            })
982            .collect();
983
984        Self {
985            name: name.to_string(),
986            graphics,
987            binds,
988            fps,
989            last_time: None,
990            curr_i: 0,
991        }
992    }
993}
994
995pub struct ImageTexture {
996    name: String,
997    pub graphics: GraphicsRef,
998}
999
1000impl ImageTexture {
1001    pub fn render(&self, device_state_for_render: &DeviceStateForRender, other: &GraphicsRef) {
1002        self.graphics
1003            .render(device_state_for_render.device_state(), other);
1004    }
1005
1006    pub fn new_mut(
1007        name: &str,
1008        path: &PathBuf,
1009        c: &GraphicsWindowConf,
1010        address_mode: wgpu::AddressMode,
1011    ) -> ImageTextureRef {
1012        ImageTexture::new_mut_with_dims(name, path, c, address_mode)
1013    }
1014
1015    pub fn new_mut_with_dims(
1016        name: &str,
1017        path: &PathBuf,
1018        c: &GraphicsWindowConf,
1019        address_mode: wgpu::AddressMode,
1020    ) -> ImageTextureRef {
1021        ImageTextureRef(Rc::new(RefCell::new(ImageTexture::new(
1022            name,
1023            path,
1024            c,
1025            address_mode,
1026        ))))
1027    }
1028
1029    pub fn new(
1030        name: &str,
1031        src_path: &PathBuf,
1032        c: &GraphicsWindowConf,
1033        address_mode: wgpu::AddressMode,
1034    ) -> Self {
1035        // load one as dummy to get image
1036        // let source_dims = wgpu::Texture::from_path(c.window, src_path).unwrap().size();
1037
1038        // hrm, when this was set to width/height, it didn't work, it shrunk the whole thing..
1039        // let (_, width, height) = crate::device_state::check_img_size(src_path).unwrap();
1040        let source_dims = c.dims; //[width, height]; // c.dims; // ??
1041        let target_dims = c.dims;
1042        println!("source: {:?} {:?}", source_dims, target_dims);
1043
1044        let repeat_img: String = build_shader! {
1045            (
1046                raw r###"
1047                // the sizes of the input and output maps are in pixels
1048                let entire_source_size_pxl = uniforms.more_info_other.xy;
1049                let target_size_pxl = uniforms.more_info_other.zw;
1050
1051                // let aspect = vec2<f32>(uniforms.dims.x / uniforms.dims.y);
1052
1053                let source_normalized_dims = vec2<f32>(1.0 / entire_source_size_pxl.x, 1.0 / entire_source_size_pxl.y); //uniforms.dims.zw;
1054
1055                // grab the intended size of the source window and offset.
1056                let windowed_source_size_pxl = uniforms.more_info.zw;
1057                let windowed_source_offset_pxl = uniforms.more_info.xy;
1058
1059                let windowed_source_offset_txl = windowed_source_offset_pxl * source_normalized_dims;
1060
1061                // how much of the source image should we sample?
1062                let window_to_entire_ratio = windowed_source_size_pxl / entire_source_size_pxl;
1063
1064                // how many times should we repeat the sampled image?
1065                let window_to_entire_multi = target_size_pxl / windowed_source_size_pxl;
1066
1067                // okay here we go
1068                // start with figuring out where in the square we should sample for
1069                let target_coords_txl1 = fract(tex_coords * window_to_entire_multi);
1070                // now figure out where in the square we should sample, this will just zoom in
1071                let target_coords_txl = target_coords_txl1 * window_to_entire_ratio + windowed_source_offset_txl;
1072
1073                let result: vec4<f32> = textureSample(tex, tex_sampler, target_coords_txl);
1074                "###;
1075            )
1076        };
1077
1078        // let _uniforms = BasicUniform::from_dims(target_dims);
1079
1080        let conf = GraphicsCreator::default()
1081            .with_first_texture_format(DEFAULT_LOADED_TEXTURE_FORMAT)
1082            .with_dst_format(DEFAULT_TEXTURE_FORMAT)
1083            .with_mag_filter(wgpu::FilterMode::Nearest)
1084            .with_address_mode(address_mode);
1085
1086        let graphics = GraphicsRef::new_with_src(
1087            name,
1088            c, // gets dims from here
1089            &repeat_img,
1090            &conf,
1091            GraphicsAssets::LocalFilesystem(src_path.to_path_buf()),
1092        );
1093        graphics.update_uniforms_other(
1094            c,
1095            [0.0, 0.0, 0.0, 0.0],
1096            [
1097                source_dims[0] as f32,
1098                source_dims[1] as f32,
1099                target_dims[0] as f32,
1100                target_dims[1] as f32,
1101            ],
1102        );
1103
1104        Self {
1105            name: name.to_owned(),
1106            graphics,
1107        }
1108    }
1109
1110    // for colormaps!
1111    pub fn new_nearest(name: &str, src_path: &PathBuf, c: &GraphicsWindowConf) -> Self {
1112        // let _source_dims = wgpu::Texture::from_path(c.window, src_path).unwrap().size();
1113        // let target_dims = c.dims;
1114        let repeat_img: String = build_shader! {
1115            (
1116                raw r###"
1117
1118
1119                let result: vec4<f32> = textureSample(tex, tex_sampler, tex_coords);
1120                "###;
1121            )
1122        };
1123
1124        // let uniforms = BasicUniform::from_dims(target_dims);
1125        let conf = GraphicsCreator::default()
1126            .with_mag_filter(wgpu::FilterMode::Nearest)
1127            .with_address_mode(wgpu::AddressMode::ClampToEdge);
1128
1129        let graphics = GraphicsRef::new_with_src(
1130            name,
1131            c,
1132            &repeat_img,
1133            &conf,
1134            GraphicsAssets::LocalFilesystem(src_path.to_path_buf()),
1135        );
1136        Self {
1137            name: name.to_owned(),
1138            graphics,
1139        }
1140    }
1141
1142    pub fn update_uniforms(&self, c: &GraphicsWindowConf, offset: Vec2, size: Vec2) {
1143        self.graphics
1144            .update_uniforms(c, [offset.x, offset.y, size.x, size.y]);
1145    }
1146}