librashader_test/render/gl/
mod.rsmod context;
use crate::render::gl::context::{GLVersion, GlfwContext};
use crate::render::{CommonFrameOptions, RenderTest};
use anyhow::anyhow;
use glow::{HasContext, PixelPackData, PixelUnpackData};
use image::RgbaImage;
use librashader::presets::ShaderPreset;
use librashader::runtime::gl::{FilterChain, FilterChainOptions, FrameOptions, GLImage};
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
use librashader::runtime::{Size, Viewport};
use librashader_runtime::image::{Image, UVDirection, RGBA8};
use std::path::Path;
use std::sync::Arc;
struct OpenGl {
context: GlfwContext,
texture: GLImage,
image_bytes: Image<RGBA8>,
}
pub struct OpenGl3(OpenGl);
pub struct OpenGl4(OpenGl);
impl RenderTest for OpenGl3 {
fn new(path: &Path) -> anyhow::Result<Self>
where
Self: Sized,
{
OpenGl3::new(path)
}
fn image_size(&self) -> Size<u32> {
self.0.image_bytes.size
}
fn render_with_preset_and_params(
&mut self,
preset: ShaderPreset,
frame_count: usize,
output_size: Option<Size<u32>>,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
frame_options: Option<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage> {
let mut filter_chain = unsafe {
FilterChain::load_from_preset(
preset,
Arc::clone(&self.0.context.gl),
Some(&FilterChainOptions {
glsl_version: 330,
use_dsa: false,
force_no_mipmaps: false,
disable_cache: false,
}),
)
}?;
if let Some(setter) = param_setter {
setter(filter_chain.parameters());
}
Ok(self.0.render(
&mut filter_chain,
frame_count,
output_size,
frame_options
.map(|options| FrameOptions {
clear_history: options.clear_history,
frame_direction: options.frame_direction,
rotation: options.rotation,
total_subframes: options.total_subframes,
current_subframe: options.current_subframe,
aspect_ratio: options.aspect_ratio,
frametime_delta: options.frametime_delta,
frames_per_second: options.frames_per_second,
})
.as_ref(),
)?)
}
}
impl RenderTest for OpenGl4 {
fn new(path: &Path) -> anyhow::Result<Self>
where
Self: Sized,
{
OpenGl4::new(path)
}
fn image_size(&self) -> Size<u32> {
self.0.image_bytes.size
}
fn render_with_preset_and_params(
&mut self,
preset: ShaderPreset,
frame_count: usize,
output_size: Option<Size<u32>>,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
frame_options: Option<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage> {
let mut filter_chain = unsafe {
FilterChain::load_from_preset(
preset,
Arc::clone(&self.0.context.gl),
Some(&FilterChainOptions {
glsl_version: 460,
use_dsa: true,
force_no_mipmaps: false,
disable_cache: true,
}),
)
}?;
if let Some(setter) = param_setter {
setter(filter_chain.parameters());
}
Ok(self.0.render(
&mut filter_chain,
frame_count,
output_size,
frame_options
.map(|options| FrameOptions {
clear_history: options.clear_history,
frame_direction: options.frame_direction,
rotation: options.rotation,
total_subframes: options.total_subframes,
current_subframe: options.current_subframe,
aspect_ratio: options.aspect_ratio,
frametime_delta: options.frametime_delta,
frames_per_second: options.frames_per_second,
})
.as_ref(),
)?)
}
}
impl OpenGl3 {
pub fn new(image_path: &Path) -> anyhow::Result<Self> {
Ok(Self(OpenGl::new(image_path, false)?))
}
}
impl OpenGl4 {
pub fn new(image_path: &Path) -> anyhow::Result<Self> {
Ok(Self(OpenGl::new(image_path, true)?))
}
}
impl OpenGl {
pub fn new(image_path: &Path, use_dsa: bool) -> anyhow::Result<Self> {
let image: Image<RGBA8> = Image::load(image_path, UVDirection::TopLeft)?;
let height = image.size.height;
let width = image.size.width;
let version = if use_dsa {
GLVersion(4, 6)
} else {
GLVersion(3, 3)
};
let context = GlfwContext::new(version, width, height)?;
let texture = unsafe {
let tex = context.gl.create_texture().map_err(|s| anyhow!("{}", s))?;
context.gl.bind_texture(glow::TEXTURE_2D, Some(tex));
context.gl.tex_storage_2d(
glow::TEXTURE_2D,
1,
glow::RGBA8,
image.size.width as i32,
image.size.height as i32,
);
context.gl.pixel_store_i32(glow::UNPACK_ROW_LENGTH, 0);
context.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 4);
context.gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, None);
context.gl.tex_sub_image_2d(
glow::TEXTURE_2D,
0,
0,
0,
image.size.width as i32,
image.size.height as i32,
glow::RGBA,
glow::UNSIGNED_BYTE,
PixelUnpackData::Slice(&image.bytes),
);
context.gl.bind_texture(glow::TEXTURE_2D, None);
tex
};
Ok(Self {
context,
texture: GLImage {
handle: Some(texture),
format: glow::RGBA8,
size: image.size,
},
image_bytes: image,
})
}
pub fn render(
&self,
chain: &mut FilterChain,
frame_count: usize,
output_size: Option<Size<u32>>,
options: Option<&FrameOptions>,
) -> Result<RgbaImage, anyhow::Error> {
let output_size = output_size.unwrap_or(self.image_bytes.size);
let render_texture = unsafe {
let tex = self
.context
.gl
.create_texture()
.map_err(|s| anyhow!("{}", s))?;
self.context.gl.bind_texture(glow::TEXTURE_2D, Some(tex));
self.context.gl.tex_storage_2d(
glow::TEXTURE_2D,
1,
glow::RGBA8,
output_size.width as i32,
output_size.height as i32,
);
self.context.gl.bind_texture(glow::TEXTURE_2D, None);
tex
};
let output = GLImage {
handle: Some(render_texture),
format: glow::RGBA8,
size: output_size,
};
let viewport = Viewport::new_render_target_sized_origin(&output, None)?;
for frame in 0..=frame_count {
unsafe {
chain.frame(&self.texture, &viewport, frame, options)?;
}
}
let mut data = vec![0u8; output_size.width as usize * output_size.height as usize * 4];
unsafe {
self.context
.gl
.bind_texture(glow::TEXTURE_2D, output.handle);
self.context.gl.get_tex_image(
glow::TEXTURE_2D,
0,
glow::RGBA,
glow::UNSIGNED_BYTE,
PixelPackData::Slice(&mut data),
)
}
Ok(
RgbaImage::from_raw(output_size.width, output_size.height, data)
.ok_or(anyhow!("failed to create image from slice"))?,
)
}
}