librashader_test/render/gl/
mod.rs

1mod context;
2
3use crate::render::gl::context::{GLVersion, GlfwContext};
4use crate::render::{CommonFrameOptions, RenderTest};
5use anyhow::anyhow;
6use glow::{HasContext, PixelPackData, PixelUnpackData};
7use image::RgbaImage;
8use librashader::presets::ShaderPreset;
9use librashader::runtime::gl::{FilterChain, FilterChainOptions, FrameOptions, GLImage};
10use librashader::runtime::{FilterChainParameters, RuntimeParameters};
11use librashader::runtime::{Size, Viewport};
12use librashader_runtime::image::{Image, UVDirection, RGBA8};
13use std::path::Path;
14use std::sync::Arc;
15
16struct OpenGl {
17    context: GlfwContext,
18    texture: GLImage,
19    image_bytes: Image<RGBA8>,
20}
21
22pub struct OpenGl3(OpenGl);
23pub struct OpenGl4(OpenGl);
24
25impl RenderTest for OpenGl3 {
26    fn new(path: &Path) -> anyhow::Result<Self>
27    where
28        Self: Sized,
29    {
30        OpenGl3::new(path)
31    }
32
33    fn image_size(&self) -> Size<u32> {
34        self.0.image_bytes.size
35    }
36
37    fn render_with_preset_and_params(
38        &mut self,
39        preset: ShaderPreset,
40        frame_count: usize,
41        output_size: Option<Size<u32>>,
42        param_setter: Option<&dyn Fn(&RuntimeParameters)>,
43        frame_options: Option<CommonFrameOptions>,
44    ) -> anyhow::Result<image::RgbaImage> {
45        let mut filter_chain = unsafe {
46            FilterChain::load_from_preset(
47                preset,
48                Arc::clone(&self.0.context.gl),
49                Some(&FilterChainOptions {
50                    glsl_version: 330,
51                    use_dsa: false,
52                    force_no_mipmaps: false,
53                    disable_cache: false,
54                }),
55            )
56        }?;
57
58        if let Some(setter) = param_setter {
59            setter(filter_chain.parameters());
60        }
61
62        Ok(self.0.render(
63            &mut filter_chain,
64            frame_count,
65            output_size,
66            frame_options
67                .map(|options| FrameOptions {
68                    clear_history: options.clear_history,
69                    frame_direction: options.frame_direction,
70                    rotation: options.rotation,
71                    total_subframes: options.total_subframes,
72                    current_subframe: options.current_subframe,
73                    aspect_ratio: options.aspect_ratio,
74                    frametime_delta: options.frametime_delta,
75                    frames_per_second: options.frames_per_second,
76                })
77                .as_ref(),
78        )?)
79    }
80}
81
82impl RenderTest for OpenGl4 {
83    fn new(path: &Path) -> anyhow::Result<Self>
84    where
85        Self: Sized,
86    {
87        OpenGl4::new(path)
88    }
89
90    fn image_size(&self) -> Size<u32> {
91        self.0.image_bytes.size
92    }
93
94    fn render_with_preset_and_params(
95        &mut self,
96        preset: ShaderPreset,
97        frame_count: usize,
98        output_size: Option<Size<u32>>,
99        param_setter: Option<&dyn Fn(&RuntimeParameters)>,
100        frame_options: Option<CommonFrameOptions>,
101    ) -> anyhow::Result<image::RgbaImage> {
102        let mut filter_chain = unsafe {
103            FilterChain::load_from_preset(
104                preset,
105                Arc::clone(&self.0.context.gl),
106                Some(&FilterChainOptions {
107                    glsl_version: 460,
108                    use_dsa: true,
109                    force_no_mipmaps: false,
110                    disable_cache: true,
111                }),
112            )
113        }?;
114
115        if let Some(setter) = param_setter {
116            setter(filter_chain.parameters());
117        }
118
119        Ok(self.0.render(
120            &mut filter_chain,
121            frame_count,
122            output_size,
123            frame_options
124                .map(|options| FrameOptions {
125                    clear_history: options.clear_history,
126                    frame_direction: options.frame_direction,
127                    rotation: options.rotation,
128                    total_subframes: options.total_subframes,
129                    current_subframe: options.current_subframe,
130                    aspect_ratio: options.aspect_ratio,
131                    frametime_delta: options.frametime_delta,
132                    frames_per_second: options.frames_per_second,
133                })
134                .as_ref(),
135        )?)
136    }
137}
138
139impl OpenGl3 {
140    pub fn new(image_path: &Path) -> anyhow::Result<Self> {
141        Ok(Self(OpenGl::new(image_path, false)?))
142    }
143}
144
145impl OpenGl4 {
146    pub fn new(image_path: &Path) -> anyhow::Result<Self> {
147        Ok(Self(OpenGl::new(image_path, true)?))
148    }
149}
150
151impl OpenGl {
152    pub fn new(image_path: &Path, use_dsa: bool) -> anyhow::Result<Self> {
153        let image: Image<RGBA8> = Image::load(image_path, UVDirection::TopLeft)?;
154        let height = image.size.height;
155        let width = image.size.width;
156        let version = if use_dsa {
157            GLVersion(4, 6)
158        } else {
159            GLVersion(3, 3)
160        };
161
162        let context = GlfwContext::new(version, width, height)?;
163
164        let texture = unsafe {
165            let tex = context.gl.create_texture().map_err(|s| anyhow!("{}", s))?;
166            context.gl.bind_texture(glow::TEXTURE_2D, Some(tex));
167            context.gl.tex_storage_2d(
168                glow::TEXTURE_2D,
169                1,
170                glow::RGBA8,
171                image.size.width as i32,
172                image.size.height as i32,
173            );
174
175            context.gl.pixel_store_i32(glow::UNPACK_ROW_LENGTH, 0);
176            context.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 4);
177            context.gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, None);
178
179            context.gl.tex_sub_image_2d(
180                glow::TEXTURE_2D,
181                0,
182                0,
183                0,
184                image.size.width as i32,
185                image.size.height as i32,
186                glow::RGBA,
187                glow::UNSIGNED_BYTE,
188                PixelUnpackData::Slice(Some(&image.bytes)),
189            );
190
191            context.gl.bind_texture(glow::TEXTURE_2D, None);
192            tex
193        };
194
195        Ok(Self {
196            context,
197            texture: GLImage {
198                handle: Some(texture),
199                format: glow::RGBA8,
200                size: image.size,
201            },
202            image_bytes: image,
203        })
204    }
205
206    pub fn render(
207        &self,
208        chain: &mut FilterChain,
209        frame_count: usize,
210        output_size: Option<Size<u32>>,
211        options: Option<&FrameOptions>,
212    ) -> Result<RgbaImage, anyhow::Error> {
213        let output_size = output_size.unwrap_or(self.image_bytes.size);
214
215        let render_texture = unsafe {
216            let tex = self
217                .context
218                .gl
219                .create_texture()
220                .map_err(|s| anyhow!("{}", s))?;
221            self.context.gl.bind_texture(glow::TEXTURE_2D, Some(tex));
222            self.context.gl.tex_storage_2d(
223                glow::TEXTURE_2D,
224                1,
225                glow::RGBA8,
226                output_size.width as i32,
227                output_size.height as i32,
228            );
229            self.context.gl.bind_texture(glow::TEXTURE_2D, None);
230            tex
231        };
232
233        let output = GLImage {
234            handle: Some(render_texture),
235            format: glow::RGBA8,
236            size: output_size,
237        };
238
239        let viewport = Viewport::new_render_target_sized_origin(&output, None)?;
240        for frame in 0..=frame_count {
241            unsafe {
242                chain.frame(&self.texture, &viewport, frame, options)?;
243            }
244        }
245
246        let mut data = vec![0u8; output_size.width as usize * output_size.height as usize * 4];
247
248        unsafe {
249            self.context
250                .gl
251                .bind_texture(glow::TEXTURE_2D, output.handle);
252            self.context.gl.get_tex_image(
253                glow::TEXTURE_2D,
254                0,
255                glow::RGBA,
256                glow::UNSIGNED_BYTE,
257                PixelPackData::Slice(Some(&mut data)),
258            )
259        }
260        Ok(
261            RgbaImage::from_raw(output_size.width, output_size.height, data)
262                .ok_or(anyhow!("failed to create image from slice"))?,
263        )
264    }
265}