我有一些很少更新的纹理数据,但我无法控制它何时更新(数据是从呈现 GUI 的外部库提供的)。
目前,我在每一帧中更改其布局两次:
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
到VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
,具有管道阶段VK_PIPELINE_STAGE_TRANSFER_BIT
到VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
到VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
,阶段为src=VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
到dst=VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
。我认为,每次发生的这种转换可能是浪费的,因为我几乎总是没有任何纹理更新,所以我正在考虑实现这个过程:
这是一个好方法吗?还有其他方法可以减少此类布局转换吗? 我也考虑只使用
VK_IMAGE_LAYOUT_GENERAL
。
您确定这是一个实际问题吗?驱动程序知道 dst_optimal 之后的下一个布局传输很可能是 read_only。因此,这些转变不太可能真正起到任何作用。
如果在发生更改时完全覆盖纹理,那么工作流程应该是:
if(image_changed){
pipeline barrier from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
vkCmdCopyBufferToImage(cmd, staging_buffer, texture, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
//copy from staging buffer to texture
pipeline barrier from VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
}
VK_IMAGE_LAYOUT_UNDEFINED
因为 src
让驱动程序完全丢弃旧数据,跳过其中一个转换。
这假设
image_changed
是一个足够好的支票。
我刚刚明白我忘记了我自己可以跟踪纹理的状态。
例如,在我的问题中,纹理有 3 种可能的状态和相应的布局:
VK_IMAGE_LAYOUT_UNDEFINED
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
。因此我们可以将状态存储在枚举中:
enum TextureState{
just_created,
used_for_render,
had_transfer,
}
并在记录每帧命令缓冲区期间访问它(伪代码):
// 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
}
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;
这样我们可以确保: