r/GraphicsProgramming 19h ago

Looking for feedback on my Vulkan renderer before I go further

I’ve been working on a small Vulkan renderer mainly as a learning project and was wondering if anyone had some time to look over it and give me feedback on the overall structure and design.

I started with the Vulkan in 2 Hours video, and after working through that I felt like I picked up how the API worked pretty quickly. From there I started pulling parts out of the original setup and building my own renderer API around them instead of keeping everything directly inside Application.cpp.

Right now I have things like the renderer context, swapchain, shaders, pipelines, descriptors, images and textures, samplers, materials, mesh loading, dynamic rendering, a geometry pass, and a skybox/environment map working.

The hardest part for me so far has probably been understanding queues, queue families, command pools, command buffers, and the overall frame lifecycle. I feel like I understand most of the individual pieces, but figuring out how they should all fit together cleanly has been more difficult than things like pipelines, descriptors, or basic rendering.

Before I start getting into compute shaders, more rendering passes, async compute, and eventually a render graph, I wanted to stop for a bit and make sure I’m not building on top of a bad foundation.

The main thing I’ve noticed is that the public-facing side of the renderer, especially what you see in Application.hpp/.cpp, is starting to feel a little too verbose.

Texture and image creation is probably where I notice it the most. There are a lot of specifications, image views, descriptor registrations, layouts, barriers, and other Vulkan details being handled by the caller. I know Vulkan is supposed to be explicit, and I don’t want to hide everything behind huge abstractions, but I’m not really sure where the line should be between giving the user control and keeping implementation details inside the renderer.

I’m also wondering about the overall structure of the project. Things like resource ownership, descriptor handling, pipeline setup, pass setup, queue and command buffer ownership, frame lifecycle, and how much Vulkan should really be exposed through the higher-level API.

My current plan is to eventually add a SceneRenderer that handles scene rendering and pass submission, then later add a render graph to handle things like resource dependencies, barriers, layouts, and pass ordering. I’d also like to experiment with async compute once I get to that point.

I’m still learning C++ and graphics programming while working on this, so I’m mostly trying to catch bad design choices early before the project gets much bigger.

If anyone has time to look through it, I’d really appreciate feedback on things like:

  • Whether the current abstractions make sense
  • What parts of the API feel too verbose
  • What responsibilities should probably move out of Application
  • Whether the image and texture API should be higher level
  • Whether my queue, command pool, command buffer, and frame lifecycle setup makes sense
  • Anything I’m doing now that might make a render graph or multiple passes harder later
  • General C++ or Vulkan design problems that stand out

I’m not expecting anyone to do a full code review. Even just looking through a few of the renderer files and pointing out anything that looks awkward or could be structured better would help a lot.

Project: https://github.com/AstrixsmCS/VkLab

Thanks.

10 Upvotes

4 comments sorted by

2

u/S48GS 18h ago

queues, queue families, command pools, command buffers, and the overall frame lifecycle

https://howtovulkan.com/#queues

Texture and image creation is probably where I notice it the most. There are a lot of specifications, image views, descriptor registrations, layouts, barriers, and other Vulkan details being handled by the caller. I know Vulkan is supposed to be explicit, and I don’t want to hide everything behind huge abstractions, but I’m not really sure where the line should be between giving the user control and keeping implementation details inside the renderer.

use Vulkan 1.3+ with bindless pipeline - link to modern tutorial above

1

u/dandy_kulomin 14h ago

I read through it. It sucks that we can't avoid descriptors completely since we need them for images. In my mind bda and push constants make so much more sense than the long setup of descriptors.

1

u/S48GS 13h ago

we can't avoid descriptors completely

https://howtovulkan.com/#loading-textures

Now that we have uploaded the texture images, put them into the correct layout and know how to sample them, we need a way for the GPU to access them in the shader. From the GPU's point of view, images are more complicated than buffers as the GPU needs more information on what they look like and how they're accessed. This is where descriptors are required, handles that represent (describe, hence the name) shader resources.

And while descriptor handling is still one of the most verbose parts, using descriptor indexing simplifies this significantly and makes it easier to scale. With that feature we can go for a "bindless" setup, where all image descriptors for the textures are put into one large array and indexed in the shader rather than having to create and bind descriptor sets for every texture. To demonstrate how this works, we'll be loading multiple textures. This approach scales up no matter how many textures you use (within the limits of what the GPU supports).

https://howtovulkan.com/#shader-data-buffers

If we were to use older Vulkan versions we now would have to deal with descriptors, a fundamental but partially limiting and hard to manage part of Vulkan.

But by using Vulkan 1.3's buffer device address feature, we can do away with descriptors (for buffers). Instead of having to access them through descriptors, we can access buffers via their address using pointer syntax in the shader. Not only does that make things easier to understand, it also removes some coupling and requires less code.

you need descriptors only for textures - and they are dynamic - with descriptor indexing

no descriptors for buffers

1

u/XAstrixsmX 8h ago

Im using a global bindless set that has three bindings one for sampled images one for samplers and one for storage images, cube and 3d image arrays get aliased to binding 0 and buffers such as uniform and storage buffers use bda