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

Vulkano util proposal #1918

Merged
merged 22 commits into from
Jun 24, 2022
Merged

Vulkano util proposal #1918

merged 22 commits into from
Jun 24, 2022

Conversation

hakolao
Copy link
Contributor

@hakolao hakolao commented Jun 21, 2022

I've been thinking about a design for a set of utility structs to help with the boilerplate that comes with any Vulkano project. Mostly meaning: Instance creation, device selection, swapchain images, etc. So everything that happens only once, and don't change that much from project to project (unless there are some niche requirements), and the organizing of pre and after render.

So here is my proposal: Vulkano Util crate.

Typical use cases would be doable with just default values, most or all use cases specified with the configs. Feel free to give feedback and criticize! :). I can always make it my own crate too, but having it be maintained and improved by vulkano developers would be great.

There would exist the following structs allowing you to focus mainly on your Vulkano pipelines.

  • VulkanoContext: Holds, Instance, Device, Queue(s) and an optional DebugUtilsMessenger. It's created with VulkanoConfig which has a set of Default values, but can be customized with create infos and device selection functions.
  • VulkanoWindows: Holds VulkanoWindowRenderer for each winit window and functions to access these windows and renderers.
  • VulkanoWindowRenderer: Used to organize rendering, swapchains, resizing and recreation of swapchain. Has an additional access to images that are resized with window (optional). Begin your render with start_frame and end with finish_frame.
    • VulkanoWindowRenderer::acquire: Recreates swapchain if needed (or requested), acquires next swapchain image and joins the future from that with previous frame end. Returns that future. Use it to synchronize your render between acquire and present.
    • VulkanoWindowRenderer::present(future: Box<dyn GpuFuture>, wait_future: bool): Used to present the swap chain image after all your executed command buffers. The idea is that you'd pass your last future before you wish to present the image on window. Optionally you can explicitly wait(None) on the future with the wait_future input. For example to ensure e.g. your compute queues aren't using a bound resource...

Usage:

    // Create event loop
    let mut event_loop = EventLoop::new();
    // Create vulkano context with default values
    let context = VulkanoContext::new(VulkanoConfig::default());
    // Create vulkano windows struct (empty at this point)
    let mut windows = VulkanoWindows::default();
    // Create window with title "Fractal" without vsync (present mode)
    let _id = windows.create_window(
        &event_loop,
        &context,
        &WindowDescriptor {
            title: "Fractal".to_string(),
            present_mode: PresentMode::Immediate,
            ..Default::default()
        },
        // You can modify `SwapchainCreateInfo` through this
        |_| {},
    );

With just all that you'd have a window ready to be drawn on.

Drawing:

    // Start frame
    let before_pipeline_future = match renderer.acquire() {
        Err(e) => {
            println!("{}", e.to_string());
            return;
        }
        Ok(future) => future,
    };
    // Execute your command buffers here in between
    // and pass your last future to `finish_frame`.
    renderer.present(after_renderpass_future, true);

I've also modified the fractal and game of life examples to use these. I don't think this replaces other tutorials necessarily, but could be helpful in future examples.

So if you think this is something that would be useful, let me know what we can do to get this PR through. Renames, docs, refactors, improvements, test suggestions.

These util structs should only wrap things in a way that can help the usage of Vulkano, but should not prevent you from doing anything.

@Rua
Copy link
Contributor

Rua commented Jun 21, 2022

This looks quite useful!

One question though: does your swapchain handling code assume that the user will create a framebuffer from the swapchain image? In my own code, it's done a little differently: there is only a single framebuffer image, and its contents gets copied to the current swapchain image after rendering. The swapchain image is only acquired at the end of rendering (since rendering can use its own image). If the window is resized, the swapchain is recreated but the dedicated framebuffer image is also recreated. My reason for doing this is to have more control over how the framebuffer image is used: a swapchain image is only guaranteed to support the color_attachment usage, but in my case I also want transfer_src in order to copy screenshots.

I'm not necessarily saying that your PR needs to support that use case. I just wondered if it does, and whether you think alternatives like mine would be worth supporting as well.

Another possible addition would be to support begin_rendering, as an alternative to creating a render pass and framebuffer.

@hakolao
Copy link
Contributor Author

hakolao commented Jun 21, 2022

@Rua This makes no assumptions of framebuffer usage. The only added functionality is that you can add StorageImage views through the api of VulkanoWindowRenderer with add_additional_image_view that are also automatically resized with the swapchain. But you don't have to add such images if you don't want to and can handle them yourself in your app and in winit event loop.

Anyway, the framebuffers definitions, pipeline definitions would all be outside these utils. And your drawing is intended to occur between start_frame and finish_frame, but you could draw outside too... Just these would help with the future synchronization.

You can get your image targets with:

// For the basic image views that you've added with the API if you want to. Such images are automatically resized if you just
// call `renderer.resize()` at the right place in your winit event loop.
let img = renderer.get_additional_image_view(target_image_id)
// For swapchain image
let img = renderer.swapchain_image_view()

The idea is that this should not prevent you from using images the way you want to.

@hakolao
Copy link
Contributor Author

hakolao commented Jun 21, 2022

So the correct swapchain image let img = renderer.swapchain_image_view() will be available for you to do anything you can to do it between start_frame and finish_frame.

begin_rendering should already work! As long ass its requirements are filled through configs.

@Rua
Copy link
Contributor

Rua commented Jun 21, 2022

So as I understand it, start_frame really only does the swapchain side of things. Its name and documentation may have misled me then. Perhaps something like acquire and present would better reflect what they actually do?

@hakolao
Copy link
Contributor Author

hakolao commented Jun 21, 2022

Yea, renaming those is a good idea. -> Done that.

@@ -223,6 +225,39 @@ impl StorageImage {
}))
}

/// Allows the creation of a simple 2D general purpose image view from `StorageImage`.
pub fn general_purpose_image_view(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I added this is that I often use storage images directly wrapped as views and it would be nice to have methods to output a view straight a way for the type. Optional method to save verbosity.

@Rua
Copy link
Contributor

Rua commented Jun 24, 2022

Is this ready for merging now? @AustinJ235 @Eliah-Lakhin could you have a look too?

@hakolao
Copy link
Contributor Author

hakolao commented Jun 24, 2022

Ready!

@Rua Rua merged commit 18f6833 into vulkano-rs:master Jun 24, 2022
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 this pull request may close these issues.

2 participants