From 85a3eaa9118470a8823cc2f52568e5fc56306548 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 26 Sep 2023 16:13:39 -0700 Subject: [PATCH] vk: layout transition clean up - Return the correct SubresourceRange for depth attachments - Fix transition for when one layout within mulitple mip-levels is different - Use implicit layout transition for renderpasses - Fix access mask for sampler in vertex shaders - Use unordered_map for VkSubresourceRange in VulkanTexture --- filament/backend/src/vulkan/VulkanBlitter.cpp | 64 ++++---- filament/backend/src/vulkan/VulkanContext.cpp | 13 +- filament/backend/src/vulkan/VulkanDriver.cpp | 58 +++---- .../backend/src/vulkan/VulkanImageUtility.cpp | 17 +-- filament/backend/src/vulkan/VulkanTexture.cpp | 143 +++++++++++++----- filament/backend/src/vulkan/VulkanTexture.h | 48 +++++- 6 files changed, 229 insertions(+), 114 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanBlitter.cpp b/filament/backend/src/vulkan/VulkanBlitter.cpp index f1eb13fc4cdd..9f20b11ac8c6 100644 --- a/filament/backend/src/vulkan/VulkanBlitter.cpp +++ b/filament/backend/src/vulkan/VulkanBlitter.cpp @@ -40,16 +40,20 @@ namespace { inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, VkFilter filter, const VkExtent2D srcExtent, VulkanAttachment src, VulkanAttachment dst, const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]) { - const VkImageBlit blitRegions[1] = {{.srcSubresource = {aspect, src.level, src.layer, 1}, + 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]}}}; + .dstOffsets = {dstRect[0], dstRect[1]}, + }}; - const VkImageResolve resolveRegions[1] = {{.srcSubresource = {aspect, src.level, src.layer, 1}, + 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}}}; + .extent = {srcExtent.width, srcExtent.height, 1}, + }}; const VkImageSubresourceRange srcRange = { .aspectMask = aspect, @@ -69,11 +73,14 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) { utils::slog.d << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level - << "layout=" << src.getLayout() + << " layout=" << src.getLayout() << " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level - << "layout=" << dst.getLayout() << utils::io::endl; + << " layout=" << dst.getLayout() << utils::io::endl; } + VulkanLayout oldSrcLayout = src.getLayout(); + VulkanLayout oldDstLayout = dst.getLayout(); + src.texture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC); dst.texture->transitionLayout(cmdbuffer, dstRange, VulkanLayout::TRANSFER_DST); @@ -91,17 +98,14 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, 1, blitRegions, filter); } - VulkanLayout newSrcLayout = ImgUtil::getDefaultLayout(src.texture->usage); - VulkanLayout const newDstLayout = ImgUtil::getDefaultLayout(dst.texture->usage); - - // In the case of blitting the depth attachment, we transition the source into GENERAL (for - // sampling) and set the copy as ATTACHMENT_OPTIMAL (to be set as the attachment). - if (any(src.texture->usage & TextureUsage::DEPTH_ATTACHMENT)) { - newSrcLayout = VulkanLayout::DEPTH_SAMPLER; + if (oldSrcLayout == VulkanLayout::UNDEFINED) { + oldSrcLayout = ImgUtil::getDefaultLayout(src.texture->usage); } - - src.texture->transitionLayout(cmdbuffer, srcRange, newSrcLayout); - dst.texture->transitionLayout(cmdbuffer, dstRange, newDstLayout); + if (oldDstLayout == VulkanLayout::UNDEFINED) { + oldDstLayout = ImgUtil::getDefaultLayout(dst.texture->usage); + } + src.texture->transitionLayout(cmdbuffer, srcRange, oldSrcLayout); + dst.texture->transitionLayout(cmdbuffer, dstRange, oldDstLayout); } struct BlitterUniforms { @@ -190,19 +194,19 @@ void VulkanBlitter::blitDepth(BlitArgs args) { } void VulkanBlitter::terminate() noexcept { - if (mDevice) { + if (mDepthResolveProgram) { delete mDepthResolveProgram; mDepthResolveProgram = nullptr; + } - if (mTriangleBuffer) { - delete mTriangleBuffer; - mTriangleBuffer = nullptr; - } + if (mTriangleBuffer) { + delete mTriangleBuffer; + mTriangleBuffer = nullptr; + } - if (mParamsBuffer) { - delete mParamsBuffer; - mParamsBuffer = nullptr; - } + if (mParamsBuffer) { + delete mParamsBuffer; + mParamsBuffer = nullptr; } } @@ -239,11 +243,11 @@ void VulkanBlitter::lazyInit() noexcept { mDepthResolveProgram->samplerGroupInfo[0].samplers.reserve(1); mDepthResolveProgram->samplerGroupInfo[0].samplers.resize(1); - if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) { - utils::slog.d << "Created Shader Module for VulkanBlitter " - << "shaders = (" << vertexShader << ", " << fragmentShader << ")" - << utils::io::endl; - } +#if FVK_ENABLED(FVK_DEBUG_BLITTER) + utils::slog.d << "Created Shader Module for VulkanBlitter " + << "shaders = (" << vertexShader << ", " << fragmentShader << ")" + << utils::io::endl; +#endif static const float kTriangleVertices[] = { -1.0f, -1.0f, diff --git a/filament/backend/src/vulkan/VulkanContext.cpp b/filament/backend/src/vulkan/VulkanContext.cpp index f8a4e496d221..1fc2e46c77c9 100644 --- a/filament/backend/src/vulkan/VulkanContext.cpp +++ b/filament/backend/src/vulkan/VulkanContext.cpp @@ -64,21 +64,12 @@ VkImageView VulkanAttachment::getImageView(VkImageAspectFlags aspect) { VkImageSubresourceRange VulkanAttachment::getSubresourceRange(VkImageAspectFlags aspect) const { assert_invariant(texture); - uint32_t levelCount = 1; - uint32_t layerCount = 1; - // For depth attachments, we consider all the subresource range since layout transitions of - // depth and stencil attachments should always be carried out for all subresources. - if (aspect & VK_IMAGE_ASPECT_DEPTH_BIT) { - auto range = texture->getPrimaryRange(); - levelCount = range.levelCount; - layerCount = range.layerCount; - } return { .aspectMask = aspect, .baseMipLevel = uint32_t(level), - .levelCount = levelCount, + .levelCount = 1, .baseArrayLayer = uint32_t(layer), - .layerCount = layerCount, + .layerCount = 1, }; } diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 2f95f0f17f62..da7280e653a6 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1021,7 +1021,6 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP // If that's the case, we need to change the layout of the texture to DEPTH_SAMPLER, which is a // more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for // the non-sampling case. - bool samplingDepthAttachment = false; VulkanCommandBuffer& commands = mCommands->get(); VkCommandBuffer const cmdbuffer = commands.buffer(); @@ -1041,29 +1040,24 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP if (!any(texture->usage & TextureUsage::DEPTH_ATTACHMENT)) { continue; } - samplingDepthAttachment - = depth.texture && texture->getVkImage() == depth.texture->getVkImage(); if (texture->getPrimaryImageLayout() == VulkanLayout::DEPTH_SAMPLER) { continue; } - VkImageSubresourceRange const subresources{ - .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, - .baseMipLevel = 0, - .levelCount = texture->levels, - .baseArrayLayer = 0, - .layerCount = texture->depth, - }; commands.acquire(texture); - texture->transitionLayout(cmdbuffer, subresources, VulkanLayout::DEPTH_SAMPLER); + + // Transition the primary view, which is the sampler's view into the right layout. + texture->transitionLayout(cmdbuffer, texture->getPrimaryViewRange(), + VulkanLayout::DEPTH_SAMPLER); break; } } } - // currentDepthLayout tracks state of the layout after the (potential) transition in the above block. - VulkanLayout currentDepthLayout = depth.getLayout(); - VulkanLayout const renderPassDepthLayout = samplingDepthAttachment - ? VulkanLayout::DEPTH_SAMPLER - : VulkanLayout::DEPTH_ATTACHMENT; + + VulkanLayout const currentDepthLayout = depth.getLayout(); + VulkanLayout const renderPassDepthLayout = VulkanLayout::DEPTH_ATTACHMENT; + // We need to keep the final layout as an attachment because the implicit transition does not + // have any barrier guarrantees, meaning that if we want to sample from the output in the next + // pass, then we'd have a race-condition/validation error. VulkanLayout const finalDepthLayout = renderPassDepthLayout; TargetBufferFlags clearVal = params.flags.clear; @@ -1073,11 +1067,8 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP discardEndVal &= ~TargetBufferFlags::DEPTH; clearVal &= ~TargetBufferFlags::DEPTH; } - if (currentDepthLayout != renderPassDepthLayout) { - depth.texture->transitionLayout(cmdbuffer, - depth.getSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT), renderPassDepthLayout); - currentDepthLayout = renderPassDepthLayout; - } + auto const attachmentSubresourceRange = depth.getSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT); + depth.texture->setLayout(attachmentSubresourceRange, VulkanLayout::DEPTH_ATTACHMENT); } // Create the VkRenderPass or fetch it from cache. @@ -1271,12 +1262,11 @@ void VulkanDriver::endRenderPass(int) { // NOTE: ideally dstStageMask would merely be VERTEX_SHADER_BIT | FRAGMENT_SHADER_BIT, but this // seems to be insufficient on Mali devices. To work around this we are adding a more aggressive // TOP_OF_PIPE barrier. - if (!rt->isSwapChain()) { - VkMemoryBarrier barrier { - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + VkMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, }; VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; if (rt->hasDepth()) { @@ -1593,6 +1583,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r if (UTILS_LIKELY(boundSampler->t)) { VulkanTexture* texture = mResourceAllocator.handle_cast(boundSampler->t); + VkImageViewType const expectedType = texture->getViewType(); // TODO: can this uninitialized check be checked in a higher layer? // This fallback path is very flaky because the dummy texture might not have @@ -1611,9 +1602,22 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r usage = VulkanPipelineCache::getUsageFlags(sampler.binding, samplerGroup.stageFlags, usage); + VkImageView imageView = VK_NULL_HANDLE; + VkImageSubresourceRange const range = texture->getPrimaryViewRange(); + if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) + && expectedType == VK_IMAGE_VIEW_TYPE_2D) { + // If the sampler is part of a mipmapped depth texture, where one of the level + // *can* be an attachment, then the sampler for this texture has the same view + // properties as a view for an attachment. Therefore, we can use + // getAttachmentView to get a corresponding VkImageView. + imageView = texture->getAttachmentView(range); + } else { + imageView = texture->getViewForType(range, expectedType); + } + samplerInfo[sampler.binding] = { .sampler = vksampler, - .imageView = texture->getPrimaryImageView(), + .imageView = imageView, .imageLayout = ImgUtil::getVkLayout(texture->getPrimaryImageLayout()) }; samplerTextures[sampler.binding] = texture; diff --git a/filament/backend/src/vulkan/VulkanImageUtility.cpp b/filament/backend/src/vulkan/VulkanImageUtility.cpp index 186771778c32..108a3f2b96a9 100644 --- a/filament/backend/src/vulkan/VulkanImageUtility.cpp +++ b/filament/backend/src/vulkan/VulkanImageUtility.cpp @@ -66,8 +66,8 @@ getVkTransition(const VulkanLayoutTransition& transition) { switch (transition.oldLayout) { case VulkanLayout::UNDEFINED: - srcAccessMask = 0; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + srcStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; break; case VulkanLayout::COLOR_ATTACHMENT: srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT @@ -93,8 +93,7 @@ getVkTransition(const VulkanLayoutTransition& transition) { srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VulkanLayout::DEPTH_ATTACHMENT: - srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT - | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; srcStage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; case VulkanLayout::DEPTH_SAMPLER: @@ -125,7 +124,7 @@ getVkTransition(const VulkanLayoutTransition& transition) { break; case VulkanLayout::READ_ONLY: dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; break; case VulkanLayout::TRANSFER_SRC: dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; @@ -141,10 +140,10 @@ getVkTransition(const VulkanLayoutTransition& transition) { dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; break; case VulkanLayout::DEPTH_SAMPLER: - dstAccessMask - = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT - | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + dstAccessMask = + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; break; case VulkanLayout::PRESENT: case VulkanLayout::COLOR_ATTACHMENT_RESOLVE: diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index c40f6f779cab..2f4132e262e9 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -40,13 +40,14 @@ VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanComm mViewType(ImgUtil::getViewType(target)), mSwizzle({}), mTextureImage(image), - mPrimaryViewRange{ + mFullViewRange{ .aspectMask = getImageAspect(), .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, + mPrimaryViewRange(mFullViewRange), mStagePool(stagePool), mDevice(device), mAllocator(allocator), @@ -197,24 +198,31 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, error = vkBindImageMemory(mDevice, mTextureImage, mTextureImageMemory, 0); ASSERT_POSTCONDITION(!error, "Unable to bind image."); - // Spec out the "primary" VkImageView that shaders use to sample from the image. - mPrimaryViewRange.aspectMask = getImageAspect(); - mPrimaryViewRange.baseMipLevel = 0; - mPrimaryViewRange.levelCount = levels; - mPrimaryViewRange.baseArrayLayer = 0; + uint32_t layerCount = 0; if (target == SamplerType::SAMPLER_CUBEMAP) { - mPrimaryViewRange.layerCount = 6; + layerCount = 6; } else if (target == SamplerType::SAMPLER_CUBEMAP_ARRAY) { - mPrimaryViewRange.layerCount = depth * 6; + layerCount = depth * 6; } else if (target == SamplerType::SAMPLER_2D_ARRAY) { - mPrimaryViewRange.layerCount = depth; + layerCount = depth; } else if (target == SamplerType::SAMPLER_3D) { - mPrimaryViewRange.layerCount = 1; + layerCount = 1; } else { - mPrimaryViewRange.layerCount = 1; + layerCount = 1; } - // Go ahead and create the primary image view, no need to do it lazily. + mFullViewRange = { + .aspectMask = getImageAspect(), + .baseMipLevel = 0, + .levelCount = levels, + .baseArrayLayer = 0, + .layerCount = layerCount, + }; + + // Spec out the "primary" VkImageView that shaders use to sample from the image. + mPrimaryViewRange = mFullViewRange; + + // Go ahead and create the primary image view. getImageView(mPrimaryViewRange, mViewType, mSwizzle); // Transition the layout of each image slice that might be used as a render target. @@ -223,14 +231,10 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, if (imageInfo.usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - const uint32_t layers = mPrimaryViewRange.layerCount; - VkImageSubresourceRange range = { getImageAspect(), 0, levels, 0, layers }; - VulkanCommandBuffer& commands = mCommands->get(); VkCommandBuffer const cmdbuf = commands.buffer(); commands.acquire(this); - - transitionLayout(cmdbuf, range, ImgUtil::getDefaultLayout(imageInfo.usage)); + transitionLayout(cmdbuf, mFullViewRange, ImgUtil::getDefaultLayout(imageInfo.usage)); } } @@ -318,11 +322,11 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt } VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST; - VulkanLayout nextLayout = getLayout(0, miplevel); + VulkanLayout nextLayout = getLayout(transitionRange.baseArrayLayer, miplevel); VkImageLayout const newVkLayout = ImgUtil::getVkLayout(newLayout); if (nextLayout == VulkanLayout::UNDEFINED) { - nextLayout = VulkanLayout::READ_WRITE; + nextLayout = ImgUtil::getDefaultLayout(this->usage); } transitionLayout(cmdbuf, transitionRange, newLayout); @@ -360,10 +364,10 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u .dstOffsets = { rect[0], rect[1] } }}; - const VkImageSubresourceRange range = { aspect, miplevel, 1, 0, 1 }; + const VkImageSubresourceRange range = { aspect, miplevel, 1, layer, 1 }; VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST; - VulkanLayout const oldLayout = getLayout(0, miplevel); + VulkanLayout const oldLayout = getLayout(layer, miplevel); transitionLayout(cmdbuf, range, newLayout); vkCmdBlitImage(cmdbuf, stage->image, ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC), @@ -386,9 +390,14 @@ VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) { return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {}); } +VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) { + return getImageView(range, type, mSwizzle); +} + VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) { - auto iter = mCachedImageViews.find(range); + ImageViewKey const key {range, viewType, swizzle}; + auto iter = mCachedImageViews.find(key); if (iter != mCachedImageViews.end()) { return iter->second; } @@ -404,7 +413,7 @@ VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageVi }; VkImageView imageView; vkCreateImageView(mDevice, &viewInfo, VKALLOC, &imageView); - mCachedImageViews.emplace(range, imageView); + mCachedImageViews.emplace(key, imageView); return imageView; } @@ -415,22 +424,72 @@ VkImageAspectFlags VulkanTexture::getImageAspect() const { void VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, const VkImageSubresourceRange& range, VulkanLayout newLayout) { - VulkanLayout oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel); - #if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION) - utils::slog.i << "transition layout of " << mTextureImage << ",layer=" << range.baseArrayLayer - << ",level=" << range.baseMipLevel << " from=" << oldLayout << " to=" << newLayout + VulkanLayout const oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel); + + uint32_t const firstLayer = range.baseArrayLayer; + uint32_t const lastLayer = firstLayer + range.layerCount; + uint32_t const firstLevel = range.baseMipLevel; + uint32_t const lastLevel = firstLevel + range.levelCount; + + // If we are transitioning more than one layer/level (slice), we need to know whether they are + // all of the same layer. If not, we need to transition slice-by-slice. Otherwise it would + // trigger the validation layer saying that the `oldLayout` provided is incorrect. + // TODO: transition by multiple slices with more sophiscated range finding. + bool transitionSliceBySlice = false; + for (uint32_t i = firstLayer; i < lastLayer; ++i) { + for (uint32_t j = firstLevel; j < lastLevel; ++j) { + if (oldLayout != getLayout(i, j)) { + transitionSliceBySlice = true; + break; + } + } + } + +#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION) + utils::slog.d << "transition texture=" << mTextureImage + << " (" << range.baseArrayLayer + << "," << range.baseMipLevel << ")" + << " count=(" << range.layerCount + << "," << range.levelCount << ")" + << " from=" << oldLayout << " to=" << newLayout << " format=" << mVkFormat - << " depth=" << isDepthFormat(mVkFormat) << utils::io::endl; - #endif + << " depth=" << isDepthFormat(mVkFormat) + << " slice-by-slice=" << transitionSliceBySlice + << utils::io::endl; +#endif - ImgUtil::transitionLayout(cmdbuf, { + if (transitionSliceBySlice) { + for (uint32_t i = firstLayer; i < lastLayer; ++i) { + for (uint32_t j = firstLevel; j < lastLevel; ++j) { + VulkanLayout const layout = getLayout(i, j); + ImgUtil::transitionLayout(cmdbuf, { + .image = mTextureImage, + .oldLayout = layout, + .newLayout = newLayout, + .subresources = { + .aspectMask = range.aspectMask, + .baseMipLevel = j, + .levelCount = 1, + .baseArrayLayer = i, + .layerCount = 1, + }, + }); + } + } + } else { + ImgUtil::transitionLayout(cmdbuf, { .image = mTextureImage, .oldLayout = oldLayout, .newLayout = newLayout, .subresources = range, - }); + }); + } + setLayout(range, newLayout); +} + +void VulkanTexture::setLayout(const VkImageSubresourceRange& range, VulkanLayout newLayout) { uint32_t const firstLayer = range.baseArrayLayer; uint32_t const lastLayer = firstLayer + range.layerCount; uint32_t const firstLevel = range.baseMipLevel; @@ -466,16 +525,32 @@ VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const { #if FVK_ENABLED(FVK_DEBUG_TEXTURE) void VulkanTexture::print() const { const uint32_t firstLayer = 0; - const uint32_t lastLayer = firstLayer + mPrimaryViewRange.layerCount; + const uint32_t lastLayer = firstLayer + mFullViewRange.layerCount; const uint32_t firstLevel = 0; - const uint32_t lastLevel = firstLevel + mPrimaryViewRange.levelCount; + const uint32_t lastLevel = firstLevel + mFullViewRange.levelCount; for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) { for (uint32_t level = firstLevel; level < lastLevel; ++level) { + bool primary = + layer >= mPrimaryViewRange.baseArrayLayer && + layer < (mPrimaryViewRange.baseArrayLayer + mPrimaryViewRange.layerCount) && + level >= mPrimaryViewRange.baseMipLevel && + level < (mPrimaryViewRange.baseMipLevel + mPrimaryViewRange.levelCount); utils::slog.d << "[" << mTextureImage << "]: (" << layer << "," << level - << ")=" << getLayout(layer, level) << utils::io::endl; + << ")=" << getLayout(layer, level) + << " primary=" << primary + << utils::io::endl; } } + + for (auto view: mCachedImageViews) { + auto& range = view.first.range; + utils::slog.d << "[" << mTextureImage << ", imageView=" << view.second << "]=>" + << " (" << range.baseArrayLayer << "," << range.baseMipLevel << ")" + << " count=(" << range.layerCount << "," << range.levelCount << ")" + << " aspect=" << range.aspectMask << " viewType=" << view.first.type + << utils::io::endl; + } } #endif diff --git a/filament/backend/src/vulkan/VulkanTexture.h b/filament/backend/src/vulkan/VulkanTexture.h index d3a84067c82b..d6a36ecdc527 100644 --- a/filament/backend/src/vulkan/VulkanTexture.h +++ b/filament/backend/src/vulkan/VulkanTexture.h @@ -25,6 +25,8 @@ #include +#include + namespace filament::backend { struct VulkanTexture : public HwTexture, VulkanResource { @@ -48,12 +50,18 @@ struct VulkanTexture : public HwTexture, VulkanResource { uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel); // Returns the primary image view, which is used for shader sampling. - VkImageView getPrimaryImageView() const { return mCachedImageViews.at(mPrimaryViewRange); } + VkImageView getPrimaryImageView() { + return getImageView(mPrimaryViewRange, mViewType, mSwizzle); + } + + VkImageViewType getViewType() const { return mViewType; } // Sets the min/max range of miplevels in the primary image view. void setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel); - VkImageSubresourceRange getPrimaryRange() const { return mPrimaryViewRange; } + VkImageSubresourceRange getPrimaryViewRange() const { return mPrimaryViewRange; } + + VkImageSubresourceRange getFullViewRange() const { return mFullViewRange; } VulkanLayout getPrimaryImageLayout() const { return getLayout(mPrimaryViewRange.baseArrayLayer, mPrimaryViewRange.baseMipLevel); @@ -64,6 +72,12 @@ struct VulkanTexture : public HwTexture, VulkanResource { // and the identity swizzle. VkImageView getAttachmentView(VkImageSubresourceRange range); + // This is a workaround for the first few frames where we're waiting for the texture to actually + // be uploaded. In that case, we bind the sampler to an empty texture, but the corresponding + // imageView needs to be of the right type. Hence, we provide an option to indicate the + // view type. Swizzle option does not matter in this case. + VkImageView getViewForType(VkImageSubresourceRange const& range, VkImageViewType type); + VkFormat getVkFormat() const { return mVkFormat; } VkImage getVkImage() const { return mTextureImage; } @@ -84,11 +98,37 @@ struct VulkanTexture : public HwTexture, VulkanResource { // For now this always returns either DEPTH or COLOR. VkImageAspectFlags getImageAspect() const; + // For implicit transition like the end of a render pass, we need to be able to set the layout + // manually (outside of calls to transitionLayout). + void setLayout(const VkImageSubresourceRange& range, VulkanLayout newLayout); + #if FVK_ENABLED(FVK_DEBUG_TEXTURE) void print() const; #endif private: + + struct ImageViewKey { + VkImageSubresourceRange range; // 4 * 5 bytes + VkImageViewType type; // 4 bytes + VkComponentMapping swizzle; // 4 * 4 bytes + + bool operator==(ImageViewKey const& k2) const { + auto const& k1 = *this; + return k1.range.aspectMask == k2.range.aspectMask + && k1.range.baseMipLevel == k2.range.baseMipLevel + && k1.range.levelCount == k2.range.levelCount + && k1.range.baseArrayLayer == k2.range.baseArrayLayer + && k1.range.layerCount == k2.range.layerCount && k1.type == k2.type + && k1.swizzle.r == k2.swizzle.r && k1.swizzle.g == k2.swizzle.g + && k1.swizzle.b == k2.swizzle.b && k1.swizzle.a == k2.swizzle.a; + } + }; + // No implicit padding allowed due to it being a hash key. + static_assert(sizeof(ImageViewKey) == 40); + + using ImageViewHash = utils::hash::MurmurHashFn; + // Gets or creates a cached VkImageView for a range of miplevels, array layers, viewType, and // swizzle (or not). VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType, @@ -108,11 +148,13 @@ struct VulkanTexture : public HwTexture, VulkanResource { // Track the image layout of each subresource using a sparse range map. utils::RangeMap mSubresourceLayouts; + VkImageSubresourceRange mFullViewRange; + // Track the range of subresources that define the "primary" image view, which is the special // image view that gets bound to an actual texture sampler. VkImageSubresourceRange mPrimaryViewRange; - std::map mCachedImageViews; + std::unordered_map mCachedImageViews; VulkanStagePool& mStagePool; VkDevice mDevice; VmaAllocator mAllocator;