Implement readonly depth/stencil attachment on Vulkan

It requires to revise quite a few flags in order to implement
readonly depth/stencil attachment on Vulkan. For example:
  - VkAccessFlags,
  - VkPipelineStageFlags,
  - VkImageUsageFlags,
  - and the most important: VkImageLayout.

These revised flags need to be applied to many Vulkan objects.
For examples:
  - VkImageMemoryBarriers,
  - IMAGE_LAYOUT revisions in descriptor set (bindings),
    render pass, and subpass,
  - and loadOp/storeOp revisions in render pass and subpass.

This change also does some workarounds in order to make Vulkan
validation layers happy. For example:
  - use DEPTH_STENCIL_READ_ONLY image layout for binding a
    depth/stencil texture,
  - add DEPTH_STENCIL_ATTACHMENT_BIT image usage for binding a
    depth/stencil texture.

Note that STORE_OP_STORE is used for depth/stencil's storeOp for
readonly attachment. It leads to Vulkan validation error. This
change igores that error and let it proceeds and it works well.

Bug: dawn:485
Change-Id: Ie247c9cbffbd837984b0933a905632ab5ad8862d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/70280
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Yunchao He <yunchao.he@intel.com>
This commit is contained in:
Yunchao He 2021-12-09 21:32:28 +00:00 committed by Dawn LUCI CQ
parent 1b1b658d36
commit 15fec68a4f
7 changed files with 109 additions and 30 deletions

View File

@ -52,6 +52,31 @@ constexpr char kVulkanLibName[] = "libvulkan.so";
# error "Unimplemented Vulkan backend platform"
#endif
struct SkippedMessage {
const char* messageId;
const char* messageContents;
};
// Array of Validation error/warning messages that will be ignored, should include bugID
constexpr SkippedMessage kSkippedMessages[] = {
// These errors are generated when simultaneously using a read-only depth/stencil attachment as
// a texture binding. This is valid Vulkan.
//
// When storeOp=NONE is not present, Dawn uses storeOp=STORE, but Vulkan validation layer
// considers the image read-only and produces a hazard. Dawn can't rely on storeOp=NONE and
// so this is not expected to be worked around.
// See http://crbug.com/dawn/1225 for more details.
{"SYNC-HAZARD-WRITE_AFTER_READ",
"depth aspect during store with storeOp VK_ATTACHMENT_STORE_OP_STORE. Access info (usage: "
"SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage: "
"SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, read_barriers: VK_PIPELINE_STAGE_2_NONE_KHR, "},
{"SYNC-HAZARD-WRITE_AFTER_READ",
"stencil aspect during store with stencilStoreOp VK_ATTACHMENT_STORE_OP_STORE. Access info "
"(usage: SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage: "
"SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, read_barriers: VK_PIPELINE_STAGE_2_NONE_KHR, "},
};
namespace dawn_native { namespace vulkan {
namespace {
@ -63,14 +88,26 @@ namespace dawn_native { namespace vulkan {
#endif // defined(DAWN_ENABLE_SWIFTSHADER)
};
// Suppress validation errors that are known. Returns false in that case.
bool ShouldReportDebugMessage(const char* messageId, const char* message) {
for (const SkippedMessage& msg : kSkippedMessages) {
if (strstr(messageId, msg.messageId) != nullptr &&
strstr(message, msg.messageContents) != nullptr) {
return false;
}
}
return true;
}
VKAPI_ATTR VkBool32 VKAPI_CALL
OnDebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT /* messageTypes */,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* /* pUserData */) {
dawn::WarningLog() << pCallbackData->pMessage;
ASSERT((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) == 0);
if (ShouldReportDebugMessage(pCallbackData->pMessageIdName, pCallbackData->pMessage)) {
dawn::WarningLog() << pCallbackData->pMessage;
ASSERT((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) == 0);
}
return VK_FALSE;
}

View File

@ -217,10 +217,11 @@ namespace dawn_native { namespace vulkan {
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
const auto& attachmentInfo = renderPass->depthStencilAttachment;
query.SetDepthStencil(attachmentInfo.view->GetTexture()->GetFormat().format,
attachmentInfo.depthLoadOp, attachmentInfo.depthStoreOp,
attachmentInfo.stencilLoadOp,
attachmentInfo.stencilStoreOp);
query.SetDepthStencil(
attachmentInfo.view->GetTexture()->GetFormat().format,
attachmentInfo.depthLoadOp, attachmentInfo.depthStoreOp,
attachmentInfo.stencilLoadOp, attachmentInfo.stencilStoreOp,
attachmentInfo.depthReadOnly || attachmentInfo.stencilReadOnly);
}
query.SetSampleCount(renderPass->attachmentState->GetSampleCount());

View File

@ -34,6 +34,8 @@ namespace dawn_native { namespace vulkan {
}
VkAttachmentStoreOp VulkanAttachmentStoreOp(wgpu::StoreOp op) {
// TODO(crbug.com/dawn/485): return STORE_OP_STORE_NONE_QCOM if the device has required
// extension.
switch (op) {
case wgpu::StoreOp::Store:
return VK_ATTACHMENT_STORE_OP_STORE;
@ -62,13 +64,15 @@ namespace dawn_native { namespace vulkan {
wgpu::LoadOp depthLoadOpIn,
wgpu::StoreOp depthStoreOpIn,
wgpu::LoadOp stencilLoadOpIn,
wgpu::StoreOp stencilStoreOpIn) {
wgpu::StoreOp stencilStoreOpIn,
bool readOnly) {
hasDepthStencil = true;
depthStencilFormat = format;
depthLoadOp = depthLoadOpIn;
depthStoreOp = depthStoreOpIn;
stencilLoadOp = stencilLoadOpIn;
stencilStoreOp = stencilStoreOpIn;
readOnlyDepthStencil = readOnly;
}
void RenderPassCacheQuery::SetSampleCount(uint32_t sampleCount) {
@ -144,17 +148,23 @@ namespace dawn_native { namespace vulkan {
depthStencilAttachment = &depthStencilAttachmentRef;
depthStencilAttachmentRef.attachment = attachmentCount;
depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthStencilAttachmentRef.layout =
query.readOnlyDepthStencil ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
: VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachmentDesc.flags = 0;
attachmentDesc.format = VulkanImageFormat(mDevice, query.depthStencilFormat);
attachmentDesc.samples = vkSampleCount;
attachmentDesc.loadOp = VulkanAttachmentLoadOp(query.depthLoadOp);
attachmentDesc.storeOp = VulkanAttachmentStoreOp(query.depthStoreOp);
attachmentDesc.stencilLoadOp = VulkanAttachmentLoadOp(query.stencilLoadOp);
attachmentDesc.stencilStoreOp = VulkanAttachmentStoreOp(query.stencilStoreOp);
attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// There is only one subpass, so it is safe to set both initialLayout and finalLayout to
// the only subpass's layout.
attachmentDesc.initialLayout = depthStencilAttachmentRef.layout;
attachmentDesc.finalLayout = depthStencilAttachmentRef.layout;
++attachmentCount;
}
@ -235,7 +245,8 @@ namespace dawn_native { namespace vulkan {
HashCombine(&hash, query.hasDepthStencil);
if (query.hasDepthStencil) {
HashCombine(&hash, query.depthStencilFormat, query.depthLoadOp, query.stencilLoadOp);
HashCombine(&hash, query.depthStencilFormat, query.depthLoadOp, query.stencilLoadOp,
query.readOnlyDepthStencil);
}
HashCombine(&hash, query.sampleCount);
@ -270,7 +281,8 @@ namespace dawn_native { namespace vulkan {
if (a.hasDepthStencil) {
if ((a.depthStencilFormat != b.depthStencilFormat) ||
(a.depthLoadOp != b.depthLoadOp) || (a.stencilLoadOp != b.stencilLoadOp)) {
(a.depthLoadOp != b.depthLoadOp) || (a.stencilLoadOp != b.stencilLoadOp) ||
(a.readOnlyDepthStencil != b.readOnlyDepthStencil)) {
return false;
}
}

View File

@ -47,7 +47,8 @@ namespace dawn_native { namespace vulkan {
wgpu::LoadOp depthLoadOp,
wgpu::StoreOp depthStoreOp,
wgpu::LoadOp stencilLoadOp,
wgpu::StoreOp stencilStoreOp);
wgpu::StoreOp stencilStoreOp,
bool readOnly);
void SetSampleCount(uint32_t sampleCount);
ityp::bitset<ColorAttachmentIndex, kMaxColorAttachments> colorMask;
@ -62,6 +63,7 @@ namespace dawn_native { namespace vulkan {
wgpu::StoreOp depthStoreOp;
wgpu::LoadOp stencilLoadOp;
wgpu::StoreOp stencilStoreOp;
bool readOnlyDepthStencil;
uint32_t sampleCount;
};

View File

@ -497,7 +497,9 @@ namespace dawn_native { namespace vulkan {
dynamic.pDynamicStates = dynamicStates;
// Get a VkRenderPass that matches the attachment formats for this pipeline, load/store ops
// don't matter so set them all to LoadOp::Load / StoreOp::Store
// don't matter so set them all to LoadOp::Load / StoreOp::Store. Whether the render pass
// has resolve target and whether depth/stencil attachment is read-only also don't matter,
// so set them both to false.
VkRenderPass renderPass = VK_NULL_HANDLE;
{
RenderPassCacheQuery query;
@ -510,7 +512,7 @@ namespace dawn_native { namespace vulkan {
if (HasDepthStencilAttachment()) {
query.SetDepthStencil(GetDepthStencilFormat(), wgpu::LoadOp::Load,
wgpu::StoreOp::Store, wgpu::LoadOp::Load,
wgpu::StoreOp::Store);
wgpu::StoreOp::Store, false);
}
query.SetSampleCount(GetSampleCount());

View File

@ -83,6 +83,9 @@ namespace dawn_native { namespace vulkan {
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
}
}
if (usage & kReadOnlyRenderAttachment) {
flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
}
if (usage & kPresentTextureUsage) {
// The present usage is only used internally by the swapchain and is never used in
// combination with other usages.
@ -129,7 +132,7 @@ namespace dawn_native { namespace vulkan {
flags |=
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
}
if (usage & wgpu::TextureUsage::RenderAttachment) {
if (usage & (wgpu::TextureUsage::RenderAttachment | kReadOnlyRenderAttachment)) {
if (format.HasDepthOrStencil()) {
flags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
@ -442,6 +445,12 @@ namespace dawn_native { namespace vulkan {
}
if (usage & wgpu::TextureUsage::TextureBinding) {
flags |= VK_IMAGE_USAGE_SAMPLED_BIT;
// If the sampled texture is a depth/stencil texture, its image layout will be set
// to DEPTH_STENCIL_READ_ONLY_OPTIMAL in order to support readonly depth/stencil
// attachment. That layout requires DEPTH_STENCIL_ATTACHMENT_BIT image usage.
if (format.HasDepthOrStencil() && format.isRenderable) {
flags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
}
if (usage & wgpu::TextureUsage::StorageBinding) {
flags |= VK_IMAGE_USAGE_STORAGE_BIT;
@ -453,6 +462,9 @@ namespace dawn_native { namespace vulkan {
flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
}
if (usage & kReadOnlyRenderAttachment) {
flags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
return flags;
}
@ -466,10 +478,17 @@ namespace dawn_native { namespace vulkan {
}
if (!wgpu::HasZeroOrOneBits(usage)) {
// Sampled | ReadOnlyStorage is the only possible multi-bit usage, if more appear we
// might need additional special-casing.
ASSERT(usage == wgpu::TextureUsage::TextureBinding);
return VK_IMAGE_LAYOUT_GENERAL;
// Sampled | kReadOnlyRenderAttachment is the only possible multi-bit usage, if more
// appear we might need additional special-casing.
ASSERT(usage == (wgpu::TextureUsage::TextureBinding | kReadOnlyRenderAttachment));
// WebGPU requires both aspects to be readonly if the attachment's format does have
// both depth and stencil aspects. Vulkan 1.0 supports readonly for both aspects too
// via DEPTH_STENCIL_READ_ONLY image layout. Vulkan 1.1 and above can support separate
// readonly for a single aspect via DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL and
// DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL layouts. But Vulkan 1.0 cannot support
// it, and WebGPU doesn't need that currently.
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
}
// Usage has a single bit so we can switch on its value directly.
@ -477,17 +496,23 @@ namespace dawn_native { namespace vulkan {
case wgpu::TextureUsage::CopyDst:
return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
// A texture that's sampled and storage may be used as both usages in the same pass.
// When that happens, the layout must be GENERAL because that's a requirement for
// the storage usage. We can't know at bindgroup creation time if that case will
// happen so we must prepare for the pessimistic case and always use the GENERAL
// layout.
// The layout returned here is the one that will be used at bindgroup creation time.
// The bindgrpup's layout must match the runtime layout of the image when it is
// used via the bindgroup, but we don't know exactly what it will be yet. So we
// have to prepare for the pessimistic case.
case wgpu::TextureUsage::TextureBinding:
// Only VK_IMAGE_LAYOUT_GENERAL can do sampling and storage access of texture at the
// same time.
if (texture->GetInternalUsage() & wgpu::TextureUsage::StorageBinding) {
return VK_IMAGE_LAYOUT_GENERAL;
} else {
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
// The sampled image can be used as a readonly depth/stencil attachment at the same
// time if it is a depth/stencil renderable format, so the image layout need to be
// VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL.
if (texture->GetFormat().HasDepthOrStencil() && texture->GetFormat().isRenderable) {
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
}
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Vulkan texture copy functions require the image to be in _one_ known layout.
// Depending on whether parts of the texture have been transitioned to only CopySrc

View File

@ -254,10 +254,10 @@ TEST_P(ReadOnlyStencilAttachmentTests, Test) {
}
DAWN_INSTANTIATE_TEST_P(ReadOnlyDepthAttachmentTests,
{D3D12Backend()},
{D3D12Backend(), VulkanBackend()},
std::vector<wgpu::TextureFormat>(utils::kDepthFormats.begin(),
utils::kDepthFormats.end()));
DAWN_INSTANTIATE_TEST_P(ReadOnlyStencilAttachmentTests,
{D3D12Backend()},
{D3D12Backend(), VulkanBackend()},
std::vector<wgpu::TextureFormat>(utils::kStencilFormats.begin(),
utils::kStencilFormats.end()));