SnowflakePowered / librashader

RetroArch Shaders for All
Mozilla Public License 2.0
100 stars 11 forks source link

Infinity loop with bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp #89

Closed Azq2 closed 3 months ago

Azq2 commented 3 months ago

Shader: https://github.com/libretro/slang-shaders/blob/master/bezel/Mega_Bezel/Presets/Base_CRT_Presets/MBZ__0__SMOOTH-ADV-GLASS__LCD-GRID.slangp

Engine: OpenGL 4.2

Code

use librashader_runtime_gl::options::FilterChainOptionsGL;
use librashader_runtime_gl::{FilterChainGL, GLFramebuffer, GLImage};
use librashader_common::{Size, Viewport};
use librashader::presets::{Scale2D, ScaleFactor, ScaleType, Scaling};
use librashader::presets::ShaderPreset;
use librashader::presets::context::VideoDriver;
use gl_headless::gl_headless;
use image::GenericImageView;
use image::ImageBuffer;
use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint, GLvoid};
use std::ops::Mul;
use num_traits::AsPrimitive;

#[gl_headless(version = "3.3")]
unsafe fn main() {
    // Load input image
    let img = image::open("/tmp/lenna.png").expect("Failed to load image");
    let (width, height) = img.dimensions();
    let data = img.into_rgba8().into_raw();

    //  let shader_path = "../slang-shaders/edge-smoothing/scalefx/scalefx-9x.slangp";
    //  let shader_path = "../slang-shaders/edge-smoothing/xbr/super-xbr-fast.slangp";
    //  let shader_path = "../slang-shaders/edge-smoothing/xbrz/2xbrz-linear.slangp";
        let shader_path = "../slang-shaders/./bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp";
    //  let shader_path = "../slang-shaders/./presets/crt-royale-ntsc-svideo.slangp";
    //  let shader_path = "../slang-shaders/edge-smoothing/xbrz/2xbrz-linear.slangp";
    //  let shader_path = "../slang-shaders/./edge-smoothing/scalefx/scalefx-9x.slangp";

    let output_size = calc_output_size(shader_path, Size::new(width, height));

    println!("Shader: {}", shader_path);
    println!("Input size: {}x{}", width, height);
    println!("Output size: {}x{}", output_size.width, output_size.height);

    gl::Viewport(0, 0, width as _, height as _);

    let (_rendered_framebuffer, rendered_texture) = create_texture(width, height);
    let (output_framebuffer, output_texture) = create_texture(output_size.width, output_size.height);

    gl::BindTexture(gl::TEXTURE_2D, rendered_texture);
    gl::TexSubImage2D(gl::TEXTURE_2D, 0, 0, 0, width as GLsizei, height as GLsizei, gl::RGBA, gl::UNSIGNED_BYTE, data.as_ptr() as _);

    // Render shader
    let rendered = GLImage {
        handle: rendered_texture,
        format: gl::RGBA8,
        size: Size {
            width: width,
            height: height,
        },
    };

    let output = GLFramebuffer::new_from_raw(output_texture, output_framebuffer, gl::RGBA8, output_size, 1);

    let viewport = Viewport {
        x: 0f32,
        y: 0f32,
        output: &output,
        mvp: None,
    };

    gl::BindFramebuffer(gl::FRAMEBUFFER, 0);

    let mut filter = FilterChainGL::load_from_path(shader_path, None).unwrap();
    filter.frame(&rendered, &viewport, 0, None).unwrap();

    gl::BindFramebuffer(gl::FRAMEBUFFER, output_framebuffer);
    render_as_png("/tmp/test.png", output_size);
}

unsafe fn render_as_png(file: &str, size: Size<u32>) {
    let mut data = vec![0u8; (size.width * size.height * 4) as usize];
    gl::ReadPixels(
        0,
        0,
        size.width as _,
        size.height as _,
        gl::RGBA,
        gl::UNSIGNED_BYTE,
        data.as_mut_ptr() as _,
    );
    let buffer: ImageBuffer<image::Rgba<u8>, Vec<u8>> = ImageBuffer::from_raw(size.width, size.height, data).unwrap();
    buffer.save(file).expect("Image saving failed");
}

unsafe fn create_texture(width: u32, height: u32) -> (GLuint, GLuint) {
    let mut framebuffer = 0;
    let mut texture = 0;

    gl::GenFramebuffers(1, &mut framebuffer);
    gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer);

    gl::GenTextures(1, &mut texture);
    gl::BindTexture(gl::TEXTURE_2D, texture);

    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as _);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as _);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as _);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as _);

    gl::TexStorage2D(gl::TEXTURE_2D, 1, gl::RGBA8, width as _, height as _);
    gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, texture, 0);

    if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
        panic!("failed to create fbo")
    }

    return (framebuffer, texture);
}

unsafe fn calc_output_size(shader_path: &str, original_size: Size<u32>) -> Size<u32> {
    let preset = ShaderPreset::try_parse_with_driver_context(shader_path, VideoDriver::GlCore).unwrap();

    let mut iterator = preset.shaders.iter().enumerate().peekable();
    let mut target_size = original_size;

    println!("target_size={}x{}", target_size.width, target_size.height);
    while let Some((_index, shader_config)) = iterator.next() {
        let next_size = scale_pass::<u32>(
            shader_config.scaling.clone(),
            target_size,
            original_size,
            original_size,
        );
        target_size = next_size;
        println!("target_size={}x{}", target_size.width, target_size.height);
    }

    return target_size;
}

// From librashader-runtime/src/scaling.rs
fn scale_pass<T>(scaling: Scale2D, source: Size<T>, viewport: Size<T>, original: Size<T>) -> Size<T>
where
    T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
    f32: AsPrimitive<T>,
{
    const MAX_TEXEL_SIZE: f32 = 16384f32;

    let width = match scaling.x {
        Scaling {
            scale_type: ScaleType::Input,
            factor,
        } => source.width * factor,
        Scaling {
            scale_type: ScaleType::Absolute,
            factor,
        } => factor.into(),
        Scaling {
            scale_type: ScaleType::Viewport,
            factor,
        } => viewport.width * factor,
        Scaling {
            scale_type: ScaleType::Original,
            factor,
        } => original.width * factor,
    };

    let height = match scaling.y {
        Scaling {
            scale_type: ScaleType::Input,
            factor,
        } => source.height * factor,
        Scaling {
            scale_type: ScaleType::Absolute,
            factor,
        } => factor.into(),
        Scaling {
            scale_type: ScaleType::Viewport,
            factor,
        } => viewport.height * factor,
        Scaling {
            scale_type: ScaleType::Original,
            factor,
        } => original.height * factor,
    };

    Size {
        width: std::cmp::min(
            std::cmp::max(width.round().as_(), 1f32.as_()),
            MAX_TEXEL_SIZE.as_(),
        ),
        height: std::cmp::min(
            std::cmp::max(height.round().as_(), 1f32.as_()),
            MAX_TEXEL_SIZE.as_(),
        ),
    }
}
chyyran commented 3 months ago

Where is the infinite loop occurring exactly? I don’t understand what your code is doing.

Azq2 commented 3 months ago
  1. Infinity loop in frame()
  2. My code is processing static images with a given shader.
  3. Any other shaders work fine.
  4. The rest of the code and images of input are not important. I think this can be reproduced with hello_triangle (gl3), but I can't run this example because it requires wayland for unknown reasons.
chyyran commented 3 months ago

I can’t reproduce this. By infinite loop do you mean frame doesn’t return or you get a blank image? You don’t seem to be incrementing the frame counter, some shaders require that to play intros.

Azq2 commented 3 months ago

I can’t reproduce this. By infinite loop do you mean frame doesn’t return or you get a blank image? You don’t seem to be incrementing the frame counter, some shaders require that to play intros.

No, I mean exactly infinity loop. Code is never ending on filter.frame().

More minimal example: https://github.com/Azq2/librashader-bug

Azq2 commented 3 months ago

Hmm, this shader is just slow. Ok, my mistake, sorry :)

$ time cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.16s
     Running `target/debug/librashader-bug`
call frame...
frame is ended!

real    0m10.581s
user    0m13.920s
sys     0m0.850s