hoj-senna / ashen-aetna

Ashen Aetna — Rustily stumbling around on an ash-covered volcano (A tutorial on/in/about/with 3D graphics, Rust, Vulkan, ash)
45 stars 3 forks source link

Some improvements #5

Open michidk opened 3 years ago

michidk commented 3 years ago

Hey, just wanted to let you know of some of the improvements I would make. Might be of interest to everyone, that used your guide as starting point and wants to improve it a little bit further.

ash_window

We can make the code platform independent by using ash_window, like so:

        let surface = unsafe { ash_window::create_surface(entry, instance, window, None).unwrap() };
        let surface_loader = khr::Surface::new(entry, instance);

Also add ash-window = "0.5.0" to your cargo.toml. Also makes the code a little bit shorter.

vk-mem

As long as vk-mem version 0.2.3. is not released (https://github.com/gwihlidal/vk-mem-rs/issues/46), use vk-mem = { git = "https://github.com/gwihlidal/vk-mem-rs", version = "0.2.3" } in cargo.toml. Current version is not working, see https://github.com/gwihlidal/vk-mem-rs/issues/47;

Graphics device selection

We should pick a device more carefully. Prefer discrete GPUs, but maybe rank by other metrics. See https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Physical_devices_and_queue_families.

Presentation queue

See https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Window_surface, chapter "Querying for presentation support" and following.

Because it's possible that the queue supporting the drawing command won't support presentation. So we should create a separate presentation queue, and use it for presentation. We already have a separate transfer queue, but it seems like it's never used.

EDIT: On the other hand, it's very rare (not sure if such a device does even exist), where there is not a queue that supports presentation and drawing. So maybe just use that one queue and panic otherwise. Because using two different queues might have some implications later, where transfers occur.

Presentation mode

The Mailbox presentation mode should be preferred. Example:

    pub fn choose_present_mode(
        &self,
        physical_device: vk::PhysicalDevice,
    ) -> Result<vk::PresentModeKHR, vk::Result> {
        let present_modes = self.get_present_modes(physical_device)?;
        Ok(if present_modes.contains(&vk::PresentModeKHR::MAILBOX) {
            vk::PresentModeKHR::MAILBOX
        } else {
            vk::PresentModeKHR::FIFO
        })
    }

Presentation format

Similarly, we should choose a presentation format. Right now, we just select the first entry (*surface.get_formats(physical_device)?.first().unwrap();, remember? See #4).

E.g.:

    pub fn choose_format(
        &self,
        physical_device: vk::PhysicalDevice,
    ) -> Result<vk::SurfaceFormatKHR, vk::Result> {
        let formats = self.get_formats(physical_device)?;
        let optimal = formats.iter().find(|x| {
            x.format == vk::Format::B8G8R8A8_UNORM
                && x.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR
        });
        Ok(if let Some(optimal) = optimal {
            *optimal
        } else {
            formats[0]
        })
    }

Also consider using vk::Format::B8G8R8A8_SRGB to apply gamma correction automatically (currently is no gamma correction applied). But then also the shader inputs have to be in SRGB space.

Texture Descriptor

In chapter 37/38 we display textures. But when using the Mailbox presentation mode, the texture will flicker. This is because we have to create a WriteDescriptorSet for every texture descriptor set (of every swapchain image), like so:

    let texture_id = renderer.new_texture_from_file("./assets/images/rust.png")?;
    if let Some(texture) = renderer.texture_storage.get(texture_id) {
        for dss in &renderer.descriptor_sets_texture {
            let imageinfo = vk::DescriptorImageInfo {
                image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
                image_view: texture.imageview,
                sampler: texture.sampler,
                ..Default::default()
            };
            let descriptorwrite_image = vk::WriteDescriptorSet {
                dst_set: *dss,
                dst_binding: 0,
                dst_array_element: 0,
                descriptor_type: vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
                descriptor_count: 1,
                p_image_info: [imageinfo].as_ptr(),
                ..Default::default()
            };

            unsafe {
                renderer
                    .device
                    .update_descriptor_sets(&[descriptorwrite_image], &[]);
            }
        }
    }

Source code

Oh and lastly it would be nice, if all the code would be available in this repository, too instead of just in the markdown files.

I might extend this list in the future, as I finish your tutorial.

hoj-senna commented 3 years ago

Many thanks!

(For me, in particular the reference to ash_window is interesting. I was not aware of this crate.)

In order to preserve the character of the notes as a protocol of "this is how I've stumbled around, this is what I've tried" and avoid inconsistencies of the type "update included only in one place, later references or comments thereby broken", I won't update the text now.

(I may later (when I again find more time to work on these notes) include a few links to this list in appropriate places or account for them in a future cleanup/corrections/improvement chapter.)

Excuses for the lack of responses aside, I am very glad to have a list of possible improvements, for me as well as for other readers.

Thank you again, and keep the comments coming.