diff --git a/filament/backend/src/vulkan/VulkanContext.cpp b/filament/backend/src/vulkan/VulkanContext.cpp index 16fbc746e4..16fb685db7 100644 --- a/filament/backend/src/vulkan/VulkanContext.cpp +++ b/filament/backend/src/vulkan/VulkanContext.cpp @@ -31,6 +31,7 @@ #pragma clang diagnostic pop #include "VulkanContext.h" +#include "VulkanHandles.h" #include "VulkanUtility.h" #include @@ -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 diff --git a/filament/backend/src/vulkan/VulkanContext.h b/filament/backend/src/vulkan/VulkanContext.h index 0e011b0f35..34cca2daef 100644 --- a/filament/backend/src/vulkan/VulkanContext.h +++ b/filament/backend/src/vulkan/VulkanContext.h @@ -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 diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 3a81fc2b07..67a4233acb 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1461,104 +1461,41 @@ void VulkanDriver::blit(TargetBufferFlags buffers, Handle dst, V Handle src, Viewport srcRect, SamplerMagFilter filter) { VulkanRenderTarget* dstTarget = handle_cast(mHandleMap, dst); VulkanRenderTarget* srcTarget = handle_cast(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); } } diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index ebaf55636c..3d2697b1e3 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -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 = {};