Vulkan: add blit support for depth and MRT.

This commit is contained in:
Philip Rideout
2020-10-29 17:03:24 -07:00
parent 6abef94d95
commit 4e4d4f9ee3
4 changed files with 146 additions and 81 deletions

View File

@@ -31,6 +31,7 @@
#pragma clang diagnostic pop
#include "VulkanContext.h"
#include "VulkanHandles.h"
#include "VulkanUtility.h"
#include <utils/Panic.h>
@@ -831,5 +832,122 @@ VkImageLayout getTextureLayout(TextureUsage usage) {
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
static void blit(VkImageAspectFlags aspect, VkFilter filter, VulkanContext* context,
const VulkanRenderTarget* srcTarget, VulkanAttachment src, VulkanAttachment dst,
const VkOffset3D srcRect[2], const VkOffset3D dstRect[2], VkCommandBuffer cmdbuffer) {
const VkImageBlit blitRegions[1] = {{
.srcSubresource = { aspect, src.level, src.layer, 1 },
.srcOffsets = { srcRect[0], srcRect[1] },
.dstSubresource = { aspect, dst.level, dst.layer, 1 },
.dstOffsets = { dstRect[0], dstRect[1] }
}};
const VkExtent2D srcExtent = srcTarget->getExtent();
const VkImageResolve resolveRegions[1] = {{
.srcSubresource = { aspect, src.level, src.layer, 1 },
.srcOffset = srcRect[0],
.dstSubresource = { aspect, dst.level, dst.layer, 1 },
.dstOffset = dstRect[0],
.extent = { srcExtent.width, srcExtent.height, 1 }
}};
VulkanTexture::transitionImageLayout(cmdbuffer, src.image, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, src.level, 1, 1, aspect);
VulkanTexture::transitionImageLayout(cmdbuffer, dst.image, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dst.level, 1, 1, aspect);
if (src.texture && src.texture->samples > 1 && dst.texture && dst.texture->samples == 1) {
vkCmdResolveImage(cmdbuffer, src.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, resolveRegions);
} else {
vkCmdBlitImage(cmdbuffer, src.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blitRegions, filter);
}
if (src.texture) {
VulkanTexture::transitionImageLayout(cmdbuffer, src.image, VK_IMAGE_LAYOUT_UNDEFINED,
getTextureLayout(src.texture->usage), src.level, 1, 1, aspect);
} else if (!context->currentSurface->headlessQueue) {
VulkanTexture::transitionImageLayout(cmdbuffer, src.image, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, src.level, 1, 1, aspect);
}
// Determine the desired texture layout for the destination while ensuring that the default
// render target is supported, which has no associated texture.
const VkImageLayout desiredLayout = dst.texture ? getTextureLayout(dst.texture->usage) :
getSwapContext(*context).attachment.layout;
VulkanTexture::transitionImageLayout(cmdbuffer, dst.image, VK_IMAGE_LAYOUT_UNDEFINED,
desiredLayout, dst.level, 1, 1, aspect);
}
void blitDepth(VulkanContext* context, const VulkanRenderTarget* dstTarget,
const VkOffset3D dstRect[2], const VulkanRenderTarget* srcTarget,
const VkOffset3D srcRect[2]) {
const VulkanAttachment src = srcTarget->getDepth();
const VulkanAttachment dst = dstTarget->getDepth();
const VkImageAspectFlags aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
// In debug builds, verify that the two render targets have blittable formats.
#ifndef NDEBUG
const VkPhysicalDevice gpu = context->physicalDevice;
VkFormatProperties info;
vkGetPhysicalDeviceFormatProperties(gpu, src.format, &info);
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT,
"Depth format is not blittable")) {
return;
}
vkGetPhysicalDeviceFormatProperties(gpu, dst.format, &info);
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT,
"Depth format is not blittable")) {
return;
}
#endif
if (!context->currentCommands) {
VkCommandBuffer cmdbuf = acquireWorkCommandBuffer(*context);
blit(aspect, VK_FILTER_NEAREST, context, srcTarget, src, dst, srcRect, dstRect, cmdbuf);
flushWorkCommandBuffer(*context);
} else {
VkCommandBuffer cmdbuf = context->currentCommands->cmdbuffer;
blit(aspect, VK_FILTER_NEAREST, context, srcTarget, src, dst, srcRect, dstRect, cmdbuf);
}
}
void blitColor(VulkanContext* context, const VulkanRenderTarget* dstTarget,
const VkOffset3D dstRect[2], const VulkanRenderTarget* srcTarget,
const VkOffset3D srcRect[2], VkFilter filter, int targetIndex) {
const VulkanAttachment src = srcTarget->getColor(targetIndex);
const VulkanAttachment dst = dstTarget->getColor(0);
const VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT;
// In debug builds, verify that the two render targets have blittable formats.
#ifndef NDEBUG
const VkPhysicalDevice gpu = context->physicalDevice;
VkFormatProperties info;
vkGetPhysicalDeviceFormatProperties(gpu, src.format, &info);
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT,
"Source format is not blittable")) {
return;
}
vkGetPhysicalDeviceFormatProperties(gpu, dst.format, &info);
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT,
"Destination format is not blittable")) {
return;
}
#endif
if (!context->currentCommands) {
VkCommandBuffer cmdbuf = acquireWorkCommandBuffer(*context);
blit(aspect, filter, context, srcTarget, src, dst, srcRect, dstRect, cmdbuf);
flushWorkCommandBuffer(*context);
} else {
VkCommandBuffer cmdbuf = context->currentCommands->cmdbuffer;
blit(aspect, filter, context, srcTarget, src, dst, srcRect, dstRect, cmdbuf);
}
}
} // namespace filament
} // namespace backend

