The design of my program uses one big sampler array in which all textures are stored, including framebuffer images.
Currently I am getting an error complaining that the image layout is VK_IMAGE_LAYOUT_UNDESIGNED when I attempt to render something. The framebuffer’s color and depth images are stored in a sampler array that is active in the current descriptor set, but those particular textures are not being read in the shader.
Previously I got around this by using a lot of image copies, but I am trying to get rid of those and just use layout transitions.
Is my current design something that is even possible to fix, or should I be using a separate descriptor set for each post-processing pass, with the images from the previous pass tacked onto that?
There are multiple reasons not to do this, but the simplest one is that implementations don’t have to allow it.
In order to do what you’re trying to do, the VkImage that all of these array layers come from would have to explicitly state that they want to be used as both sampled images and as framebuffer attachments. Which also means that the memory type used for the memory associated with this image must allow both of those usages.
But implementations are not required to provide any memory types that offer both usages. NVIDIA’s nearly one dozen memory types for example tend to have types that are very specific to certain usages.
Your program will need to adapt to such implementations. So you should just plan for that.
Is there some type of autocorrect on this forum? How did I type “Undesigned”???
I’m going mad here because shadowmaps work fine with this scheme, but no matter what I do some framebuffer color image is still in the undefined image layout, when I know I have transitioned them all, and all other textures work fine. I spent a 12 hour day yesterday just trying to figure this out and got nowhere.
@Alfonse_Reinheart are you saying it is standard practice to do an image copy from a render texture to a read texture? It seems like copying the pixel data would be relatively inefficient. The image usage flags, of course, are set correctly for each texture.
But that is actually what I assume almost every desktop game is doing, isn’t it? Of course, one has to make sure not to sample from an image that is currently written to — but I think, the OP has taken measures to prevent that.
What I mean: Using a color or depth texture that has been rendered in a previous (render)pass – i.e. as a framebuffer attachment – in a subsequent pass as a sampled image is super common, right? Or what am I missing here?
@JoshKlint to your problem:
I assume that there’s just something wrong with your image layout transitions. That could be in your renderpass’ usage descriptions or some other layout transition. Can you post exactly the values that you have specified for the problematic attachments in:
and each VkAttachmentReference::layout ?!
Furthermore, please post validation layer error messages!
Another question: What’s your GPU’s vendor? I found that NVIDIA is way more forgiving w.r.t. layout transitions compared to Intel or especially AMD. So if you’re running on NVIDIA, there’s a chance that it works regardless of a wrong layout. But anyways, the validation layers should point out any obvious errors w.r.t. the image layouts if you have set them up correctly. They were very helpful when I struggled with layouts.
Well, think about what you’re doing. You have an image view which has a bunch of array layers. To read from them as textures, that image view must be in a particular layout.
But (for some reason) you are going to attach one of the layers within this array to the framebuffer. To do that, that specific layer must be in a particular layout which is (or at least may be) different from the layout of all of the other images in that array.
While it is true that you specify layouts for an image subresource (through a view), the layout itself is a property of the memory backing that subresource. Therefore, if you have two views of the same image, A and B, and view A that specifies all of the array layers of an image, and view B only specifies one layer within the same array, if you transition A’s layout, you have also transitioned B’s layout.
So odds are good, you’re stomping on a layout transition at some point. Your shadowmaps work because they’re not within this array texture.
I’m saying that implementations don’t have to allow you to do what you’re doing. How you deal with that fact is ultimately up to you, but at the very least, you have to verify that any memory types you use for this array texture supports both usages.
How could there be? Ignoring the fact that there’s no point in adding a feature to verify something you told the system to do, the “current” layout of an image is ultimately based on sequences of commands in the command buffer, which get executed in queues at its own pace.
Besides, the validation layer is verifying the image layout. That’s why you got an error
Also, I just realized something. You said that the error claims the image layout is undefined. You can’t actually transition an image into the undefined layout. Images only get that layout at the start (if it’s not pre-defined) or due to losing the context. There might be a few other cases that force an image into the undefined layout, but I couldn’t find any with a quick search. So unless you lost the context, you can only get this error if you never transitioned the image in the first place.
Can you provide the exact error message(s) you’re getting?
Are we sure that we are talking about the same thing?
I thought that we are talking about an array of descriptors (each probably referencing a separate VkImageView and descriptor type might be VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) and just those descriptors stay the same forever.
Reading the response from @Alfonse_Reinheart, it appears to me that it refers to layered images (i.e. VkImageCreateInfo::arrayLayers > 1?)
So, what are we talking about exactly? I think, we need much more detailed description of the setup from @JoshKlint. At least I need some clarification. And we definitely need those validation layer error messages.
I think what I have found is that if a shader accesses the texture array, an validation error is thrown if the textures don’t all have the SHADER_READ_ONLY layout, even if the image in question is not used by the shader. If the texture array is not accessed, then it does not matter. This is why I have no problems with shadowmap passes, which presently do not access any textures or have a fragment shader module.
What this means is either a vkImageCopy command is required, so I don’t need to worry about trying to optimize something that can’t be optimized. Alternatively, it could mean that the validation error can be ignored if I know that texture is not accessed in the shader, but that is pretty dicey.
In my design, I am “binding” all textures at once in a big array, so every shader can access every texture that is loaded at any given time. Therefore, it is necessary to have “read” and “write” textures and do mem copies in between them (or change this design, which I am not keen on doing).
The reason I was getting confused was that when draw commands were executed that did not access the color image array, no validation layer was being triggered. So I thought maybe I would be okay if the texture I was writing to wasn’t actually used in the draw call.
If we’re talking about an array of textures, then you can simply “unbind” such images to those array indices. There isn’t actually a way to “unbind” something you’ve already bound, but you don’t have to fill in all of the array elements in a descriptor. You also can bind an empty texture (a 1x1 texture containing no useful data) to effectively “unbind” an image from the descriptor if you want to start using the image in a different layout.
I use one descriptor set for all shaders and sync with the GPU when something changes, which is usually only when a scene is loaded. So getting fancy with that would add another layer of complexity I’m not sure I want to deal with.
It really shouldn’t be particularly complicated. While I don’t particularly agree with any idea that starts with “sync with GPU,” if you’re already paying that cost when things change, then it shouldn’t be too much of a hassle to pay that cost a little more when you’re playing around with render targets.
And if it is too expensive, then you can just double-buffer the descriptor set. Or more specifically, split your descriptors into two sets: one for the array of textures (set 1) and one for the other stuff (set 0). When you need to update the array, just create a new descriptor set, modify its descriptors, swap them out, and get rid of the old descriptor set once the GPU gets finished using it as a cleanup step.
This kind of thing isn’t particularly fancy; it’s standard procedure for using Vulkan, so it’s best to get used to double-buffering things like this.
Alternatively, if you have Vulkan 1.2 or VK_EXT_descriptor_indexing, you can turn on the descriptor-indexing feature/extension. If the implementation supports descriptorIndexing, then you can turn that on as well as the sub-feature descriptorBindingPartiallyBound. This will allow you to create your descriptor sets using the VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT flag. This should remove warnings about invalid descriptors in the array. So long as you don’t use those invalid images during rendering, you’ll be fine.