Implement VK_KHR_depth_stencil_resolve
This extension allows users to pass depthstencil attachments that
vkCmdResolveImage will resolve to in addition to its other resolve
operations. Only the mandatory resolve modes
"VK_RESOLVE_MODE_SAMPLE_ZERO_BIT" and "VK_RESOLVE_MODE_NONE" are
supported.
It's trivial to support both independent resolve modes since we must
resolve depth and stencil attachments separately due to how depth and
stencil attachments are stored internally.
Change-Id: I0f8ff7cddca5f5acbac1d991b11f0a4447956784
Bug: b/167558951
Test: dEQP-VK.renderpass2.depth_stencil_resolve.*
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/48808
Tested-by: Sean Risser <srisser@google.com>
Commit-Queue: Sean Risser <srisser@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/Device/Blitter.cpp b/src/Device/Blitter.cpp
index f709c31..4926469 100644
--- a/src/Device/Blitter.cpp
+++ b/src/Device/Blitter.cpp
@@ -22,6 +22,7 @@
#include "System/Memory.hpp"
#include "Vulkan/VkBuffer.hpp"
#include "Vulkan/VkImage.hpp"
+#include "Vulkan/VkImageView.hpp"
#include <utility>
@@ -1880,6 +1881,88 @@
dst->contentsChanged(dstSubresRange);
}
+static void resolveDepth(const vk::ImageView *src, vk::ImageView *dst, const VkSubpassDescriptionDepthStencilResolve &dsrDesc)
+{
+ if(dsrDesc.depthResolveMode == VK_RESOLVE_MODE_NONE)
+ {
+ return;
+ }
+
+ vk::Format format = src->getFormat(VK_IMAGE_ASPECT_DEPTH_BIT);
+ VkExtent2D extent = src->getMipLevelExtent(0, VK_IMAGE_ASPECT_DEPTH_BIT);
+ int width = extent.width;
+ int height = extent.height;
+ int pitch = src->rowPitchBytes(VK_IMAGE_ASPECT_DEPTH_BIT, 0);
+
+ // To support other resolve modes, get the slice bytes and get a pointer to each sample plane.
+ // Then modify the loop below to include logic for handling each new mode.
+ uint8_t *source = (uint8_t *)src->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0);
+ uint8_t *dest = (uint8_t *)dst->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0);
+
+ size_t formatSize = format.bytes();
+ // TODO(b/167558951) support other resolve modes.
+ ASSERT(dsrDesc.depthResolveMode == VK_RESOLVE_MODE_SAMPLE_ZERO_BIT);
+ for(int y = 0; y < height; y++)
+ {
+ memcpy(dest, source, formatSize * width);
+
+ source += pitch;
+ dest += pitch;
+ }
+
+ dst->contentsChanged();
+}
+
+static void resolveStencil(const vk::ImageView *src, vk::ImageView *dst, const VkSubpassDescriptionDepthStencilResolve &dsrDesc)
+{
+ if(dsrDesc.stencilResolveMode == VK_RESOLVE_MODE_NONE)
+ {
+ return;
+ }
+
+ VkExtent2D extent = src->getMipLevelExtent(0, VK_IMAGE_ASPECT_STENCIL_BIT);
+ int width = extent.width;
+ int height = extent.height;
+ int pitch = src->rowPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
+
+ // To support other resolve modes, use src->slicePitchBytes() and get a pointer to each sample's slice.
+ // Then modify the loop below to include logic for handling each new mode.
+ uint8_t *source = reinterpret_cast<uint8_t *>(src->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0));
+ uint8_t *dest = reinterpret_cast<uint8_t *>(dst->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0));
+
+ // TODO(b/167558951) support other resolve modes.
+ ASSERT(dsrDesc.stencilResolveMode == VK_RESOLVE_MODE_SAMPLE_ZERO_BIT);
+ for(int y = 0; y < height; y++)
+ {
+ // Stencil is always 8 bits, so the width of the resource we're resolving is
+ // the number of bytes in each row we need to copy during for SAMPLE_ZERO
+ memcpy(dest, source, width);
+
+ source += pitch;
+ dest += pitch;
+ }
+
+ dst->contentsChanged();
+}
+
+void Blitter::resolveDepthStencil(const vk::ImageView *src, vk::ImageView *dst, const VkSubpassDescriptionDepthStencilResolve &dsrDesc)
+{
+ VkImageSubresourceRange srcRange = src->getSubresourceRange();
+ VkImageSubresourceRange dstRange = src->getSubresourceRange();
+ ASSERT(src->getFormat() == dst->getFormat());
+ ASSERT(srcRange.layerCount == 1 && dstRange.layerCount == 1);
+ ASSERT(srcRange.aspectMask == dstRange.aspectMask);
+
+ if(srcRange.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT)
+ {
+ resolveDepth(src, dst, dsrDesc);
+ }
+ if(srcRange.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)
+ {
+ resolveStencil(src, dst, dsrDesc);
+ }
+}
+
void Blitter::resolve(const vk::Image *src, vk::Image *dst, VkImageResolve region)
{
// "The aspectMask member of srcSubresource and dstSubresource must only contain VK_IMAGE_ASPECT_COLOR_BIT"
diff --git a/src/Device/Blitter.hpp b/src/Device/Blitter.hpp
index 51b280d..b9fc7c7 100644
--- a/src/Device/Blitter.hpp
+++ b/src/Device/Blitter.hpp
@@ -28,6 +28,7 @@
namespace vk {
class Image;
+class ImageView;
class Buffer;
} // namespace vk
@@ -145,6 +146,7 @@
void blit(const vk::Image *src, vk::Image *dst, VkImageBlit region, VkFilter filter);
void resolve(const vk::Image *src, vk::Image *dst, VkImageResolve region);
+ void resolveDepthStencil(const vk::ImageView *src, vk::ImageView *dst, const VkSubpassDescriptionDepthStencilResolve &dsrDesc);
void copy(const vk::Image *src, uint8_t *dst, unsigned int dstPitch);
void updateBorders(vk::Image *image, const VkImageSubresource &subresource);
diff --git a/src/Vulkan/VkFramebuffer.cpp b/src/Vulkan/VkFramebuffer.cpp
index 1ed2642..4f6a088 100644
--- a/src/Vulkan/VkFramebuffer.cpp
+++ b/src/Vulkan/VkFramebuffer.cpp
@@ -183,6 +183,17 @@
}
}
}
+
+ if(renderPass->hasDepthStencilResolve() && subpass.pDepthStencilAttachment != nullptr)
+ {
+ VkSubpassDescriptionDepthStencilResolve dsResolve = renderPass->getSubpassDepthStencilResolve(subpassIndex);
+ uint32_t depthStencilAttachment = subpass.pDepthStencilAttachment->attachment;
+ if(depthStencilAttachment != VK_ATTACHMENT_UNUSED)
+ {
+ ImageView *imageView = attachments[depthStencilAttachment];
+ imageView->resolveDepthStencil(attachments[dsResolve.pDepthStencilResolveAttachment->attachment], dsResolve);
+ }
+ }
}
size_t Framebuffer::ComputeRequiredAllocationSize(const VkFramebufferCreateInfo *pCreateInfo)
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 53150d5..a0287cc 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -17,6 +17,7 @@
#include "VkBuffer.hpp"
#include "VkDevice.hpp"
#include "VkDeviceMemory.hpp"
+#include "VkImageView.hpp"
#include "VkStringify.hpp"
#include "Device/ASTC_Decoder.hpp"
#include "Device/BC_Decoder.hpp"
@@ -992,6 +993,11 @@
device->getBlitter()->resolve(this, dstImage, region);
}
+void Image::resolveDepthStencilTo(const ImageView *src, ImageView *dst, const VkSubpassDescriptionDepthStencilResolve &dsResolve) const
+{
+ device->getBlitter()->resolveDepthStencil(src, dst, dsResolve);
+}
+
VkFormat Image::getClearFormat() const
{
// Set the proper format for the clear value, as described here:
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 6869d53..9274191 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -31,6 +31,7 @@
class Buffer;
class Device;
class DeviceMemory;
+class ImageView;
#ifdef __ANDROID__
struct BackingMemory
@@ -65,6 +66,7 @@
void blitTo(Image *dstImage, const VkImageBlit ®ion, VkFilter filter) const;
void copyTo(uint8_t *dst, unsigned int dstPitch) const;
void resolveTo(Image *dstImage, const VkImageResolve ®ion) const;
+ void resolveDepthStencilTo(const ImageView *src, ImageView *dst, const VkSubpassDescriptionDepthStencilResolve &depthStencilResolve) const;
void clear(const VkClearValue &clearValue, const vk::Format &viewFormat, const VkRect2D &renderArea, const VkImageSubresourceRange &subresourceRange);
void clear(const VkClearColorValue &color, const VkImageSubresourceRange &subresourceRange);
void clear(const VkClearDepthStencilValue &color, const VkImageSubresourceRange &subresourceRange);
diff --git a/src/Vulkan/VkImageView.cpp b/src/Vulkan/VkImageView.cpp
index b58b35d..e629c77 100644
--- a/src/Vulkan/VkImageView.cpp
+++ b/src/Vulkan/VkImageView.cpp
@@ -262,6 +262,17 @@
}
}
+void ImageView::resolveDepthStencil(ImageView *resolveAttachment, const VkSubpassDescriptionDepthStencilResolve &dsResolve)
+{
+ ASSERT(subresourceRange.levelCount == 1 && resolveAttachment->subresourceRange.levelCount == 1);
+ if((subresourceRange.layerCount != 1) || (resolveAttachment->subresourceRange.layerCount != 1))
+ {
+ UNIMPLEMENTED("b/148242443: layerCount != 1"); // FIXME(b/148242443)
+ }
+
+ image->resolveDepthStencilTo(this, resolveAttachment, dsResolve);
+}
+
const Image *ImageView::getImage(Usage usage) const
{
switch(usage)
@@ -310,6 +321,13 @@
return { extent.width, extent.height };
}
+VkExtent2D ImageView::getMipLevelExtent(uint32_t mipLevel, VkImageAspectFlagBits aspect) const
+{
+ VkExtent3D extent = image->getMipLevelExtent(aspect, subresourceRange.baseMipLevel + mipLevel);
+
+ return { extent.width, extent.height };
+}
+
int ImageView::getDepthOrLayerCount(uint32_t mipLevel) const
{
VkExtent3D extent = image->getMipLevelExtent(static_cast<VkImageAspectFlagBits>(subresourceRange.aspectMask),
diff --git a/src/Vulkan/VkImageView.hpp b/src/Vulkan/VkImageView.hpp
index bba9a37..5bf6b50 100644
--- a/src/Vulkan/VkImageView.hpp
+++ b/src/Vulkan/VkImageView.hpp
@@ -79,6 +79,7 @@
void resolve(ImageView *resolveAttachment);
void resolve(ImageView *resolveAttachment, int layer);
void resolveWithLayerMask(ImageView *resolveAttachment, uint32_t layerMask);
+ void resolveDepthStencil(ImageView *resolveAttachment, const VkSubpassDescriptionDepthStencilResolve &dsResolve);
VkImageViewType getType() const { return viewType; }
Format getFormat(Usage usage = RAW) const;
@@ -88,6 +89,7 @@
int getMipLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel, Usage usage = RAW) const;
int layerPitchBytes(VkImageAspectFlagBits aspect, Usage usage = RAW) const;
VkExtent2D getMipLevelExtent(uint32_t mipLevel) const;
+ VkExtent2D getMipLevelExtent(uint32_t mipLevel, VkImageAspectFlagBits aspect) const;
int getDepthOrLayerCount(uint32_t mipLevel) const;
int getSampleCount() const
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index cacc782..9b76407 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -838,8 +838,8 @@
{
properties->supportedDepthResolveModes = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT | VK_RESOLVE_MODE_NONE;
properties->supportedStencilResolveModes = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT | VK_RESOLVE_MODE_NONE;
- properties->independentResolveNone = VK_FALSE;
- properties->independentResolve = VK_FALSE;
+ properties->independentResolveNone = VK_TRUE;
+ properties->independentResolve = VK_TRUE;
}
void PhysicalDevice::getProperties(VkPhysicalDeviceDepthStencilResolveProperties *properties) const
diff --git a/src/Vulkan/VkRenderPass.cpp b/src/Vulkan/VkRenderPass.cpp
index 0830709..dab3d51 100644
--- a/src/Vulkan/VkRenderPass.cpp
+++ b/src/Vulkan/VkRenderPass.cpp
@@ -129,7 +129,7 @@
, subpassCount(pCreateInfo->subpassCount)
, dependencyCount(pCreateInfo->dependencyCount)
{
- init(pCreateInfo, mem);
+ init(pCreateInfo, &mem);
}
RenderPass::RenderPass(const VkRenderPassCreateInfo2KHR *pCreateInfo, void *mem)
@@ -137,18 +137,76 @@
, subpassCount(pCreateInfo->subpassCount)
, dependencyCount(pCreateInfo->dependencyCount)
{
- init(pCreateInfo, mem);
+ init(pCreateInfo, &mem);
// Note: the init function above ignores:
// - pCorrelatedViewMasks: This provides a potential performance optimization
// - VkAttachmentReference2::aspectMask : This specifies which aspects may be used
// - VkSubpassDependency2::viewOffset : This is the same as VkRenderPassMultiviewCreateInfo::pViewOffsets, which is currently ignored
// - Any pNext pointer in VkRenderPassCreateInfo2KHR's internal structures
+
+ char *hostMemory = reinterpret_cast<char *>(mem);
+
+ // Handle the extensions in each subpass
+ for(uint32_t i = 0; i < subpassCount; i++)
+ {
+ auto const &subpass = pCreateInfo->pSubpasses[i];
+ const VkBaseInStructure *extension = reinterpret_cast<const VkBaseInStructure *>(subpass.pNext);
+ while(extension)
+ {
+ switch(extension->sType)
+ {
+ case VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE:
+ {
+ const auto *ext = reinterpret_cast<const VkSubpassDescriptionDepthStencilResolve *>(extension);
+ // If any subpass includes depthStencilResolve, allocate a DSR struct for each subpass
+ // This allows us to index into subpassDepthStencilResolves using the subpass index.
+ if(ext->pDepthStencilResolveAttachment != nullptr && ext->pDepthStencilResolveAttachment->attachment != VK_ATTACHMENT_UNUSED)
+ {
+ if(subpassDepthStencilResolves == nullptr)
+ {
+ subpassDepthStencilResolves = reinterpret_cast<VkSubpassDescriptionDepthStencilResolve *>(hostMemory);
+ hostMemory += subpassCount * sizeof(VkSubpassDescriptionDepthStencilResolve);
+ for(uint32_t subpass = 0; subpass < subpassCount; subpass++)
+ {
+ subpassDepthStencilResolves[subpass].sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE;
+ subpassDepthStencilResolves[subpass].pNext = nullptr;
+ subpassDepthStencilResolves[subpass].depthResolveMode = VK_RESOLVE_MODE_NONE;
+ subpassDepthStencilResolves[subpass].stencilResolveMode = VK_RESOLVE_MODE_NONE;
+ subpassDepthStencilResolves[subpass].pDepthStencilResolveAttachment = nullptr;
+ }
+ }
+
+ VkAttachmentReference2 *reference = reinterpret_cast<VkAttachmentReference2 *>(hostMemory);
+ hostMemory += sizeof(VkAttachmentReference2);
+
+ subpassDepthStencilResolves[i].depthResolveMode = ext->depthResolveMode;
+ subpassDepthStencilResolves[i].stencilResolveMode = ext->stencilResolveMode;
+ reference->pNext = nullptr;
+ reference->sType = ext->pDepthStencilResolveAttachment->sType;
+ reference->attachment = ext->pDepthStencilResolveAttachment->attachment;
+ reference->layout = ext->pDepthStencilResolveAttachment->layout;
+ reference->aspectMask = ext->pDepthStencilResolveAttachment->aspectMask;
+ subpassDepthStencilResolves[i].pDepthStencilResolveAttachment = reinterpret_cast<const VkAttachmentReference2 *>(reference);
+
+ MarkFirstUse(reference->attachment, i);
+ }
+ }
+ break;
+ default:
+ LOG_TRAP("VkRenderPassCreateInfo2KHR->subpass[%d]->pNext sType: %s",
+ i, vk::Stringify(extension->sType).c_str());
+ break;
+ }
+
+ extension = extension->pNext;
+ }
+ }
}
template<class T>
-void RenderPass::init(const T *pCreateInfo, void *mem)
+void RenderPass::init(const T *pCreateInfo, void **mem)
{
- char *hostMemory = reinterpret_cast<char *>(mem);
+ char *hostMemory = reinterpret_cast<char *>(*mem);
// subpassCount must be greater than 0
ASSERT(pCreateInfo->subpassCount > 0);
@@ -300,7 +358,9 @@
{
dependencies = reinterpret_cast<VkSubpassDependency *>(hostMemory);
CopySubpassDependencies(dependencies, pCreateInfo->pDependencies, pCreateInfo->dependencyCount);
+ hostMemory += dependencyCount * sizeof(VkSubpassDependency);
}
+ *mem = hostMemory;
}
void RenderPass::destroy(const VkAllocationCallbacks *pAllocator)
@@ -315,7 +375,46 @@
size_t RenderPass::ComputeRequiredAllocationSize(const VkRenderPassCreateInfo2KHR *pCreateInfo)
{
- return ComputeRequiredAllocationSizeT(pCreateInfo);
+ size_t requiredMemory = ComputeRequiredAllocationSizeT(pCreateInfo);
+
+ // Calculate the memory required to handle depth stencil resolves
+ bool usesDSR = false;
+ for(uint32_t i = 0; i < pCreateInfo->subpassCount; i++)
+ {
+ auto const &subpass = pCreateInfo->pSubpasses[i];
+ const VkBaseInStructure *extension = reinterpret_cast<const VkBaseInStructure *>(subpass.pNext);
+ while(extension)
+ {
+ switch(extension->sType)
+ {
+ case VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE:
+ {
+ const auto *ext = reinterpret_cast<const VkSubpassDescriptionDepthStencilResolve *>(extension);
+ if(ext->pDepthStencilResolveAttachment != nullptr && ext->pDepthStencilResolveAttachment->attachment != VK_ATTACHMENT_UNUSED)
+ {
+ if(!usesDSR)
+ {
+ // If any subpass uses DSR, then allocate a VkSubpassDescriptionDepthStencilResolve
+ // for all subpasses. This allows us to index into our DSR structs using the subpass index.
+ requiredMemory += sizeof(VkSubpassDescriptionDepthStencilResolve) * pCreateInfo->subpassCount;
+ usesDSR = true;
+ }
+ // For each subpass that actually uses DSR, allocate a VkAttachmentReference2.
+ requiredMemory += sizeof(VkAttachmentReference2);
+ }
+ }
+ break;
+ default:
+ LOG_TRAP("VkRenderPassCreateInfo2KHR->subpass[%d]->pNext sType: %s",
+ i, vk::Stringify(extension->sType).c_str());
+ break;
+ }
+
+ extension = extension->pNext;
+ }
+ }
+
+ return requiredMemory;
}
void RenderPass::getRenderAreaGranularity(VkExtent2D *pGranularity) const
diff --git a/src/Vulkan/VkRenderPass.hpp b/src/Vulkan/VkRenderPass.hpp
index 3f02f6b..23a3ffa 100644
--- a/src/Vulkan/VkRenderPass.hpp
+++ b/src/Vulkan/VkRenderPass.hpp
@@ -53,6 +53,16 @@
return subpasses[subpassIndex];
}
+ bool hasDepthStencilResolve() const
+ {
+ return subpassDepthStencilResolves != nullptr;
+ }
+
+ VkSubpassDescriptionDepthStencilResolve getSubpassDepthStencilResolve(uint32_t subpassIndex) const
+ {
+ return subpassDepthStencilResolves[subpassIndex];
+ }
+
uint32_t getDependencyCount() const
{
return dependencyCount;
@@ -88,6 +98,7 @@
VkAttachmentDescription *attachments = nullptr;
uint32_t subpassCount = 0;
VkSubpassDescription *subpasses = nullptr;
+ VkSubpassDescriptionDepthStencilResolve *subpassDepthStencilResolves = nullptr;
uint32_t dependencyCount = 0;
VkSubpassDependency *dependencies = nullptr;
int *attachmentFirstUse = nullptr;
@@ -96,7 +107,7 @@
void MarkFirstUse(int attachment, int subpass);
template<class T>
- void init(const T *pCreateInfo, void *mem);
+ void init(const T *pCreateInfo, void **mem);
};
static inline RenderPass *Cast(VkRenderPass object)
@@ -106,4 +117,4 @@
} // namespace vk
-#endif // VK_RENDER_PASS_HPP_
\ No newline at end of file
+#endif // VK_RENDER_PASS_HPP_
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 36d8a21..e98d8d0 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -408,6 +408,7 @@
{ VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, VK_EXT_HOST_QUERY_RESET_SPEC_VERSION },
{ VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME, VK_EXT_SCALAR_BLOCK_LAYOUT_SPEC_VERSION },
{ VK_EXT_SEPARATE_STENCIL_USAGE_EXTENSION_NAME, VK_EXT_SEPARATE_STENCIL_USAGE_SPEC_VERSION },
+ { VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME, VK_KHR_DEPTH_STENCIL_RESOLVE_SPEC_VERSION },
{ VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION },
{ VK_KHR_IMAGELESS_FRAMEBUFFER_EXTENSION_NAME, VK_KHR_IMAGELESS_FRAMEBUFFER_SPEC_VERSION },
{ VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME, VK_KHR_SHADER_FLOAT_CONTROLS_SPEC_VERSION },