attackgoat / screen-13

Screen 13 is an easy-to-use Vulkan rendering engine in the spirit of QBasic.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Changing the access descriptor for a Unifrom multiple times within the same pass.

DGriffin91 opened this issue · comments

I'm trying to figure out the best way to bind a different uniform buffer for each draw where each uniform has the same layout, and the rest of the pass/pipeline remains consistent.

Another example would be changing texture bindings for each draw within the same pass.

This is what I've tried, but it's very slow as it results in a new subpass for every draw. It's my understanding that in vulkan, pipelines and bindings can change within a subpass (but not attachments).

let mut pass = frame
    .render_graph
    .begin_pass("Triangle Example")
    .bind_pipeline(&triangle_pipeline)
    .access_node(index_node, AccessType::IndexBuffer)
    .access_node(vertex_node, AccessType::VertexBuffer)
    .clear_color(0, frame.swapchain_image)
    .store_color(0, frame.swapchain_image);

for (i, uniform_node) in uniform_nodes.iter().enumerate() {
    pass = pass
        .access_descriptor(
            5,
            uniform_node.clone(),
            AccessType::VertexShaderReadUniformBuffer,
        )
        .record_subpass(move |subpass, _| {
            subpass.bind_index_buffer(index_node, vk::IndexType::UINT16);
            subpass.bind_vertex_buffer(vertex_node);
            subpass.draw_indexed(3, 1, 0, 0, i as u32);
        });
}

You might be able to improve the performance by using a bindless approach where you record one subpass and use push constants or gl_InstanceIndex to change what index the next draw references out of that bindless array. Example.

If you are changing other bindings or pipelines this won't help because the code at graph/resolver.rs:1108 ultimately creates one subpass for each "exec" (each call to record_subpass).

This could be done in a smarter way to avoid creating new subpasses when the rules allow. There is also some performance left on the table because fresh descriptor sets are written and bound for each subpass. I can't say how soon I can fix these items they should not change any public API.

... it's very slow ...

There is a performance tax of the runtime approach compared to pre-baked command buffers and descriptor sets. I do think there should be a compatible solution for times when a section of the render graph is known to stay the same or just need descriptor writes. Right now it's possible to use record_cmd_buf and do raw Vulkan inside of a larger graph but I would like to provide a better solution.

Aside from finding solutions that might work for you in the meantime, I see three issues to correct here:

  • Create subpasses only if required
  • Re-use existing descriptor sets when possible
  • Provide a way to "bake" render graphs and replay them over many frames

Thanks for your reply. And thanks for your work on this project, it's really awesome!

I am planning on using bindless where possible. But there's some cases where rebinding within a pass is somewhat inevitable.

It would be great to be able to cache/record pipelines, descriptor sets, etc... In my case, the majority of these don't change from frame to frame.