VULKAN: Some bit twiddling for the hell of it

I was tinkering with Vulkan the other day because I wanted to achieve multisampling in my Vulkan-based 3D application.

And I was reading the Vulkan Tutorial’s page on Multisampling to refresh my mind, when I saw something a bit « ugly » that caught my eye.

What is this long chain of ifs and returns ? That sucks

I don’t like the way it looks. First of all, the purpose of this code is to return the highest number of samples available, but all those ifs are not future proof.

What if one day, a GPU can support more than 64 samples per pixel ? This piece of lower level engine code is definitely going to be forgotten deep down for maybe a long time. Even when you’ll want to crank MSAA to the max, this code will never go higher than 64 spp, which could be a bit sad, maybe. (This is actually the highest value for probably a long time on the current hardware, but ! Imagine we don’t know that.)

Surely we have a way to « do it better », so to speak ? But bear in mind that I’m not criticizing. This tutorial is aimed at true Vulkan beginners, and so is already packed full with a truck load of hard-to-decipher information. So I totally understand the need of the original author to « keep it simple and stupid » (KISS).

I just like to fiddle with bits. Now what can we do about it ? We know that the sample count enum looks like this :

It’s a typical power-of-two bitflags enum, which is a bit weird for sample counts when you think about it (because you cannot have multiple valid values !). But it is probably done that way so the device can return us a bitmask of the available counts for each type of attachments, rather than a single « max » value, for example.

Knowing this, we can greatly simplify the above return code. Since we know that the largest valid sample count bit is the leftmost bit set to 1, and that all bits to its right are probably going to be 1 too, all we have to do is :

  • invert all bits (using binary NOT)
  • bit shift that inverted value to the right (so that we save the most significant set bit later)
  • then mask the original value with an AND to reset all other bits to zero, except the most significant set bit (the one of the highest sample count), which is the only bit that is going to stay at 1 after the AND.
Example of a right bit shift : all bits of the value move to the right, discarding those that « fall out »

And we’re done ! It basically looks like this :

I had to reinterpret_cast because the type system definitely didn’t want me to consider a vk::SampleCountFlags as an integer. But it works ! It made the code both shorter and more future proof, but maybe a tiny bit harder to understand because of this bit wizardry.

There may even be a simpler and/or better way to do it. Feel free to let us know in the comments if you have a better idea !


Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *