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().