View File

@@ -47,6 +47,7 @@ constexpr VkAllocationCallbacks* VKALLOC = nullptr;
constexpr static const int VK_REQUIRED_VERSION_MAJOR = 1;
constexpr static const int VK_REQUIRED_VERSION_MINOR = 0;
struct VulkanRenderTarget;
struct VulkanSurfaceContext;
struct VulkanTexture;
@@ -176,6 +177,14 @@ void flushWorkCommandBuffer(VulkanContext& context);
void createFinalDepthBuffer(VulkanContext& context, VulkanSurfaceContext& sc, VkFormat depthFormat);
VkImageLayout getTextureLayout(TextureUsage usage);
void blitDepth(VulkanContext* context, const VulkanRenderTarget* dstTarget,
const VkOffset3D dstRect[2], const VulkanRenderTarget* srcTarget,
const VkOffset3D srcRect[2]);
void blitColor(VulkanContext* context, const VulkanRenderTarget* dstTarget,
const VkOffset3D dstRect[2], const VulkanRenderTarget* srcTarget,
const VkOffset3D srcRect[2], VkFilter filter, int index);
} // namespace filament
} // namespace backend

View File

@@ -1461,104 +1461,41 @@ void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, V
Handle<HwRenderTarget> src, Viewport srcRect, SamplerMagFilter filter) {
VulkanRenderTarget* dstTarget = handle_cast<VulkanRenderTarget>(mHandleMap, dst);
VulkanRenderTarget* srcTarget = handle_cast<VulkanRenderTarget>(mHandleMap, src);
const int targetIndex = 0; // TODO: support MRT in blit
// In debug builds, verify that the two render targets have blittable formats.
#ifndef NDEBUG
const VkPhysicalDevice gpu = mContext.physicalDevice;
VkFormatProperties info;
vkGetPhysicalDeviceFormatProperties(gpu, srcTarget->getColor(targetIndex).format, &info);
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT,
"Source format is not blittable")) {
return;
}
vkGetPhysicalDeviceFormatProperties(gpu, dstTarget->getColor(targetIndex).format, &info);
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT,
"Destination format is not blittable")) {
return;
}
if (any(buffers & TargetBufferFlags::DEPTH)) {
utils::slog.w << "Depth blits are not yet supported." << utils::io::endl;
}
#endif
VkFilter vkfilter = filter == SamplerMagFilter::NEAREST ? VK_FILTER_NEAREST : VK_FILTER_LINEAR;
const VkExtent2D srcExtent = srcTarget->getExtent();
const VkExtent2D dstExtent = dstTarget->getExtent();
const int32_t srcLeft = std::min(srcRect.left, (int32_t) srcExtent.width);
const int32_t srcBottom = std::min(srcRect.bottom, (int32_t) srcExtent.height);
const int32_t srcRight = std::min(srcRect.left + srcRect.width, srcExtent.width);
const int32_t srcTop = std::min(srcRect.bottom + srcRect.height, srcExtent.height);
const uint32_t srcLevel = srcTarget->getColor(targetIndex).level;
const uint32_t srcLayer = srcTarget->getColor(targetIndex).layer;
const VkOffset3D srcOffsets[2] = { { srcLeft, srcBottom, 0 }, { srcRight, srcTop, 1 }};
const VkExtent2D dstExtent = dstTarget->getExtent();
const int32_t dstLeft = std::min(dstRect.left, (int32_t) dstExtent.width);
const int32_t dstBottom = std::min(dstRect.bottom, (int32_t) dstExtent.height);
const int32_t dstRight = std::min(dstRect.left + dstRect.width, dstExtent.width);
const int32_t dstTop = std::min(dstRect.bottom + dstRect.height, dstExtent.height);
const uint32_t dstLevel = dstTarget->getColor(targetIndex).level;
const uint32_t dstLayer = dstTarget->getColor(targetIndex).layer;
const VkOffset3D dstOffsets[2] = { { dstLeft, dstBottom, 0 }, { dstRight, dstTop, 1 }};
const VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT;
if (any(buffers & TargetBufferFlags::DEPTH) && srcTarget->hasDepth() && dstTarget->hasDepth()) {
blitDepth(&mContext, dstTarget, dstOffsets, srcTarget, srcOffsets);
}
const VkImageBlit blitRegions[1] = {{
.srcSubresource = { aspect, srcLevel, srcLayer, 1 },
.srcOffsets = { { srcLeft, srcBottom, 0 }, { srcRight, srcTop, 1 }},
.dstSubresource = { aspect, dstLevel, dstLayer, 1 },
.dstOffsets = { { dstLeft, dstBottom, 0 }, { dstRight, dstTop, 1 }}
}};
if (any(buffers & TargetBufferFlags::COLOR0)) {
blitColor(&mContext, dstTarget, dstOffsets, srcTarget, srcOffsets, vkfilter, 0);
}
const VkImageResolve resolveRegions[1] = {{
.srcSubresource = { aspect, srcLevel, srcLayer, 1 },
.srcOffset = { srcLeft, srcBottom, 0 },
.dstSubresource = { aspect, dstLevel, dstLayer, 1 },
.dstOffset = { dstLeft, dstBottom, 0 },
.extent = { srcExtent.width, srcExtent.height, 1 }
}};
if (any(buffers & TargetBufferFlags::COLOR1)) {
blitColor(&mContext, dstTarget, dstOffsets, srcTarget, srcOffsets, vkfilter, 1);
}
const VulkanTexture* srcTexture = srcTarget->getColor(targetIndex).texture;
const VulkanTexture* dstTexture = dstTarget->getColor(targetIndex).texture;
if (any(buffers & TargetBufferFlags::COLOR2)) {
blitColor(&mContext, dstTarget, dstOffsets, srcTarget, srcOffsets, vkfilter, 2);
}
auto vkblit = [=](VkCommandBuffer cmdbuffer) {
VkImage srcImage = srcTarget->getColor(targetIndex).image;
VulkanTexture::transitionImageLayout(cmdbuffer, srcImage, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, srcLevel, 1, 1, aspect);
VkImage dstImage = dstTarget->getColor(targetIndex).image;
VulkanTexture::transitionImageLayout(cmdbuffer, dstImage, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dstLevel, 1, 1, aspect);
if (srcTexture && srcTexture->samples > 1 && dstTexture && dstTexture->samples == 1) {
vkCmdResolveImage(cmdbuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, resolveRegions);
} else {
vkCmdBlitImage(cmdbuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blitRegions,
filter == SamplerMagFilter::NEAREST ? VK_FILTER_NEAREST : VK_FILTER_LINEAR);
}
if (srcTexture) {
VulkanTexture::transitionImageLayout(cmdbuffer, srcImage, VK_IMAGE_LAYOUT_UNDEFINED,
getTextureLayout(srcTexture->usage), srcLevel, 1, 1, aspect);
} else if (!mContext.currentSurface->headlessQueue) {
VulkanTexture::transitionImageLayout(cmdbuffer, srcImage, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, srcLevel, 1, 1, aspect);
}
// Determine the desired texture layout for the destination while ensuring that the default
// render target is supported, which has no associated texture.
const VkImageLayout desiredLayout = dstTexture ? getTextureLayout(dstTexture->usage) :
getSwapContext(mContext).attachment.layout;
VulkanTexture::transitionImageLayout(cmdbuffer, dstImage, VK_IMAGE_LAYOUT_UNDEFINED,
desiredLayout, dstLevel, 1, 1, aspect);
};
if (!mContext.currentCommands) {
vkblit(acquireWorkCommandBuffer(mContext));
flushWorkCommandBuffer(mContext);
} else {
vkblit(mContext.currentCommands->cmdbuffer);
if (any(buffers & TargetBufferFlags::COLOR3)) {
blitColor(&mContext, dstTarget, dstOffsets, srcTarget, srcOffsets, vkfilter, 3);
}
}

View File

@@ -61,6 +61,7 @@ struct VulkanRenderTarget : private HwRenderTarget {
int getColorTargetCount(const VulkanRenderPass& pass) const;
bool invalidate();
uint8_t getSamples() const { return mSamples; }
bool hasDepth() const { return mDepth.format != VK_FORMAT_UNDEFINED; }
private:
VulkanAttachment mColor[MRT::TARGET_COUNT] = {};
VulkanAttachment mDepth = {};