Vulkan: add blit support for depth and MRT.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
Reference in New Issue
Block a user