From 3f4fe033ed01d6b14b1a8b064c4cfd78b7252933 Mon Sep 17 00:00:00 2001 From: Maximilien Dagois Date: Tue, 6 Aug 2024 16:40:36 +0900 Subject: [PATCH] Transient attachment support Attachment tweaks Minor tweaks Set the load operation of the attachment based on whether it is transient or not Fixed a bug using the wrong texture Addressed some comments in the PR Remove the boolean to select transient --- .../backend/include/backend/DriverEnums.h | 3 ++- filament/backend/src/vulkan/VulkanContext.h | 5 ++++ filament/backend/src/vulkan/VulkanDriver.cpp | 16 ++++++++--- .../backend/src/vulkan/VulkanFboCache.cpp | 3 ++- filament/backend/src/vulkan/VulkanFboCache.h | 7 ++--- filament/backend/src/vulkan/VulkanHandles.cpp | 7 ++++- filament/backend/src/vulkan/VulkanTexture.cpp | 27 +++++++++++++++---- filament/backend/src/vulkan/VulkanTexture.h | 6 +++++ .../src/vulkan/platform/VulkanPlatform.cpp | 14 ++++++++++ 9 files changed, 74 insertions(+), 14 deletions(-) diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index 51926ad2def..191e7ac2262 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -765,7 +765,8 @@ enum class TextureUsage : uint16_t { BLIT_SRC = 0x0040, //!< Texture can be used the source of a blit() BLIT_DST = 0x0080, //!< Texture can be used the destination of a blit() PROTECTED = 0x0100, //!< Texture can be used for protected content - DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage + DEFAULT = UPLOADABLE | SAMPLEABLE, //!< Default texture usage + ALL_ATTACHMENTS = COLOR_ATTACHMENT | DEPTH_ATTACHMENT | STENCIL_ATTACHMENT | SUBPASS_INPUT, //!< Mask of all attachments }; inline const char* stringify(TextureUsage usage) { diff --git a/filament/backend/src/vulkan/VulkanContext.h b/filament/backend/src/vulkan/VulkanContext.h index a820087d7a2..e7a18141962 100644 --- a/filament/backend/src/vulkan/VulkanContext.h +++ b/filament/backend/src/vulkan/VulkanContext.h @@ -144,6 +144,10 @@ struct VulkanContext { return mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE; } + inline bool isLazilyAllocatedMemorySupported() const noexcept { + return mLazilyAllocatedMemorySupported; + } + private: VkPhysicalDeviceMemoryProperties mMemoryProperties = {}; VkPhysicalDeviceProperties mPhysicalDeviceProperties = {}; @@ -151,6 +155,7 @@ struct VulkanContext { bool mDebugMarkersSupported = false; bool mDebugUtilsSupported = false; bool mMultiviewEnabled = false; + bool mLazilyAllocatedMemorySupported = false; VkFormatList mDepthStencilFormats; VkFormatList mBlittableDepthStencilFormats; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 97c6102e74d..185659a212f 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1315,12 +1315,14 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP // Create the VkRenderPass or fetch it from cache. VulkanFboCache::RenderPassKey rpkey = { - .initialDepthLayout = currentDepthLayout, .depthFormat = depth.getFormat(), .clear = clearVal, .discardStart = discardStart, .discardEnd = discardEndVal, + .initialDepthLayout = currentDepthLayout, .samples = rt->getSamples(), + .needsResolveMask = 0, + .usesLazilyAllocatedMemory = 0, .subpassMask = uint8_t(params.subpassMask), .viewCount = renderTargetLayerCount, }; @@ -1329,8 +1331,16 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP if (info.texture) { assert_invariant(info.layerCount == renderTargetLayerCount); rpkey.colorFormat[i] = info.getFormat(); - if (rpkey.samples > 1 && info.texture->samples == 1) { - rpkey.needsResolveMask |= (1 << i); + if (rpkey.samples > 1) { + const VulkanTexture* sidecar = info.texture->getSidecar(); + assert_invariant(sidecar); + assert_invariant(sidecar->samples > 1); + if (sidecar && sidecar->isTransientAttachment()) { + rpkey.usesLazilyAllocatedMemory |= (1 << i); + } + if (info.texture->samples == 1) { + rpkey.needsResolveMask |= (1 << i); + } } } else { rpkey.colorFormat[i] = VK_FORMAT_UNDEFINED; diff --git a/filament/backend/src/vulkan/VulkanFboCache.cpp b/filament/backend/src/vulkan/VulkanFboCache.cpp index 927dd4bcedf..980131025e5 100644 --- a/filament/backend/src/vulkan/VulkanFboCache.cpp +++ b/filament/backend/src/vulkan/VulkanFboCache.cpp @@ -41,6 +41,7 @@ bool VulkanFboCache::RenderPassEq::operator()(const RenderPassKey& k1, if (k1.discardEnd != k2.discardEnd) return false; if (k1.samples != k2.samples) return false; if (k1.needsResolveMask != k2.needsResolveMask) return false; + if (k1.usesLazilyAllocatedMemory != k2.usesLazilyAllocatedMemory) return false; if (k1.subpassMask != k2.subpassMask) return false; if (k1.viewCount != k2.viewCount) return false; return true; @@ -254,7 +255,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { .format = config.colorFormat[i], .samples = (VkSampleCountFlagBits) config.samples, .loadOp = clear ? kClear : (discard ? kDontCare : kKeep), - .storeOp = kEnableStore, + .storeOp = (config.usesLazilyAllocatedMemory & (1 << i)) ? kDisableStore : kEnableStore, .stencilLoadOp = kDontCare, .stencilStoreOp = kDisableStore, .initialLayout = imgutil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT), diff --git a/filament/backend/src/vulkan/VulkanFboCache.h b/filament/backend/src/vulkan/VulkanFboCache.h index 3335db7c1c9..2b186106f68 100644 --- a/filament/backend/src/vulkan/VulkanFboCache.h +++ b/filament/backend/src/vulkan/VulkanFboCache.h @@ -42,18 +42,19 @@ class VulkanFboCache { // RenderPassKey is a small POD representing the immutable state that is used to construct // a VkRenderPass. It is hashed and used as a lookup key. struct alignas(8) RenderPassKey { - VulkanLayout initialDepthLayout; - uint8_t padding[3] = {}; - VkFormat colorFormat[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]; // 32 bytes VkFormat depthFormat; // 4 bytes TargetBufferFlags clear; // 4 bytes TargetBufferFlags discardStart; // 4 bytes TargetBufferFlags discardEnd; // 4 bytes + + VulkanLayout initialDepthLayout; // 1 byte uint8_t samples; // 1 byte uint8_t needsResolveMask; // 1 byte + uint8_t usesLazilyAllocatedMemory; // 1 byte uint8_t subpassMask; // 1 byte uint8_t viewCount; // 1 byte + uint8_t padding[2]; }; struct RenderPassVal { VkRenderPass handle; diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index e2adea0f560..9e8324b3d2b 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -283,11 +283,16 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica if (texture && texture->samples == 1) { auto msTexture = texture->getSidecar(); if (UTILS_UNLIKELY(!msTexture)) { + // Clear all usage flags that are not related to attachments, so that we can + // use the transient usage flag. + const TextureUsage usage = texture->usage & TextureUsage::ALL_ATTACHMENTS; + assert_invariant(static_cast(usage) != 0U); + // TODO: This should be allocated with the ResourceAllocator. msTexture = new VulkanTexture(device, physicalDevice, context, allocator, commands, handleAllocator, texture->target, ((VulkanTexture const*) texture)->levels, texture->format, - samples, texture->width, texture->height, texture->depth, texture->usage, + samples, texture->width, texture->height, texture->depth, usage, stagePool, true /* heap allocated */); texture->setSidecar(msTexture); } diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index 2181ee59053..e33fd6faa00 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -128,7 +128,8 @@ VulkanTextureState::VulkanTextureState( mStagePool(stagePool), mDevice(device), mAllocator(allocator), - mCommands(commands) { + mCommands(commands), + mIsTransientAttachment(false) { } VulkanTextureState* VulkanTexture::getSharedState() { @@ -209,6 +210,19 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; } + // Determine if we can use the transient usage flag combined with lazily allocated memory. + const bool useTransientAttachment = + // Lazily allocated memory is available. + context.isLazilyAllocatedMemorySupported() && + // Usage consists of attachment flags only. + none(tusage & ~TextureUsage::ALL_ATTACHMENTS) && + // Usage contains at least one attachment flag. + any(tusage & TextureUsage::ALL_ATTACHMENTS); + state->mIsTransientAttachment = useTransientAttachment; + + const VkImageUsageFlags transientFlag = + useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U; + if (any(usage & TextureUsage::SAMPLEABLE)) { #if FVK_ENABLED(FVK_DEBUG_TEXTURE) @@ -224,19 +238,19 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; } if (any(usage & TextureUsage::COLOR_ATTACHMENT)) { - imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transientFlag; if (any(usage & TextureUsage::SUBPASS_INPUT)) { imageInfo.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; } } if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) { - imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag; } if (any(usage & TextureUsage::UPLOADABLE)) { imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; } if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) { - imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag; // Depth resolves uses a custom shader and therefore needs to be sampleable. if (samples > 1) { @@ -285,8 +299,11 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VkMemoryRequirements memReqs = {}; vkGetImageMemoryRequirements(state->mDevice, state->mTextureImage, &memReqs); + const VkFlags requiredMemoryFlags = + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + (useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U); uint32_t memoryTypeIndex - = context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + = context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags); FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES) << "VulkanTexture: unable to find a memory type that meets requirements."; diff --git a/filament/backend/src/vulkan/VulkanTexture.h b/filament/backend/src/vulkan/VulkanTexture.h index e3d1babbbd0..4b76eb5329e 100644 --- a/filament/backend/src/vulkan/VulkanTexture.h +++ b/filament/backend/src/vulkan/VulkanTexture.h @@ -79,6 +79,7 @@ struct VulkanTextureState : public VulkanResource { VmaAllocator mAllocator; VulkanCommands* mCommands; std::shared_ptr mTransitionFence; + bool mIsTransientAttachment; }; @@ -170,6 +171,11 @@ struct VulkanTexture : public HwTexture, VulkanResource { return state->mSidecarMSAA.get(); } + bool isTransientAttachment() const { + VulkanTextureState const* state = getSharedState(); + return state->mIsTransientAttachment; + } + bool transitionLayout(VulkanCommandBuffer* commands, const VkImageSubresourceRange& range, VulkanLayout newLayout); diff --git a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp index 2e88542080f..6ddfb94bd31 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp @@ -740,6 +740,20 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME); context.mMultiviewEnabled = setContains(deviceExts, VK_KHR_MULTIVIEW_EXTENSION_NAME); + // Check the availability of lazily allocated memory + { + context.mLazilyAllocatedMemorySupported = false; + const uint32_t typeCount = context.mMemoryProperties.memoryTypeCount; + for(uint32_t i = 0; i < typeCount; ++i) { + const VkMemoryType type = context.mMemoryProperties.memoryTypes[i]; + if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) { + context.mLazilyAllocatedMemorySupported = true; + assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + break; + } + } + } + #ifdef NDEBUG // If we are in release build, we should not have turned on debug extensions FILAMENT_CHECK_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported)