This allows us to avoid doing some unneeded work on the meta paths where we
know that the image view will be used for exactly one thing. The meta
paths also sometimes do things that aren't quite valid like setting the
array slice on a 3-D texture and we want to limit the number of paths that
need to be able to sensibly handle the lies.
We can't use a global descriptor pool like we were because it's not
thread-safe. For now, we'll allocate them on-the-fly and that should work
fine. At some point in the future, we could do something where we
stack-allocate them or allocate them out of one of the state streams.
Descriptor pools are an optimization that lets applications allocate
descriptor sets through an externally synchronized object (that is,
unlocked). In our case it's also plugging a memory leak, since we
didn't track all allocated sets and failed to free them in
vkResetDescriptorPool() and vkDestroyDescriptorPool().