hadronized / luminance-rs

Type-safe, type-level and stateless Rust graphics framework
https://phaazon.github.io/learn-luminance/
Other
1.09k stars 59 forks source link

Segmentation fault when dropping shader program struct #605

Closed kwobny closed 1 year ago

kwobny commented 2 years ago

Hi, I encountered a segmentation fault while working on a personal project. The fault occurs whenever I close the window that my program creates. I'm using Linux with X11 and OpenGL as my luminance backend. Here are the crates and versions I am using (retrieved from Cargo.lock): luminance: 0.47.0 luminance-front: 0.6.1 luminance-gl: 0.19.1 luminance-glfw: 0.18.2 luminance-derive: 0.10.0 luminance-std140: 0.2.1 glfw: 0.44.0 gl: 0.14.0

Here is the backtrace I got from GDB.

#0  0x00007ffff758ece0 in ?? ()
#1  0x00005555555a2220 in gl::DeleteProgram (program=3) at /home/kwobny/Documents/software/rust/boids/target/debug/build/gl-28063578b00c550f/out/bindings.rs:1763
#2  0x000055555559a33c in luminance_gl::gl33::shader::{impl#1}::drop (self=0x7fffffffe410)
    at /home/kwobny/.cargo/registry/src/github.com-1ecc6299db9ec823/luminance-gl-0.19.1/src/gl33/shader.rs:44
#3  0x000055555559993b in core::ptr::drop_in_place<luminance_gl::gl33::shader::Program> () at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/ptr/mod.rs:448
#4  0x000055555557cb4b in core::ptr::drop_in_place<luminance::shader::Program<luminance_gl::gl33::GL33, boids::graphics_backend::VertexSemantics, (), ()>> ()
    at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/ptr/mod.rs:448

The backtrace suggests that the fault occurs when a shader program struct is dropped.

Upon further examination, it seems the bug occurs when the shader program struct is dropped in a scope that it is not declared in. Take this code for example:

use luminance_glfw::{GlfwSurface, GlfwSurfaceError};
use glfw::{WindowMode, InitError};
use luminance_front::context::GraphicsContext;
use luminance_derive::Semantics;

#[derive(Copy, Clone, Debug, Semantics)]
pub enum VertexSemantics {
    #[sem(name = "position", repr = "[f32; 2]", wrapper = "VertexPosition")]
    Position,
    #[sem(name = "color", repr = "[u8; 3]", wrapper = "VertexRGB")]
    Color,
}

const VS_STR: &str = include_str!("vs.glsl");
const FS_STR: &str = include_str!("fs.glsl");

fn main() {
    let shader_program = {
        let surface_result: Result<GlfwSurface, GlfwSurfaceError<()>> = GlfwSurface::new(|glfw| {
            glfw.create_window(300, 300, "hello world", WindowMode::Windowed)
                .ok_or(GlfwSurfaceError::InitError(InitError::Internal))
        });
        let mut surface = surface_result.unwrap();

        let shader_program = surface.context
            .new_shader_program::<VertexSemantics, (), ()>()
            .from_strings(VS_STR, None, None, FS_STR)
            .unwrap()
            .ignore_warnings();

        shader_program
    };
}

The above code results in a seg fault. But the same code without the extra scope does not:

fn main() {
    // let shader_program = {
        let surface_result: Result<GlfwSurface, GlfwSurfaceError<()>> = GlfwSurface::new(|glfw| {
            glfw.create_window(300, 300, "hello world", WindowMode::Windowed)
                .ok_or(GlfwSurfaceError::InitError(InitError::Internal))
        });
        let mut surface = surface_result.unwrap();

        let shader_program = surface.context
            .new_shader_program::<VertexSemantics, (), ()>()
            .from_strings(VS_STR, None, None, FS_STR)
            .unwrap()
            .ignore_warnings();

    //     shader_program
    // };
}

A seg fault also occurs if I place the code in another function and call it from main:

use luminance_front::shader::Program;

fn get_shader_program() -> Program<VertexSemantics, (), ()> {
    let surface_result: Result<GlfwSurface, GlfwSurfaceError<()>> = GlfwSurface::new(|glfw| {
        glfw.create_window(300, 300, "hello world", WindowMode::Windowed)
            .ok_or(GlfwSurfaceError::InitError(InitError::Internal))
    });
    let mut surface = surface_result.unwrap();

    let shader_program = surface.context
        .new_shader_program::<VertexSemantics, (), ()>()
        .from_strings(VS_STR, None, None, FS_STR)
        .unwrap()
        .ignore_warnings();

    shader_program
}

fn main() {
    let shader_program = get_shader_program();
}

The examples above are partially derived from the Learn Luminance book: https://phaazon.github.io/learn-luminance/chapter_2_3.html The files vs.glsl and fs.glsl are from the book as well, but I have also put them below. vs.glsl:

// those are our vertex attributes
in vec2 position;
in vec3 color;

// this is the output of the vertex shader (we could have had several ones)
out vec3 v_color;

void main() {
  // simply forward the color
  v_color = color;

  // mandatory; tell the GPU to use the position vertex attribute to put the vertex in space
  gl_Position = vec4(position, 0., 1.);
}

fs.glsl:

// this was the vertex shader output; it’s now our (rasterized and interpolated) input!
in vec3 v_color;

// we will output a single color
out vec4 frag_color;

void main() {
  // KISS
  frag_color = vec4(v_color, 1.0);
}
kwobny commented 2 years ago

After further experimentation, I think this bug happens when luminance_glfw::GlfwSurface is dropped before luminance::shader::Program is dropped.

This code results in a seg fault.

let program;
let mut surface;

let surface_result: Result<GlfwSurface, GlfwSurfaceError<()>> = GlfwSurface::new(|glfw| {
glfw.create_window(300, 300, "hello world", WindowMode::Windowed)
        .ok_or(GlfwSurfaceError::InitError(InitError::Internal))
});
surface = surface_result.unwrap();

program = Box::new(
    surface.context
        .new_shader_program::<VertexSemantics, (), ()>()
        .from_strings(VS_STR, None, None, FS_STR)
        .unwrap()
        .ignore_warnings()
);

But the same code with the top two variable declarations swapped does not fault.

// surface and program variable declarations swapped
let mut surface;
let program;

let surface_result: Result<GlfwSurface, GlfwSurfaceError<()>> = GlfwSurface::new(|glfw| {
    glfw.create_window(300, 300, "hello world", WindowMode::Windowed)
        .ok_or(GlfwSurfaceError::InitError(InitError::Internal))
});
surface = surface_result.unwrap();

program = Box::new(
    surface.context
        .new_shader_program::<VertexSemantics, (), ()>()
        .from_strings(VS_STR, None, None, FS_STR)
        .unwrap()
        .ignore_warnings()
);
hadronized commented 1 year ago

Yes, it’s a drop issue, and is a well known problem. I’m probably going to prioritize that in the next release.

hadronized commented 1 year ago

Duplicate of #304.