Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segfault in create_render_pipeline when vertex shader returns void #3550

Closed
WorkForPizza opened this issue Mar 2, 2023 · 1 comment · Fixed by gfx-rs/naga#2264
Closed

Comments

@WorkForPizza
Copy link

Description
Trying to create a pipeline with a wgsl shader whose vertex function returns void results in a SIGSEGV and no error message.

Repro steps
Here's a small reproduction program

use winit::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::WindowBuilder,
};
use pollster::FutureExt as _;

fn main() {
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new().build(&event_loop).unwrap();

    let size = window.inner_size();

    let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
        backends: wgpu::Backends::all(),
        dx12_shader_compiler: Default::default(),
    });

    let surface = unsafe { instance.create_surface(&window) }.unwrap();

    let adapter = instance.request_adapter(
        &wgpu::RequestAdapterOptions {
            power_preference: wgpu::PowerPreference::default(),
            compatible_surface: Some(&surface),
            force_fallback_adapter: false,
        },
    ).block_on().unwrap();

    let (device, _queue) = adapter.request_device(
        &wgpu::DeviceDescriptor {
            features: wgpu::Features::empty(),
            limits: wgpu::Limits::downlevel_webgl2_defaults(),
            label: None,
        },
        None,
    ).block_on().unwrap();

    let surface_caps = surface.get_capabilities(&adapter);
    let surface_format = surface_caps.formats.iter()
        .copied()
        .filter(|f| f.describe().srgb)
        .next()
        .unwrap_or(surface_caps.formats[0]);
    let config = wgpu::SurfaceConfiguration {
        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
        format: surface_format,
        width: size.width,
        height: size.height,
        present_mode: wgpu::PresentMode::Fifo,
        alpha_mode: wgpu::CompositeAlphaMode::Auto,
        view_formats: vec![],
    };
    surface.configure(&device, &config);

    let shader_src = r#"
    @vertex
    fn vs_main() {
        return;
    }

    @vertex
    fn vs_main2() -> @builtin(position) vec4<f32> {
        return vec4<f32>(0.0, 0.0, 0.0, 1.0);
    }

    @fragment
    fn fs_main() -> @location(0) vec4<f32> {
        return vec4<f32>(0.2, 0.2, 0.2, 1.0);
    }
"#;
    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
        label: Some("Shader"),
        source: wgpu::ShaderSource::Wgsl(shader_src.into()),
    });
    let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
        label: Some("Render Pipeline Layout"),
        bind_group_layouts: &[],
        push_constant_ranges: &[],
    });
    let _pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
        label: Some("Render Pipeline"),
        layout: Some(&render_pipeline_layout),
        vertex: wgpu::VertexState {
            module: &shader,
            entry_point: "vs_main",
            // entry_point: "vs_main2",
            buffers: &[],
        },
        fragment: Some(wgpu::FragmentState {
            module: &shader,
            entry_point: "fs_main",
            targets: &[Some(wgpu::ColorTargetState {
                format: config.format,
                blend: Some(wgpu::BlendState::REPLACE),
                write_mask: wgpu::ColorWrites::ALL,
            })],
        }),
        primitive: wgpu::PrimitiveState::default(),
        depth_stencil: None,
        multisample: wgpu::MultisampleState::default(),
        multiview: None,
    });

    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Wait;

        match event {
            Event::WindowEvent {
                window_id,
                event: WindowEvent::CloseRequested,
            } if window_id == window.id() => *control_flow = ControlFlow::Exit,
            _ => (),
        }
    });
}

