Drawing a UI overlay

Heysh,

I’m drawing a basic 3D scene like so:

Two attachments were made
One was given to Color
And the other to Depth
One RenderPass to draw them all
One Framebuffer to draw them to
One CommandBuffer to bring them all
And to the Pipeline bind them

Well, technically one CommandBuffer for each image in the Swapchain, but that’s beside the point.

So let’s say I wanted to draw a 2D UI overlay on top of the 3D scene. The rendering of the UI wouldn’t depend on the 3D output in any way, so it could render in parallel while the 3D was being rendered. Once both are finished, blend the UI on top of the 3D. My question is, can I do this still in just one RenderPass (with possibly several subpasses) by just adding a third attachment? And my second question is, if I wanted multisampling on the UI but not on the 3D, do I then need a separate RenderPass, and maybe a separate Pipeline? And since there are probably many ways of going about this, is there a best practice for this particular common problem?

Cheers,
Miika Vihersaari

PS. I’m not sure whether to put this topic in basic or advanced coding, but it feels more of a basic thing so here it goes.

Oh no, the rendermancer has returned!

I mean “parallel”… You only have one GPU. Only thing that could be called vaguely parallel is async compute executed next to geometric frontend stuff. Everything else just largelly uses the same underlying resources. You likely want to minimize memory bandwith, which means just render it to the same image, instead of introducing some additional temporary images.

If you have opaque 2D UI, the smart thing to do is render it first. Then when 3D renders, some of the fragments that are covered get discarded by depth or stencil test.

Should be perfectly doable in one Render Pass. Everything that is localized to a single pixel can be done in a single Render Pass. In fact, simple draws can be done in a single subpass.

If you have multisampling in the UI, the natural way to do it I think would be to render it to multisampled image, and resolve it into the target (as resolve attachment). Then in another subpass continue rendering singlesamplingly to the target (as color attachment).

Heh :slight_smile: I had the intention to quip about the Viewport of Sauron, but perhaps I shall leave that for another day.

About multisampling, let me see if I understood you correctly. The Framebuffer would have:

  1. Swapchain image as color attachment
  2. An image as depth attachment
  3. A multisampled image as color attachment (with VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
  4. A resolve attachment

Then first I render the UI to 3, and resolve it 4 → 1. Finally render the 3D scene to 1. Is that about right? Does the resolve attachment take up additional image memory, or is it more like a sampler in the sense that it only reads from one image and writes to another? I’m reading the Vulkan spec, but I’m left confused about what’s going on under the hood.

Thanks @krOoze for your help! I hope to be able to contribute to the forums as I learn more.

Cheers,
Miika Vihersaari

If the UI have same samples, and it is opaque, then it is trivial, right? You would just render your stuff, then render the UI on top (or better yet in oposite order to save on fragment shader invocation count).

The annoyances here are the multisampled stuff, and transparency.

The multisampled stuff needs to be resolved. Annoyingly the default way to resolve just writes the whole image, and is not aware of stuff like blending or stencil. Otherwisely a tryhard way would be to try resolving it “manually” with input attachments and shader code.

The transparency requires some kind of blending (i.e. access to both current and previous value). That is problematic, because we only want to blend the UI, but probably none of the 3D with other 3D. Again, if masochistic enough, one could try to do something equivalent to default Blending with input attachments.

Well, there’s always more ways to skin the cat. But ideally one should have some basic sane codepath before trying to be smart.

So as you say, you could have:

  1. the output image
  2. the multisampled temporary\transient image for the UI
  3. the singlesampled temporary\transient image for the UI

One subpass would have (3) as color attachment, and (4) as resolve attachment, to render the UI. Another subpass would have (1) as color attachment, to render the 3D stuff. A third subpass would depend on the previous subpasses, and have (4) as input attachment, and (1) as color attachments, and blend stuff together.

It has the annoying property of having two temporary images though. But it seems flexible and good enough.

Depends on the driver\GPU. Though you can give the driver strong hints that the image is temporary. Do not VK_ATTACHMENT_LOAD_OP_LOAD or VK_ATTACHMENT_STORE_OP_STORE. Either dont-care it, or clear it. Then create it with VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT and bind it to VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT.

In all likelihood on a tiled-based architecture, such images will never exist in general-purpose RAM. They will only exist on-chip for the particular tile being processed.