I have some texture data that updates very rarely but I cannot control when it updates (data is provided from an external library that renders GUI).
Currently, I change their layout twice in every frame:
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
with pipeline stages VK_PIPELINE_STAGE_TRANSFER_BIT
to VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
with stages src=VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
to dst=VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
.I think, such transformations that happen every time may be wasteful because I almost always don't have any updates for my textures, so I am thinking about implementing this procedure:
Is this a good approach? Are there any other ways to accomplish a reduction of such layout transforms?
I also consider just using VK_IMAGE_LAYOUT_GENERAL
.
I just understood that I forgot that I can track the state of texture myself.
For example, in my question, there is 3 possible states and corresponding layouts of a texture:
VK_IMAGE_LAYOUT_UNDEFINED
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
.So we can store a state in a enum:
enum TextureState{
just_created,
used_for_render,
had_transfer,
}
And access it during recording our command buffers every frame (pseudocode):
// Copying data to texture.
if (need_to_copy_to_texture) {
if (tex_state == TextureState::had_transfer) {
// Do nothing, we already in compatible layout
}
else if (tex_state == TextureState::just_created) {
// Need VkImageMemoryBarrier here
pipeline barrier VK_IMAGE_LAYOUT_UNDEFINED =>
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
}
else if (tex_state == TextureState::used_for_render) {
// Need VkImageMemoryBarrier here
pipeline barrier VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL =>
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
}
record copy of data to texture;
tex_state = TextureState::had_transfer;
}
// Now we want to render
if (tex_state == TextureState::used_for_render) {
// Do nothing, we already in compatible layout for rendering
// because there weren't any transfers after last rendering.
}
else if (tex_state == TextureState::just_created) {
// This probably should not happen.
assert(false);
}
else if (tex_state == TextureState::had_transfer) {
// Need VkImageMemoryBarrier here
pipeline barrier VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL =>
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
}
tex_state = TextureState::used_for_render;
begin rendering;
bind pipeline with our texture;
bind descriptor set with out texture;
issue draw call;
end rendering;
submit command buffer to a queue;
This way we ensure that: