Vulkan: Add support for surface multisampling
A multisample image is created for the surface if multisampling is
enabled. Prior to present, this multisample image is resolved into the
swapchain image.
FramebufferVk::readPixelsImpl similarly has got the ability to resolve
the region of interest into a temporary image prior to readback.
Tests are added to render a point, line and a triangle on a 4x
multisampled surface.
Bug: angleproject:3204
Change-Id: I34aca502fa1918b5cbf000ff11521c350372e051
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1610188
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
index cbd13ee..b563024 100644
--- a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
@@ -1169,6 +1169,8 @@
{
TRACE_EVENT0("gpu.angle", "FramebufferVk::readPixelsImpl");
+ RendererVk *renderer = contextVk->getRenderer();
+
ANGLE_TRY(renderTarget->ensureImageInitialized(contextVk));
vk::CommandBuffer *commandBuffer = nullptr;
@@ -1186,6 +1188,56 @@
readFormat = &GetDepthStencilImageToBufferFormat(*readFormat, copyAspectFlags);
}
+ size_t level = renderTarget->getLevelIndex();
+ size_t layer = renderTarget->getLayerIndex();
+ VkOffset3D srcOffset = {area.x, area.y, 0};
+ VkExtent3D srcExtent = {static_cast<uint32_t>(area.width), static_cast<uint32_t>(area.height),
+ 1};
+
+ // If the source image is multisampled, we need to resolve it into a temporary image before
+ // performing a readback.
+ bool isMultisampled = srcImage->getSamples() > 1;
+ vk::Scoped<vk::ImageHelper> resolvedImage(contextVk->getDevice());
+ if (isMultisampled)
+ {
+ ANGLE_TRY(resolvedImage.get().init2DStaging(
+ contextVk, renderer->getMemoryProperties(), gl::Extents(area.width, area.height, 1),
+ srcImage->getFormat(),
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, 1));
+ resolvedImage.get().updateQueueSerial(renderer->getCurrentQueueSerial());
+
+ // TODO(syoussefi): resolve only works on color images (not depth/stencil). If readback
+ // on multisampled depth/stencil image is done, we would need a different path. One
+ // possible solution would be a compute shader that directly reads from the multisampled
+ // image, performs the resolve and outputs to the buffer in one go.
+ // http://anglebug.com/3200
+ ASSERT(copyAspectFlags == VK_IMAGE_ASPECT_COLOR_BIT);
+
+ VkImageResolve resolveRegion = {};
+ resolveRegion.srcSubresource.aspectMask = copyAspectFlags;
+ resolveRegion.srcSubresource.mipLevel = level;
+ resolveRegion.srcSubresource.baseArrayLayer = layer;
+ resolveRegion.srcSubresource.layerCount = 1;
+ resolveRegion.srcOffset = srcOffset;
+ resolveRegion.dstSubresource.aspectMask = copyAspectFlags;
+ resolveRegion.dstSubresource.mipLevel = 0;
+ resolveRegion.dstSubresource.baseArrayLayer = 0;
+ resolveRegion.dstSubresource.layerCount = 1;
+ resolveRegion.dstOffset = {};
+ resolveRegion.extent = srcExtent;
+
+ srcImage->resolve(&resolvedImage.get(), resolveRegion, commandBuffer);
+
+ resolvedImage.get().changeLayout(copyAspectFlags, vk::ImageLayout::TransferSrc,
+ commandBuffer);
+
+ // Make the resolved image the target of buffer copy.
+ srcImage = &resolvedImage.get();
+ level = 0;
+ layer = 0;
+ srcOffset = {0, 0, 0};
+ }
+
VkBuffer bufferHandle = VK_NULL_HANDLE;
uint8_t *readPixelBuffer = nullptr;
VkDeviceSize stagingOffset = 0;
@@ -1195,19 +1247,15 @@
&stagingOffset, nullptr));
VkBufferImageCopy region = {};
- region.bufferImageHeight = area.height;
+ region.bufferImageHeight = srcExtent.height;
region.bufferOffset = stagingOffset;
- region.bufferRowLength = area.width;
- region.imageExtent.width = area.width;
- region.imageExtent.height = area.height;
- region.imageExtent.depth = 1;
- region.imageOffset.x = area.x;
- region.imageOffset.y = area.y;
- region.imageOffset.z = 0;
+ region.bufferRowLength = srcExtent.width;
+ region.imageExtent = srcExtent;
+ region.imageOffset = srcOffset;
region.imageSubresource.aspectMask = copyAspectFlags;
- region.imageSubresource.baseArrayLayer = renderTarget->getLayerIndex();
+ region.imageSubresource.baseArrayLayer = layer;
region.imageSubresource.layerCount = 1;
- region.imageSubresource.mipLevel = renderTarget->getLevelIndex();
+ region.imageSubresource.mipLevel = level;
commandBuffer->copyImageToBuffer(srcImage->getImage(), srcImage->getCurrentLayout(),
bufferHandle, 1, ®ion);