问题描述
我正在尝试新的Vulkan synchronization validation tool,它在我的纹理加载代码中发现了写/写危险:
mipLevelCount = mipmaps == Mipmaps::Generate ? MathUtil::GetMipmapCount( width,height ) : 1;
VkImageSubresourceRange range = {};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.baseMipLevel = 0;
range.levelCount = mipLevelCount;
range.baseArrayLayer = 0;
range.layerCount = 1;
imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGnorED;
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGnorED;
imageMemoryBarrier.srcAccessMask = 0;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
imageMemoryBarrier.image = image;
imageMemoryBarrier.subresourceRange = range;
vkCmdPipelineBarrier(
GfxDeviceGlobal::texCmdBuffer,VK_PIPELINE_STAGE_TRANSFER_BIT,VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,nullptr,1,&imageMemoryBarrier );
for (int i = 1; i < mipLevelCount; ++i)
{
const std::int32_t mipwidth = MathUtil::Max( width >> i,1 );
const std::int32_t mipHeight = MathUtil::Max( height >> i,1 );
VkImageBlit imageBlit = {};
imageBlit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageBlit.srcSubresource.baseArrayLayer = 0;
imageBlit.srcSubresource.layerCount = 1;
imageBlit.srcSubresource.mipLevel = 0;
imageBlit.srcOffsets[ 0 ] = { 0,0 };
imageBlit.srcOffsets[ 1 ] = { width,height,1 };
imageBlit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageBlit.dstSubresource.baseArrayLayer = 0;
imageBlit.dstSubresource.layerCount = 1;
imageBlit.dstSubresource.mipLevel = i;
imageBlit.dstOffsets[ 0 ] = { 0,0 };
imageBlit.dstOffsets[ 1 ] = { mipwidth,mipHeight,1 };
vkCmdBlitimage( GfxDeviceGlobal::texCmdBuffer,image,VK_IMAGE_LAYOUT_GENERAL,&imageBlit,VK_FILTER_LINEAR );
}
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageMemoryBarrier.image = image;
imageMemoryBarrier.subresourceRange = range;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.levelCount = mipLevelCount;
vkCmdPipelineBarrier( // <-- The error happens at this call.
GfxDeviceGlobal::texCmdBuffer,&imageMemoryBarrier );
错误是:
ERROR: Validation Error: [ SYNC-HAZARD-WRITE_AFTER_WRITE ] Object 0: handle = 0x559307e35610,type = VK_OBJECT_TYPE_IMAGE; | MessageID = 0xfdf9f5e1 | vkCmdPipelineBarrier: Hazard WRITE_AFTER_WRITE for image barrier 0 VkImage 0x559307e35610[]. Access info (usage: SYNC_IMAGE_LAYOUT_TRANSITION,prior_usage: SYNC_IMAGE_LAYOUT_TRANSITION,write_barriers: 0,command: vkCmdPipelineBarrier,seq_no: 1,reset_no: 3).
如何解决?
解决方法
请记住,布局转换是发生在srcStage
和dstStage
之间的读写操作。
首先,您提交布局转换。它发生在VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
之前。
然后您执行vkCmdBlitImage
,由于它是STAGE_TRANSFER
,而不是STAGE_FRAGMENT_SHADER
,因此会被错误同步。虽然是您的mipLevelCount == 1
,所以整个循环都是无效代码。
然后您将进行另一个布局转换。它发生在VK_PIPELINE_STAGE_TRANSFER_BIT
之后。
因此,您可能同时发生两个布局转换。
我认为设置dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT
应该可以解决此问题。