gfx-rs / wgpu

A cross-platform, safe, pure-Rust graphics API.
https://wgpu.rs
Apache License 2.0
11.43k stars 853 forks source link

Surface configuration Validation Error #5823

Open StepingForward opened 1 week ago

StepingForward commented 1 week ago

Description I am using glfw and wgpu(following this tutorial: https://www.youtube.com/watch?v=DGPrn5qCI_c), for some reason when I run the code I get a validation error:

Error in Surface::configure: Validation Error

Caused by:
    Invalid surface

The code:

use glfw::{
    fail_on_errors, 
    Action, 
    Context, 
    Key,
    Window
};
use wgpu::core::command;

struct State <'s>{
    instance: wgpu::Instance,
    surface: wgpu::Surface<'s>,
    device: wgpu::Device,
    queue: wgpu::Queue,
    config: wgpu::SurfaceConfiguration,
    size: (i32, i32),
    window: &'s mut Window,
}

impl<'s> State<'s>{
    async fn new(window: &'s mut Window) -> Self{
        let size = window.get_size();

        let instance_descriptor = wgpu::InstanceDescriptor{
            backends: wgpu::Backends::PRIMARY, ..Default::default()
        };

        let instance = wgpu::Instance::new(instance_descriptor);

        //Unsafe surface... spooky :0 No.1
        let target = unsafe {
            wgpu::SurfaceTargetUnsafe::from_window(&window)
        }.unwrap();
        let surface = unsafe {
            instance.create_surface_unsafe(target)
        }.unwrap();

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

        let device_descriptor = wgpu::DeviceDescriptor {
            required_features: wgpu::Features::empty(),
            required_limits: wgpu::Limits::default(),
            label: Some("Device"),
        };
        let (device, queue) = adapter.request_device(&device_descriptor, None).await.unwrap();

        let surface_capabilities = surface.get_capabilities(&adapter);
        let surface_format = surface_capabilities.formats.iter()
            .find(|f| f.is_srgb())
            .copied()
            .unwrap_or(surface_capabilities.formats[0]);

        let config = wgpu::SurfaceConfiguration {
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
            format: surface_format,
            width: size.0 as u32,
            height: size.1 as u32,
            present_mode: surface_capabilities.present_modes[0],
            alpha_mode: surface_capabilities.alpha_modes[0],
            view_formats: vec![],
            desired_maximum_frame_latency: 2,
        };
        surface.configure(&device, &config);

        Self {
            instance,
            surface,
            device,
            queue,
            config,
            size,
            window
        }
    }

    fn render(&mut self) -> Result<(), wgpu::SurfaceError>{
        let drawable = self.surface.get_current_texture()?;
        let image_view_descriptor = wgpu::TextureViewDescriptor::default();
        let image_view = drawable.texture.create_view(&image_view_descriptor);

        let command_encoder_descriptor = wgpu::CommandEncoderDescriptor{
            label: Some("Render Encoder"),
        };
        let mut command_encoder = self.device.create_command_encoder(&command_encoder_descriptor);

        let color_attachment = wgpu::RenderPassColorAttachment {
            view: &image_view,
            resolve_target: None,
            ops: wgpu::Operations{
                load: wgpu::LoadOp::Clear(wgpu::Color{
                    r: 0.25,
                    g: 0.0,
                    b: 0.5,
                    a: 0.0,
                }),
                store: wgpu::StoreOp::Store,
            }
        };

        {
            let _render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                label: Some("Render Pass"),
                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                    view: &image_view,
                    resolve_target: None,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(wgpu::Color {
                            r: 0.1,
                            g: 0.2,
                            b: 0.3,
                            a: 1.0,
                        }),
                        store: wgpu::StoreOp::Store,
                    },
                })],
                depth_stencil_attachment: None,
                occlusion_query_set: None,
                timestamp_writes: None,
            });
        }

        self.queue.submit(std::iter::once(command_encoder.finish()));

        drawable.present();
        Ok(())
    }

    fn resize(&mut self, new_size: (i32, i32)) {
        if new_size.0 > 0 && new_size.1 > 0 {
            self.size = new_size;
            self.config.width = new_size.0 as u32;
            self.config.height = new_size.1 as u32;
            self.surface.configure(&self.device, &self.config)
        }
    }

    fn update_surface(&mut self) {
        //Unsafe surface... spooky :0 No.2
        let target = unsafe {
            wgpu::SurfaceTargetUnsafe::from_window(&self.window)
        }.unwrap();
        self.surface = unsafe {
            self.instance.create_surface_unsafe(target)
        }.unwrap();
    }
}

async fn run() {
    let mut glfw = glfw::init(fail_on_errors!())
        .unwrap();
    let (mut window, events) = 
        glfw.create_window(
            800, 600, "It's SNA time.", 
            glfw::WindowMode::Windowed).unwrap();

    let mut state = State::new(&mut window).await;
    state.window.set_all_polling(true);

    state.window.make_current();

    glfw.set_swap_interval(glfw::SwapInterval::Sync(1));

    while !state.window.should_close() {
        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&events) {
            match event {
                glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
                    state.window.set_should_close(true)
                }

                _ => {}
            }
        }

        match state.render() {
            Ok(_) => {},
            Err(e) => eprintln!("{:?}", e),
        }
        state.window.swap_buffers();
    }
}

fn main(){
    pollster::block_on(run());
}

Platform Windows 10, Vulkan Instance Version: 1.3.255

Vecvec commented 1 week ago

have you tried

    glfw.window_hint(WindowHint::ClientApi(ClientApiHint::NoApi));

just before creating the windows. This fixed some issues on some platforms.

StepingForward commented 1 week ago

I put it right before creating the window, but it returns a new error:

GLFW Error: Cannot make current with a window that has no OpenGL or OpenGL ES context
Vecvec commented 1 week ago

That's probably todo with

state.window.make_current();

only some calls can be done without an api (typically because the other api can do this). For example:

window.swap_buffers();

does basically the same thing as

surface_texture.present();

though on the internal API (in this case OpenGL).

Also this conversation should probably be moved to glfw-rs as this is mostly a glfw issue.

Questions I have:

Vecvec commented 1 week ago

Copying the code onto my machine and making this the run function

        let mut glfw = glfw::init(fail_on_errors!())
            .unwrap();
        glfw.window_hint(WindowHint::ClientApi(ClientApiHint::NoApi));
        let (mut window, events) =
            glfw.create_window(
                800, 600, "It's SNA time.",
                glfw::WindowMode::Windowed).unwrap();

        let mut state = State::new(&mut window).await;
        //state.window.set_all_polling(true);

        //state.window.make_current();

        //glfw.set_swap_interval(glfw::SwapInterval::Sync(1));

        while !state.window.should_close() {
            glfw.poll_events();
            for (_, event) in glfw::flush_messages(&events) {
                match event {
                    glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
                        state.window.set_should_close(true)
                    }

                    _ => {}
                }
            }

            match state.render() {
                Ok(_) => {},
                Err(e) => eprintln!("{:?}", e),
            }
            //state.window.swap_buffers();
        }

seems to fix the issue (commented out code is both not required and unsupported). This is definitely a GLFW documentation issue. I'm working on a pull request to fix the docs.

StepingForward commented 1 week ago

OMG, it works now! Thank you so much!!!

Vecvec commented 1 week ago

Thank you! By the way, there is a safe way of creating a surface:

instance.create_surface(window.render_context()).unwrap();