Files
UnrealEngine/Engine/Source/Runtime/VulkanRHI/Private/VulkanFramebuffer.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

399 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "VulkanFramebuffer.h"
#include "VulkanDevice.h"
#include "VulkanRenderpass.h"
#include "VulkanRenderTargetLayout.h"
FVulkanFramebuffer::FVulkanFramebuffer(FVulkanDevice& Device, const FRHISetRenderTargetsInfo& InRTInfo, const FVulkanRenderTargetLayout& RTLayout, const FVulkanRenderPass& RenderPass)
: Framebuffer(VK_NULL_HANDLE)
, NumColorRenderTargets(InRTInfo.NumColorRenderTargets)
, NumColorAttachments(0)
, DepthStencilRenderTargetImage(VK_NULL_HANDLE)
, DepthStencilResolveRenderTargetImage(VK_NULL_HANDLE)
, FragmentDensityImage(VK_NULL_HANDLE)
{
FMemory::Memzero(ColorRenderTargetImages);
FMemory::Memzero(ColorResolveTargetImages);
AttachmentTextureViews.Empty(RTLayout.GetNumAttachmentDescriptions());
uint32 MipIndex = 0;
const VkExtent3D& RTExtents = RTLayout.GetExtent3D();
// Adreno does not like zero size RTs
check(RTExtents.width != 0 && RTExtents.height != 0);
uint32 NumLayers = RTExtents.depth;
for (int32 Index = 0; Index < InRTInfo.NumColorRenderTargets; ++Index)
{
FRHITexture* RHITexture = InRTInfo.ColorRenderTarget[Index].Texture;
if (!RHITexture)
{
continue;
}
FVulkanTexture* Texture = ResourceCast(RHITexture);
ColorRenderTargetImages[Index] = Texture->Image;
MipIndex = InRTInfo.ColorRenderTarget[Index].MipIndex;
FVulkanView* View = GetColorRenderTargetViewDesc(Texture, MipIndex, InRTInfo.ColorRenderTarget[Index].ArraySliceIndex, RTLayout.GetMultiViewCount(), NumLayers);
AttachmentTextureViews.Add(View);
++NumColorAttachments;
// Check the RTLayout as well to make sure the resolve attachment is needed (Vulkan and Feature level specific)
// See: FVulkanRenderTargetLayout constructor with FRHIRenderPassInfo
if (InRTInfo.bHasResolveAttachments && RTLayout.GetHasResolveAttachments() && RTLayout.GetResolveAttachmentReferences()[Index].layout != VK_IMAGE_LAYOUT_UNDEFINED)
{
FRHITexture* ResolveRHITexture = InRTInfo.ColorResolveRenderTarget[Index].Texture;
FVulkanTexture* ResolveTexture = ResourceCast(ResolveRHITexture);
ColorResolveTargetImages[Index] = ResolveTexture->Image;
if (FVulkanView* ResolveView = GetColorResolveTargetViewDesc(ResolveTexture, MipIndex, InRTInfo.ColorRenderTarget[Index].ArraySliceIndex))
{
AttachmentTextureViews.Add(ResolveView);
}
}
}
if (RTLayout.GetHasDepthStencil())
{
FVulkanTexture* Texture = ResourceCast(InRTInfo.DepthStencilRenderTarget.Texture);
const FRHITextureDesc& Desc = Texture->GetDesc();
DepthStencilRenderTargetImage = Texture->Image;
check(Texture->PartialView);
PartialDepthTextureView = Texture->PartialView;
FVulkanView* View = GetDepthStencilTargetViewDesc(Texture, InRTInfo.NumColorRenderTargets, MipIndex, NumLayers);
AttachmentTextureViews.Add(View);
if (RTLayout.GetHasDepthStencilResolve() && RTLayout.GetDepthStencilResolveAttachmentReference()->layout != VK_IMAGE_LAYOUT_UNDEFINED)
{
FRHITexture* ResolveRHITexture = InRTInfo.DepthStencilResolveRenderTarget.Texture;
FVulkanTexture* ResolveTexture = ResourceCast(ResolveRHITexture);
DepthStencilResolveRenderTargetImage = ResolveTexture->Image;
if (FVulkanView* ResolveView = GetDepthStencilResolveTargetViewDesc(ResolveTexture, MipIndex))
{
AttachmentTextureViews.Add(ResolveView);
}
}
}
if (GRHISupportsAttachmentVariableRateShading && RTLayout.GetHasFragmentDensityAttachment())
{
FVulkanTexture* Texture = ResourceCast(InRTInfo.ShadingRateTexture);
FragmentDensityImage = Texture->Image;
FVulkanView* View = GetFragmentDensityAttachmentViewDesc(Texture, MipIndex);
AttachmentTextureViews.Add(View);
}
TArray<VkImageView> AttachmentViews;
AttachmentViews.Reserve(AttachmentTextureViews.Num());
for (FVulkanView const* View : AttachmentTextureViews)
{
AttachmentViews.Add(View->GetTextureView().View);
}
VkFramebufferCreateInfo CreateInfo;
ZeroVulkanStruct(CreateInfo, VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO);
CreateInfo.renderPass = RenderPass.GetHandle();
CreateInfo.attachmentCount = AttachmentViews.Num();
CreateInfo.pAttachments = AttachmentViews.GetData();
CreateInfo.width = RTExtents.width;
CreateInfo.height = RTExtents.height;
CreateInfo.layers = NumLayers;
VERIFYVULKANRESULT_EXPANDED(VulkanRHI::vkCreateFramebuffer(Device.GetHandle(), &CreateInfo, VULKAN_CPU_ALLOCATOR, &Framebuffer));
RenderArea.offset.x = 0;
RenderArea.offset.y = 0;
RenderArea.extent.width = RTExtents.width;
RenderArea.extent.height = RTExtents.height;
INC_DWORD_STAT(STAT_VulkanNumFrameBuffers);
}
FVulkanFramebuffer::~FVulkanFramebuffer()
{
ensure(Framebuffer == VK_NULL_HANDLE);
}
FVulkanView* FVulkanFramebuffer::GetColorRenderTargetViewDesc(FVulkanTexture* Texture, uint32 MipIndex, int32 SliceIndex, uint32 MultiViewCount, uint32& InOutNumLayers)
{
check(Texture->Image != VK_NULL_HANDLE);
const FRHITextureDesc& Desc = Texture->GetDesc();
const VkImageUsageFlags ImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | (Texture->ImageUsageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
if (Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D || Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D_ARRAY)
{
uint32 ArraySliceIndex = 0;
uint32 NumArraySlices = 1;
if (SliceIndex == -1)
{
ArraySliceIndex = 0;
NumArraySlices = Texture->GetNumberOfArrayLevels();
}
else
{
ArraySliceIndex = SliceIndex;
NumArraySlices = 1;
check(ArraySliceIndex < Texture->GetNumberOfArrayLevels());
}
// About !RTLayout.GetIsMultiView(), from https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkFramebufferCreateInfo.html:
// If the render pass uses multiview, then layers must be one
if (Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D_ARRAY && (MultiViewCount == 0))
{
InOutNumLayers = NumArraySlices;
}
FVulkanTextureViewDesc ViewDesc;
ViewDesc.ViewType = Texture->GetViewType();
ViewDesc.AspectFlags = Texture->GetFullAspectMask();
ViewDesc.UEFormat = Desc.Format;
ViewDesc.Format = Texture->ViewFormat;
ViewDesc.FirstMip = MipIndex;
ViewDesc.NumMips = 1;
ViewDesc.ArraySliceIndex = ArraySliceIndex;
ViewDesc.NumArraySlices = NumArraySlices;
ViewDesc.bUseIdentitySwizzle = true;
ViewDesc.ImageUsageFlags = ImageUsageFlags;
return Texture->FindOrAddInternalView(ViewDesc);
}
else if (Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_CUBE || Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
{
// Cube always renders one face at a time
INC_DWORD_STAT(STAT_VulkanNumImageViews);
FVulkanTextureViewDesc ViewDesc;
ViewDesc.ViewType = VK_IMAGE_VIEW_TYPE_2D;
ViewDesc.AspectFlags = Texture->GetFullAspectMask();
ViewDesc.UEFormat = Desc.Format;
ViewDesc.Format = Texture->ViewFormat;
ViewDesc.FirstMip = MipIndex;
ViewDesc.NumMips = 1;
ViewDesc.ArraySliceIndex = SliceIndex;
ViewDesc.NumArraySlices = 1;
ViewDesc.bUseIdentitySwizzle = true;
ViewDesc.ImageUsageFlags = ImageUsageFlags;
return Texture->FindOrAddInternalView(ViewDesc);
}
else if (Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_3D)
{
FVulkanTextureViewDesc ViewDesc;
ViewDesc.ViewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
ViewDesc.AspectFlags = Texture->GetFullAspectMask();
ViewDesc.UEFormat = Desc.Format;
ViewDesc.Format = Texture->ViewFormat;
ViewDesc.FirstMip = MipIndex;
ViewDesc.NumMips = 1;
ViewDesc.ArraySliceIndex = 0;
ViewDesc.NumArraySlices = Desc.Depth;
ViewDesc.bUseIdentitySwizzle = true;
ViewDesc.ImageUsageFlags = ImageUsageFlags;
return Texture->FindOrAddInternalView(ViewDesc);
}
ensure(0);
return nullptr;
}
FVulkanView* FVulkanFramebuffer::GetColorResolveTargetViewDesc(FVulkanTexture* ResolveTexture, uint32 MipIndex, int32 ArraySliceIndex)
{
//resolve attachments only supported for 2d/2d array textures
if (ResolveTexture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D || ResolveTexture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D_ARRAY)
{
FVulkanTextureViewDesc ViewDesc;
ViewDesc.ViewType = ResolveTexture->GetViewType();
ViewDesc.AspectFlags = ResolveTexture->GetFullAspectMask();
ViewDesc.UEFormat = ResolveTexture->GetDesc().Format;
ViewDesc.Format = ResolveTexture->ViewFormat;
ViewDesc.FirstMip = MipIndex;
ViewDesc.NumMips = 1;
ViewDesc.ArraySliceIndex = FMath::Max(0, ArraySliceIndex);
ViewDesc.NumArraySlices = ResolveTexture->GetNumberOfArrayLevels();
ViewDesc.bUseIdentitySwizzle = true;
return ResolveTexture->FindOrAddInternalView(ViewDesc);
}
return nullptr;
}
FVulkanView* FVulkanFramebuffer::GetDepthStencilTargetViewDesc(FVulkanTexture* Texture, uint32 NumColorAttachments, uint32 MipIndex, uint32& InOutNumLayers)
{
ensure(Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D || Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D_ARRAY || Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_CUBE);
if ((NumColorAttachments == 0) && (Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_CUBE))
{
InOutNumLayers = 6;
FVulkanTextureViewDesc ViewDesc;
ViewDesc.ViewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
ViewDesc.AspectFlags = Texture->GetFullAspectMask();
ViewDesc.UEFormat = Texture->GetDesc().Format;
ViewDesc.Format = Texture->ViewFormat;
ViewDesc.FirstMip = MipIndex;
ViewDesc.NumMips = 1;
ViewDesc.ArraySliceIndex = 0;
ViewDesc.NumArraySlices = 6;
ViewDesc.bUseIdentitySwizzle = true;
return Texture->FindOrAddInternalView(ViewDesc);
}
else if ((Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D) || (Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D_ARRAY))
{
// depth attachments need a separate view to have no swizzle components, for validation correctness
FVulkanTextureViewDesc ViewDesc;
ViewDesc.ViewType = Texture->GetViewType();
ViewDesc.AspectFlags = Texture->GetFullAspectMask();
ViewDesc.UEFormat = Texture->GetDesc().Format;
ViewDesc.Format = Texture->ViewFormat;
ViewDesc.FirstMip = MipIndex;
ViewDesc.NumMips = 1;
ViewDesc.ArraySliceIndex = 0;
ViewDesc.NumArraySlices = Texture->GetNumberOfArrayLevels();
ViewDesc.bUseIdentitySwizzle = true;
return Texture->FindOrAddInternalView(ViewDesc);
}
else
{
return Texture->DefaultView;
}
}
FVulkanView* FVulkanFramebuffer::GetDepthStencilResolveTargetViewDesc(FVulkanTexture* ResolveTexture, uint32 MipIndex)
{
// Resolve attachments only supported for 2d/2d array textures
if (ResolveTexture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D || ResolveTexture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D_ARRAY)
{
FVulkanTextureViewDesc ViewDesc;
ViewDesc.ViewType = ResolveTexture->GetViewType();
ViewDesc.AspectFlags = ResolveTexture->GetFullAspectMask();
ViewDesc.UEFormat = ResolveTexture->GetDesc().Format;
ViewDesc.Format = ResolveTexture->ViewFormat;
ViewDesc.FirstMip = MipIndex;
ViewDesc.NumMips = 1;
ViewDesc.ArraySliceIndex = 0;
ViewDesc.NumArraySlices = ResolveTexture->GetNumberOfArrayLevels();
ViewDesc.bUseIdentitySwizzle = true;
return ResolveTexture->FindOrAddInternalView(ViewDesc);
}
return nullptr;
}
FVulkanView* FVulkanFramebuffer::GetFragmentDensityAttachmentViewDesc(FVulkanTexture* Texture, uint32 MipIndex)
{
ensure(Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D || Texture->GetViewType() == VK_IMAGE_VIEW_TYPE_2D_ARRAY);
FVulkanTextureViewDesc ViewDesc;
ViewDesc.ViewType = Texture->GetViewType();
ViewDesc.AspectFlags = Texture->GetFullAspectMask();
ViewDesc.UEFormat = Texture->GetDesc().Format;
ViewDesc.Format = Texture->ViewFormat;
ViewDesc.FirstMip = MipIndex;
ViewDesc.NumMips = 1;
ViewDesc.ArraySliceIndex = 0;
ViewDesc.NumArraySlices = Texture->GetNumberOfArrayLevels();
ViewDesc.bUseIdentitySwizzle = true;
return Texture->FindOrAddInternalView(ViewDesc);
}
void FVulkanFramebuffer::Destroy(FVulkanDevice& Device)
{
VulkanRHI::FDeferredDeletionQueue2& Queue = Device.GetDeferredDeletionQueue();
// will be deleted in reverse order
Queue.EnqueueResource(VulkanRHI::FDeferredDeletionQueue2::EType::Framebuffer, Framebuffer);
Framebuffer = VK_NULL_HANDLE;
DEC_DWORD_STAT(STAT_VulkanNumFrameBuffers);
}
bool FVulkanFramebuffer::Matches(const FRHISetRenderTargetsInfo& InRTInfo) const
{
if (NumColorRenderTargets != InRTInfo.NumColorRenderTargets)
{
return false;
}
{
const FRHIDepthRenderTargetView& B = InRTInfo.DepthStencilRenderTarget;
if (B.Texture)
{
VkImage AImage = DepthStencilRenderTargetImage;
VkImage BImage = ResourceCast(B.Texture)->Image;
if (AImage != BImage)
{
return false;
}
}
}
{
const FRHIDepthRenderTargetView& R = InRTInfo.DepthStencilResolveRenderTarget;
if (R.Texture)
{
VkImage AImage = DepthStencilResolveRenderTargetImage;
VkImage BImage = ResourceCast(R.Texture)->Image;
if (AImage != BImage)
{
return false;
}
}
}
{
FRHITexture* Texture = InRTInfo.ShadingRateTexture;
if (Texture)
{
VkImage AImage = FragmentDensityImage;
VkImage BImage = ResourceCast(Texture)->Image;
if (AImage != BImage)
{
return false;
}
}
}
int32 AttachementIndex = 0;
for (int32 Index = 0; Index < InRTInfo.NumColorRenderTargets; ++Index)
{
if (InRTInfo.bHasResolveAttachments)
{
const FRHIRenderTargetView& R = InRTInfo.ColorResolveRenderTarget[Index];
if (R.Texture)
{
VkImage AImage = ColorResolveTargetImages[AttachementIndex];
VkImage BImage = ResourceCast(R.Texture)->Image;
if (AImage != BImage)
{
return false;
}
}
}
const FRHIRenderTargetView& B = InRTInfo.ColorRenderTarget[Index];
if (B.Texture)
{
VkImage AImage = ColorRenderTargetImages[AttachementIndex];
VkImage BImage = ResourceCast(B.Texture)->Image;
if (AImage != BImage)
{
return false;
}
AttachementIndex++;
}
}
return true;
}