Expected vs observed behavior
Running the above program with cargo run will result in a segfault. Using "vs_main2" as the vertex entrypoint in the create_render_pipeline call will fix the issue and a window will appear. I see now that a wgsl vertex shader is required to return a position (https://www.w3.org/TR/WGSL/#function-restriction), however I still would've expected an error message instead of a fault.

You can see my output, as well as the backtrace below.

martin@M ~/r/wgpu-test2 (master)> cargo run
   Compiling wgpu-test2 v0.1.0 (/Users/martin/rust/wgpu-test2)
    Finished dev [unoptimized + debuginfo] target(s) in 0.44s
     Running `target/debug/wgpu-test2`
fish: Job 1, 'cargo run' terminated by signal SIGSEGV (Address boundary error)
martin@M ~/r/wgpu-test2 (master) [SIGSEGV]> lldb ./target/debug/wgpu-test2
(lldb) target create "./target/debug/wgpu-test2"
Current executable set to '/Users/martin/rust/wgpu-test2/target/debug/wgpu-test2' (arm64).
(lldb) r
Process 38529 launched: '/Users/martin/rust/wgpu-test2/target/debug/wgpu-test2' (arm64)
2023-03-01 22:25:57.869047-0600 wgpu-test2[38529:6359801] Compiler failed to build request
Process 38529 stopped
* thread #1, name = 'main', queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x3eadde6e3620)
    frame #0: 0x000000018bbdc278 libobjc.A.dylib`objc_release + 16
libobjc.A.dylib`objc_release:
->  0x18bbdc278 <+16>: ldr    x17, [x2, #0x20]
    0x18bbdc27c <+20>: tbz    w17, #0x2, 0x18bbdc2dc    ; <+116>
    0x18bbdc280 <+24>: tbz    w16, #0x0, 0x18bbdc2f8    ; <+144>
    0x18bbdc284 <+28>: lsr    x17, x16, #55
Target 0: (wgpu-test2) stopped.
(lldb) bt
* thread #1, name = 'main', queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x3eadde6e3620)
  * frame #0: 0x000000018bbdc278 libobjc.A.dylib`objc_release + 16
    frame #1: 0x000000018bbe4168 libobjc.A.dylib`AutoreleasePoolPage::releaseUntil(objc_object**) + 196
    frame #2: 0x000000018bbe0870 libobjc.A.dylib`objc_autoreleasePoolPop + 256
    frame #3: 0x000000010060f698 wgpu-test2`_$LT$objc..rc..autorelease..AutoReleaseHelper$u20$as$u20$core..ops..drop..Drop$GT$::drop::hdf6e7b1a4aa652e4(self=0x000000016fdf9650) at autorelease.rs:17:18
    frame #4: 0x000000010044b31c wgpu-test2`core::ptr::drop_in_place$LT$objc..rc..autorelease..AutoReleaseHelper$GT$::h00bb076898f42477((null)=0x000000016fdf9650) at mod.rs:490:1
    frame #5: 0x00000001003d82f4 wgpu-test2`objc::rc::autorelease::autoreleasepool::h51d955a4a1eccf26(f={closure_env#0} @ 0x000000016fdf9660) at autorelease.rs:30:1
    frame #6: 0x00000001003cd61c wgpu-test2`wgpu_hal::metal::device::_$LT$impl$u20$wgpu_hal..Device$LT$wgpu_hal..metal..Api$GT$$u20$for$u20$wgpu_hal..metal..Device$GT$::create_render_pipeline::h6fc1dc22f43e56d7(self=0x0000000103076250, desc=0x000000016fdfb3d8) at device.rs:807:9
    frame #7: 0x0000000100142a14 wgpu-test2`wgpu_core::device::Device$LT$A$GT$::create_render_pipeline::h56def6f46007c4fe(self=0x0000000103076200, self_id=Id<wgpu_core::device::Device<wgpu_hal::empty::Api>> @ 0x000000016fdfc190, adapter=0x0000000103055408, desc=0x000000016fdfd720, implicit_context=Option<wgpu_core::device::ImplicitPipelineContext> @ 0x000000016fdfcb08, hub=0x0000000103046810, token=0x000000016fdfc53f) at mod.rs:3030:22
    frame #8: 0x00000001001cb49c wgpu-test2`wgpu_core::device::_$LT$impl$u20$wgpu_core..hub..Global$LT$G$GT$$GT$::device_create_render_pipeline::hdec4b715a29111ff(self=0x0000000103046810, device_id=Id<wgpu_core::device::Device<wgpu_hal::empty::Api>> @ 0x000000016fdfd248, desc=0x000000016fdfd720, id_in=<unavailable>, implicit_pipeline_ids=Option<wgpu_core::device::ImplicitPipelineIds<wgpu_core::hub::IdentityManagerFactory>> @ 0x000000016fdfd258) at mod.rs:4938:34
    frame #9: 0x00000001002cf0e4 wgpu-test2`_$LT$wgpu..backend..direct..Context$u20$as$u20$wgpu..context..Context$GT$::device_create_render_pipeline::h2b743d44cc14811b(self=0x0000000103046810, device=0x000000016fdfdda0, device_data=0x000060000020d240, desc=0x000000016fdfe750) at direct.rs:1174:27
    frame #10: 0x00000001002dd4ec wgpu-test2`_$LT$T$u20$as$u20$wgpu..context..DynContext$GT$::device_create_render_pipeline::h10d77c249d5b5eba(self=0x0000000103046810, device=0x000000016fdfe348, device_data=&(dyn core::any::Any + core::marker::Send + core::marker::Sync) @ 0x000000016fdfddc0, desc=0x000000016fdfe750) at context.rs:2191:13
    frame #11: 0x00000001000c605c wgpu-test2`wgpu::Device::create_render_pipeline::hd25f1cfca56bd068(self=0x000000016fdfe328, desc=0x000000016fdfe750) at lib.rs:2055:26
    frame #12: 0x000000010000a8e0 wgpu-test2`wgpu_test2::main::h46081c84012a4b20 at main.rs:80:21
    frame #13: 0x00000001000042b8 wgpu-test2`core::ops::function::FnOnce::call_once::h6cfae6d08c229c4a((null)=(wgpu-test2`wgpu_test2::main::h46081c84012a4b20 at main.rs:8), (null)=<unavailable>) at function.rs:250:5
    frame #14: 0x0000000100007104 wgpu-test2`std::sys_common::backtrace::__rust_begin_short_backtrace::h6a73e894eb6c4a0d(f=(wgpu-test2`wgpu_test2::main::h46081c84012a4b20 at main.rs:8)) at backtrace.rs:121:18
    frame #15: 0x000000010000b5e4 wgpu-test2`std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h0d1793a52b968c38 at rt.rs:166:18
    frame #16: 0x00000001006eaf48 wgpu-test2`std::rt::lang_start_internal::hed1c56e4b4db0361 [inlined] core::ops::function::impls::_$LT$impl$u20$core..ops..function..FnOnce$LT$A$GT$$u20$for$u20$$RF$F$GT$::call_once::h475731f7094d3540 at function.rs:287:13 [opt]
    frame #17: 0x00000001006eaf40 wgpu-test2`std::rt::lang_start_internal::hed1c56e4b4db0361 [inlined] std::panicking::try::do_call::h12bf4403d16a6a4a at panicking.rs:487:40 [opt]
    frame #18: 0x00000001006eaf40 wgpu-test2`std::rt::lang_start_internal::hed1c56e4b4db0361 [inlined] std::panicking::try::h18bf560b5f72e041 at panicking.rs:451:19 [opt]
    frame #19: 0x00000001006eaf40 wgpu-test2`std::rt::lang_start_internal::hed1c56e4b4db0361 [inlined] std::panic::catch_unwind::hcbe48bb64ad0be62 at panic.rs:140:14 [opt]
    frame #20: 0x00000001006eaf40 wgpu-test2`std::rt::lang_start_internal::hed1c56e4b4db0361 [inlined] std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::h8e727056af92dfee at rt.rs:148:48 [opt]
    frame #21: 0x00000001006eaf40 wgpu-test2`std::rt::lang_start_internal::hed1c56e4b4db0361 [inlined] std::panicking::try::do_call::h79bb23a7a8fc1ffe at panicking.rs:487:40 [opt]
    frame #22: 0x00000001006eaf40 wgpu-test2`std::rt::lang_start_internal::hed1c56e4b4db0361 [inlined] std::panicking::try::h6f847ef34bd6a546 at panicking.rs:451:19 [opt]
    frame #23: 0x00000001006eaf40 wgpu-test2`std::rt::lang_start_internal::hed1c56e4b4db0361 [inlined] std::panic::catch_unwind::h784da673948b3f7f at panic.rs:140:14 [opt]
    frame #24: 0x00000001006eaf40 wgpu-test2`std::rt::lang_start_internal::hed1c56e4b4db0361 at rt.rs:148:20 [opt]
    frame #25: 0x000000010000b5b0 wgpu-test2`std::rt::lang_start::h860dcbdb734f25e3(main=(wgpu-test2`wgpu_test2::main::h46081c84012a4b20 at main.rs:8), argc=1, argv=0x000000016fdfef80, sigpipe='\0') at rt.rs:165:17
    frame #26: 0x000000010000a998 wgpu-test2`main + 36
    frame #27: 0x000000018bc1fe50 dyld`start + 2544
(lldb) ^D

Platform
M1 Pro running macOS Ventura 13.2.1
cargo 1.69.0-nightly (9880b408a 2023-02-28)

[dependencies]
pollster = "0.3.0"
wgpu = "0.15.1"
winit = "0.28.1"

@teoxoy
Copy link
Member

teoxoy commented Mar 2, 2023

Thanks for the detailed report!

I see now that a wgsl vertex shader is required to return a position (https://www.w3.org/TR/WGSL/#function-restriction), however I still would've expected an error message instead of a fault.

It seems we are missing this validation from naga.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants